Skip to content

Commit 8114cc2

Browse files
committed
feat: split terminal level filter menu from terminal page
1 parent d48819b commit 8114cc2

File tree

2 files changed

+181
-181
lines changed

2 files changed

+181
-181
lines changed

lib/screens/terminal/terminal_page.dart

Lines changed: 60 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import '../../widgets/field_search.dart';
99
import '../../widgets/terminal_tiles.dart';
1010
import '../../widgets/empty_message.dart';
1111
import 'package:apidash_design_system/apidash_design_system.dart';
12+
import '../../widgets/terminal_level_filter_menu.dart';
1213

1314
class TerminalPage extends ConsumerStatefulWidget {
1415
const TerminalPage({super.key});
@@ -45,7 +46,65 @@ class _TerminalPageState extends ConsumerState<TerminalPage> {
4546
return Scaffold(
4647
body: Column(
4748
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+
),
49108
const Divider(height: 1),
50109
Expanded(
51110
child: filtered.isEmpty
@@ -105,68 +164,6 @@ class _TerminalPageState extends ConsumerState<TerminalPage> {
105164
);
106165
}
107166

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-
170167
List<TerminalEntry> _applyFilters(List<TerminalEntry> entries) {
171168
final q = _searchCtrl.text.trim().toLowerCase();
172169
bool matches(TerminalEntry e) {
@@ -181,121 +178,3 @@ class _TerminalPageState extends ConsumerState<TerminalPage> {
181178
return entries.where(matches).toList(growable: false);
182179
}
183180
}
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-
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import 'package:flutter/material.dart';
2+
import '../consts.dart';
3+
4+
class TerminalLevelFilterMenu extends StatelessWidget {
5+
const TerminalLevelFilterMenu({
6+
super.key,
7+
required this.selected,
8+
required this.onChanged,
9+
});
10+
11+
final Set<TerminalLevel> selected;
12+
final ValueChanged<Set<TerminalLevel>> onChanged;
13+
14+
void _toggleLevel(TerminalLevel level) {
15+
final next = selected.contains(level)
16+
? (selected.toSet()..remove(level))
17+
: (selected.toSet()..add(level));
18+
onChanged(next);
19+
}
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
final all = TerminalLevel.values.toSet();
24+
return PopupMenuButton<_MenuAction>(
25+
tooltip: 'Filter',
26+
icon: const Icon(Icons.filter_alt),
27+
onSelected: (action) {
28+
switch (action) {
29+
case _MenuAction.toggleDebug:
30+
_toggleLevel(TerminalLevel.debug);
31+
break;
32+
case _MenuAction.toggleInfo:
33+
_toggleLevel(TerminalLevel.info);
34+
break;
35+
case _MenuAction.toggleWarn:
36+
_toggleLevel(TerminalLevel.warn);
37+
break;
38+
case _MenuAction.toggleError:
39+
_toggleLevel(TerminalLevel.error);
40+
break;
41+
case _MenuAction.selectAll:
42+
onChanged(all);
43+
break;
44+
case _MenuAction.clearAll:
45+
onChanged(<TerminalLevel>{});
46+
break;
47+
}
48+
},
49+
itemBuilder: (context) => [
50+
CheckedPopupMenuItem<_MenuAction>(
51+
value: _MenuAction.toggleError,
52+
checked: selected.contains(TerminalLevel.error),
53+
child: const ListTile(
54+
dense: true,
55+
contentPadding: EdgeInsets.zero,
56+
leading: Icon(Icons.error_outline_rounded, color: Colors.red),
57+
title: Text('Errors'),
58+
),
59+
),
60+
CheckedPopupMenuItem<_MenuAction>(
61+
value: _MenuAction.toggleWarn,
62+
checked: selected.contains(TerminalLevel.warn),
63+
child: const ListTile(
64+
dense: true,
65+
contentPadding: EdgeInsets.zero,
66+
leading: Icon(Icons.warning_amber_outlined, color: Colors.amber),
67+
title: Text('Warnings'),
68+
),
69+
),
70+
CheckedPopupMenuItem<_MenuAction>(
71+
value: _MenuAction.toggleInfo,
72+
checked: selected.contains(TerminalLevel.info),
73+
child: const ListTile(
74+
dense: true,
75+
contentPadding: EdgeInsets.zero,
76+
leading: Icon(Icons.info_outline, color: Colors.blue),
77+
title: Text('Info'),
78+
),
79+
),
80+
CheckedPopupMenuItem<_MenuAction>(
81+
value: _MenuAction.toggleDebug,
82+
checked: selected.contains(TerminalLevel.debug),
83+
child: const ListTile(
84+
dense: true,
85+
contentPadding: EdgeInsets.zero,
86+
leading: Icon(Icons.bug_report_outlined),
87+
title: Text('Debug'),
88+
),
89+
),
90+
const PopupMenuDivider(),
91+
PopupMenuItem<_MenuAction>(
92+
value: _MenuAction.selectAll,
93+
child: const ListTile(
94+
dense: true,
95+
contentPadding: EdgeInsets.zero,
96+
leading: Icon(Icons.select_all),
97+
title: Text('Select all'),
98+
),
99+
),
100+
PopupMenuItem<_MenuAction>(
101+
value: _MenuAction.clearAll,
102+
child: const ListTile(
103+
dense: true,
104+
contentPadding: EdgeInsets.zero,
105+
leading: Icon(Icons.clear_all),
106+
title: Text('Clear selection'),
107+
),
108+
),
109+
],
110+
);
111+
}
112+
}
113+
114+
enum _MenuAction {
115+
toggleDebug,
116+
toggleInfo,
117+
toggleWarn,
118+
toggleError,
119+
selectAll,
120+
clearAll,
121+
}

0 commit comments

Comments
 (0)