@@ -9,6 +9,7 @@ import '../../widgets/field_search.dart';
9
9
import '../../widgets/terminal_tiles.dart' ;
10
10
import '../../widgets/empty_message.dart' ;
11
11
import 'package:apidash_design_system/apidash_design_system.dart' ;
12
+ import '../../widgets/terminal_level_filter_menu.dart' ;
12
13
13
14
class TerminalPage extends ConsumerStatefulWidget {
14
15
const TerminalPage ({super .key});
@@ -45,7 +46,65 @@ class _TerminalPageState extends ConsumerState<TerminalPage> {
45
46
return Scaffold (
46
47
body: Column (
47
48
children: [
48
- _buildToolbar (context, allEntries),
49
+ Padding (
50
+ padding: const EdgeInsets .fromLTRB (12 , 8 , 12 , 8 ),
51
+ child: Row (
52
+ children: [
53
+ Expanded (
54
+ child: SearchField (
55
+ controller: _searchCtrl,
56
+ hintText: 'Search logs' ,
57
+ onChanged: (_) => setState (() {}),
58
+ ),
59
+ ),
60
+ const SizedBox (width: 8 ),
61
+ // Filter button
62
+ TerminalLevelFilterMenu (
63
+ selected: _selectedLevels,
64
+ onChanged: (set ) => setState (() {
65
+ _selectedLevels
66
+ ..clear ()
67
+ ..addAll (set );
68
+ }),
69
+ ),
70
+ const SizedBox (width: 4 ),
71
+ Tooltip (
72
+ message: 'Show timestamps' ,
73
+ child: Row (
74
+ mainAxisSize: MainAxisSize .min,
75
+ children: [
76
+ Checkbox (
77
+ materialTapTargetSize: MaterialTapTargetSize .shrinkWrap,
78
+ value: _showTimestamps,
79
+ onChanged: (v) =>
80
+ setState (() => _showTimestamps = v ?? false ),
81
+ ),
82
+ const Text ('Timestamp' , style: TextStyle (fontSize: 12 )),
83
+ ],
84
+ ),
85
+ ),
86
+
87
+ const Spacer (),
88
+ // Clear button
89
+ ADIconButton (
90
+ tooltip: 'Clear logs' ,
91
+ icon: Icons .delete_outline,
92
+ iconSize: 22 ,
93
+ onPressed: () {
94
+ ref.read (terminalStateProvider.notifier).clear ();
95
+ },
96
+ ),
97
+ const SizedBox (width: 4 ),
98
+ // Copy all button
99
+ CopyButton (
100
+ showLabel: false ,
101
+ toCopy: ref
102
+ .read (terminalStateProvider.notifier)
103
+ .serializeAll (entries: allEntries),
104
+ ),
105
+ ],
106
+ ),
107
+ ),
49
108
const Divider (height: 1 ),
50
109
Expanded (
51
110
child: filtered.isEmpty
@@ -105,68 +164,6 @@ class _TerminalPageState extends ConsumerState<TerminalPage> {
105
164
);
106
165
}
107
166
108
- Widget _buildToolbar (BuildContext context, List <TerminalEntry > allEntries) {
109
- return Padding (
110
- padding: const EdgeInsets .fromLTRB (12 , 8 , 12 , 8 ),
111
- child: Row (
112
- children: [
113
- Expanded (
114
- child: SearchField (
115
- controller: _searchCtrl,
116
- hintText: 'Search logs' ,
117
- onChanged: (_) => setState (() {}),
118
- ),
119
- ),
120
- const SizedBox (width: 8 ),
121
- // Filter button
122
- _FilterMenu (
123
- selected: _selectedLevels,
124
- onChanged: (set ) => setState (() {
125
- _selectedLevels
126
- ..clear ()
127
- ..addAll (set );
128
- }),
129
- ),
130
- const SizedBox (width: 4 ),
131
- Tooltip (
132
- message: 'Show timestamps' ,
133
- child: Row (
134
- mainAxisSize: MainAxisSize .min,
135
- children: [
136
- Checkbox (
137
- materialTapTargetSize: MaterialTapTargetSize .shrinkWrap,
138
- value: _showTimestamps,
139
- onChanged: (v) =>
140
- setState (() => _showTimestamps = v ?? false ),
141
- ),
142
- const Text ('Timestamp' , style: TextStyle (fontSize: 12 )),
143
- ],
144
- ),
145
- ),
146
-
147
- const Spacer (),
148
- // Clear button
149
- ADIconButton (
150
- tooltip: 'Clear logs' ,
151
- icon: Icons .delete_outline,
152
- iconSize: 22 ,
153
- onPressed: () {
154
- ref.read (terminalStateProvider.notifier).clear ();
155
- },
156
- ),
157
- const SizedBox (width: 4 ),
158
- // Copy all button
159
- CopyButton (
160
- showLabel: false ,
161
- toCopy: ref
162
- .read (terminalStateProvider.notifier)
163
- .serializeAll (entries: allEntries),
164
- ),
165
- ],
166
- ),
167
- );
168
- }
169
-
170
167
List <TerminalEntry > _applyFilters (List <TerminalEntry > entries) {
171
168
final q = _searchCtrl.text.trim ().toLowerCase ();
172
169
bool matches (TerminalEntry e) {
@@ -181,121 +178,3 @@ class _TerminalPageState extends ConsumerState<TerminalPage> {
181
178
return entries.where (matches).toList (growable: false );
182
179
}
183
180
}
184
-
185
- class _FilterMenu extends StatelessWidget {
186
- const _FilterMenu ({
187
- required this .selected,
188
- required this .onChanged,
189
- });
190
-
191
- final Set <TerminalLevel > selected;
192
- final ValueChanged <Set <TerminalLevel >> onChanged;
193
-
194
- void _toggleLevel (TerminalLevel level) {
195
- final next = selected.contains (level)
196
- ? (selected.toSet ()..remove (level))
197
- : (selected.toSet ()..add (level));
198
- onChanged (next);
199
- }
200
-
201
- @override
202
- Widget build (BuildContext context) {
203
- final all = TerminalLevel .values.toSet ();
204
- return PopupMenuButton <_MenuAction >(
205
- tooltip: 'Filter' ,
206
- icon: const Icon (Icons .filter_alt),
207
- onSelected: (action) {
208
- switch (action) {
209
- case _MenuAction .toggleDebug:
210
- _toggleLevel (TerminalLevel .debug);
211
- break ;
212
- case _MenuAction .toggleInfo:
213
- _toggleLevel (TerminalLevel .info);
214
- break ;
215
- case _MenuAction .toggleWarn:
216
- _toggleLevel (TerminalLevel .warn);
217
- break ;
218
- case _MenuAction .toggleError:
219
- _toggleLevel (TerminalLevel .error);
220
- break ;
221
- case _MenuAction .selectAll:
222
- onChanged (all);
223
- break ;
224
- case _MenuAction .clearAll:
225
- onChanged (< TerminalLevel > {});
226
- break ;
227
- }
228
- },
229
- itemBuilder: (context) => [
230
- CheckedPopupMenuItem <_MenuAction >(
231
- value: _MenuAction .toggleError,
232
- checked: selected.contains (TerminalLevel .error),
233
- child: const ListTile (
234
- dense: true ,
235
- contentPadding: EdgeInsets .zero,
236
- leading: Icon (Icons .error_outline_rounded, color: Colors .red),
237
- title: Text ('Errors' ),
238
- ),
239
- ),
240
- CheckedPopupMenuItem <_MenuAction >(
241
- value: _MenuAction .toggleWarn,
242
- checked: selected.contains (TerminalLevel .warn),
243
- child: const ListTile (
244
- dense: true ,
245
- contentPadding: EdgeInsets .zero,
246
- leading: Icon (Icons .warning_amber_outlined, color: Colors .amber),
247
- title: Text ('Warnings' ),
248
- ),
249
- ),
250
- CheckedPopupMenuItem <_MenuAction >(
251
- value: _MenuAction .toggleInfo,
252
- checked: selected.contains (TerminalLevel .info),
253
- child: const ListTile (
254
- dense: true ,
255
- contentPadding: EdgeInsets .zero,
256
- leading: Icon (Icons .info_outline, color: Colors .blue),
257
- title: Text ('Info' ),
258
- ),
259
- ),
260
- CheckedPopupMenuItem <_MenuAction >(
261
- value: _MenuAction .toggleDebug,
262
- checked: selected.contains (TerminalLevel .debug),
263
- child: const ListTile (
264
- dense: true ,
265
- contentPadding: EdgeInsets .zero,
266
- leading: Icon (Icons .bug_report_outlined),
267
- title: Text ('Debug' ),
268
- ),
269
- ),
270
- const PopupMenuDivider (),
271
- PopupMenuItem <_MenuAction >(
272
- value: _MenuAction .selectAll,
273
- child: const ListTile (
274
- dense: true ,
275
- contentPadding: EdgeInsets .zero,
276
- leading: Icon (Icons .select_all),
277
- title: Text ('Select all' ),
278
- ),
279
- ),
280
- PopupMenuItem <_MenuAction >(
281
- value: _MenuAction .clearAll,
282
- child: const ListTile (
283
- dense: true ,
284
- contentPadding: EdgeInsets .zero,
285
- leading: Icon (Icons .clear_all),
286
- title: Text ('Clear selection' ),
287
- ),
288
- ),
289
- ],
290
- );
291
- }
292
- }
293
-
294
- enum _MenuAction {
295
- toggleDebug,
296
- toggleInfo,
297
- toggleWarn,
298
- toggleError,
299
- selectAll,
300
- clearAll,
301
- }
0 commit comments