@propriety/court-calendar 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.editorconfig +26 -0
  2. package/README.md +0 -0
  3. package/biome.json +302 -0
  4. package/dev/App.tsx +51 -0
  5. package/dev/main.tsx +10 -0
  6. package/index.html +12 -0
  7. package/package.json +54 -0
  8. package/public/vite.svg +1 -0
  9. package/src/_components/CCalendar.css +463 -0
  10. package/src/_components/CCalendar.tsx +726 -0
  11. package/src/_components/List/CalendarList.tsx +288 -0
  12. package/src/_components/Modal/CaseDetails/CaseDetails.tsx +414 -0
  13. package/src/_components/Modal/CaseDetails/EvidenceRow.tsx +83 -0
  14. package/src/_components/Modal/CaseDetails/EvidenceSection.tsx +94 -0
  15. package/src/_components/Modal/CreateEdit/CreateEditCase.tsx +241 -0
  16. package/src/_components/Modal/CreateEdit/DateSelector.tsx +42 -0
  17. package/src/_components/Modal/CreateEdit/EditUserFieldDropdown.tsx +54 -0
  18. package/src/_components/Modal/CreateEdit/EnumDropdown.tsx +54 -0
  19. package/src/_components/Modal/CreateEdit/HearingOfficerDropdown.tsx +48 -0
  20. package/src/_components/Modal/CreateEdit/TextFieldList.tsx +186 -0
  21. package/src/_components/Modal/CreateEdit/ToggleableTextField.tsx +91 -0
  22. package/src/_components/Modal/Modal.css +15 -0
  23. package/src/_components/Modal/Modal.tsx +325 -0
  24. package/src/_components/Modal/ModalActions.tsx +99 -0
  25. package/src/_components/Modal/View/CaseToolbar.tsx +81 -0
  26. package/src/_components/Modal/View/CaseViewer.tsx +237 -0
  27. package/src/_components/Modal/View/DateDetails.tsx +138 -0
  28. package/src/_components/Modal/View/InfoBox.tsx +22 -0
  29. package/src/_components/Modal/View/InfoBoxBtn.css +39 -0
  30. package/src/_components/Modal/View/InfoBoxBtn.tsx +29 -0
  31. package/src/_components/Modal/View/NoticeFileLink.tsx +44 -0
  32. package/src/_components/Shared/FirstSecondChairIcons.tsx +247 -0
  33. package/src/_components/Shared/FormRow.tsx +37 -0
  34. package/src/_components/Shared/MuniDropdown.tsx +94 -0
  35. package/src/_components/Shared/SearchBar.tsx +87 -0
  36. package/src/_components/Toolbar/CaseFilter.tsx +77 -0
  37. package/src/_components/Toolbar/DateTypeFilter.tsx +63 -0
  38. package/src/_components/Toolbar/HearingTypeFilter.tsx +63 -0
  39. package/src/_components/Toolbar/Toolbar.tsx +159 -0
  40. package/src/_components/Toolbar/UserFilter.tsx +105 -0
  41. package/src/_components/Toolbar/ViewFilter.tsx +48 -0
  42. package/src/helpers/cache.ts +89 -0
  43. package/src/helpers/cases.ts +79 -0
  44. package/src/helpers/courtDates.ts +139 -0
  45. package/src/helpers/formatter.ts +16 -0
  46. package/src/helpers/munis.ts +44 -0
  47. package/src/helpers/people.ts +46 -0
  48. package/src/index.ts +2 -0
  49. package/src/types.ts +129 -0
  50. package/tsconfig.app.json +32 -0
  51. package/tsconfig.json +4 -0
  52. package/tsconfig.node.json +30 -0
  53. package/vite.config.ts +27 -0
@@ -0,0 +1,91 @@
1
+ import TextField from '@mui/material/TextField';
2
+ import Box from '@mui/material/Box';
3
+ import Typography from '@mui/material/Typography';
4
+ import Switch from '@mui/material/Switch';
5
+
6
+ export default function ToggleableTextField({
7
+ index,
8
+ value,
9
+ onChange,
10
+ onBlur,
11
+ isToggled,
12
+ onToggleChange,
13
+ fieldLabelPrefix,
14
+ alternateFieldLabelPrefix,
15
+ }: {
16
+ index: number;
17
+ value: string;
18
+ onChange: (value: string) => void;
19
+ onBlur?: () => void;
20
+ isToggled: boolean;
21
+ onToggleChange: (checked: boolean) => void;
22
+ fieldLabelPrefix: string;
23
+ alternateFieldLabelPrefix: string;
24
+ }) {
25
+ return (
26
+ <TextField
27
+ label={
28
+ <Box
29
+ sx={{
30
+ display: 'flex',
31
+ alignItems: 'center',
32
+ gap: 1,
33
+ borderRadius: '12px',
34
+ '&:hover': { cursor: 'pointer', backgroundColor: 'var(--fc-today-bg-color) !important' },
35
+ }}
36
+ >
37
+ <Typography
38
+ variant='caption'
39
+ sx={{
40
+ fontSize: '1rem',
41
+ fontWeight: !isToggled ? 600 : 400,
42
+ color: !isToggled ? 'primary.main' : 'var(--text)',
43
+ opacity: !isToggled ? 1 : '0.5 !important',
44
+ transition: 'all 0.2s',
45
+ }}
46
+ >
47
+ {fieldLabelPrefix}
48
+ </Typography>
49
+ <Switch
50
+ checked={isToggled}
51
+ onChange={(e) => {
52
+ e.stopPropagation();
53
+ onToggleChange(e.target.checked);
54
+ }}
55
+ size='medium'
56
+ />
57
+ <Typography
58
+ variant='caption'
59
+ sx={{
60
+ fontSize: '1rem',
61
+ fontWeight: isToggled ? 600 : 400,
62
+ color: isToggled ? 'primary.main' : 'var(--text)',
63
+ transition: 'all 0.2s',
64
+ opacity: isToggled ? 1 : '0.5 !important',
65
+ }}
66
+ >
67
+ {alternateFieldLabelPrefix}
68
+ </Typography>
69
+ </Box>
70
+ }
71
+ value={value}
72
+ onChange={(e) => onChange(e.target.value)}
73
+ onBlur={onBlur}
74
+ fullWidth
75
+ slotProps={{
76
+ inputLabel: {
77
+ shrink: true,
78
+ sx: {
79
+ borderRadius: '12px',
80
+ top: '-3px',
81
+ },
82
+ },
83
+ }}
84
+ sx={{
85
+ '& legend': {
86
+ width: '170px',
87
+ },
88
+ }}
89
+ />
90
+ );
91
+ }
@@ -0,0 +1,15 @@
1
+ /* Modal appearing animation */
2
+ .modal-appear-animate {
3
+ animation: modalFadeIn 0.35s cubic-bezier(0.4, 0, 0.2, 1);
4
+ }
5
+
6
+ @keyframes modalFadeIn {
7
+ from {
8
+ opacity: 0;
9
+ transform: scale(0.96) translateY(24px);
10
+ }
11
+ to {
12
+ opacity: 1;
13
+ transform: scale(1) translateY(0);
14
+ }
15
+ }
@@ -0,0 +1,325 @@
1
+ import { useEffect, useState } from 'react';
2
+ import Dialog from '@mui/material/Dialog';
3
+ import DialogTitle from '@mui/material/DialogTitle';
4
+ import DialogContent from '@mui/material/DialogContent';
5
+ import { Lifecycle, ModalMode, SourceType, type CalendarFilterCtx, type Case, type CourtDate } from '@/types';
6
+ import CaseViewer from './View/CaseViewer';
7
+ import DateDetails from './View/DateDetails';
8
+ import ModalActions from './ModalActions';
9
+ import CreateEditCase from './CreateEdit/CreateEditCase';
10
+ import { LocalizationProvider } from '@mui/x-date-pickers';
11
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
12
+ import { createCourtDate, deleteCourtDate, isVillageDate, updateCourtDate } from '@/helpers/courtDates';
13
+ import IconButton from '@mui/material/IconButton';
14
+ import CloseIcon from '@mui/icons-material/Close';
15
+ import './Modal.css';
16
+ import CaseDetails from './CaseDetails/CaseDetails';
17
+ import Paper from '@mui/material/Paper';
18
+
19
+ export default function CCModal({
20
+ modalIsOpen,
21
+ modalMode,
22
+ setModalMode,
23
+ setIsOpen,
24
+ selectedCourtDate,
25
+ updateCourtDateInMemory,
26
+ filterCtx,
27
+ setFilterCtx,
28
+ selectedCases,
29
+ updateCases,
30
+ deleteCases,
31
+ isFetchingCases,
32
+ apiKey,
33
+ }: {
34
+ modalIsOpen: boolean;
35
+ modalMode: ModalMode;
36
+ setModalMode: (mode: ModalMode) => void;
37
+ setIsOpen: (isOpen: boolean) => void;
38
+ selectedCourtDate: CourtDate | null;
39
+ updateCourtDateInMemory: (updatedDate: CourtDate, del?: boolean) => void;
40
+ filterCtx: CalendarFilterCtx;
41
+ setFilterCtx: (ctx: CalendarFilterCtx) => void;
42
+ selectedCases: Case[];
43
+ updateCases: (cases: Record<string, Case[]>) => void;
44
+ deleteCases: (courtDateId: string) => void;
45
+ isFetchingCases: boolean;
46
+ apiKey: string;
47
+ }) {
48
+ const [editedData, setEditedData] = useState<CourtDate | undefined>(undefined);
49
+ const [editedCases, setEditedCases] = useState<Case[]>([]);
50
+ const [clickedCase, setClickedCase] = useState<Case | null>(null);
51
+
52
+ useEffect(() => {
53
+ if (!modalIsOpen) {
54
+ setModalMode(ModalMode.DETAILS);
55
+ setClickedCase(null);
56
+ }
57
+ }, [modalIsOpen]);
58
+
59
+ useEffect(() => {
60
+ if (clickedCase !== null) {
61
+ setModalMode(ModalMode.CASE_DETAILS);
62
+ }
63
+ }, [clickedCase]);
64
+
65
+ useEffect(() => {
66
+ if (modalMode === ModalMode.DETAILS) {
67
+ setClickedCase(null);
68
+ }
69
+ }, [modalMode]);
70
+
71
+ useEffect(() => {
72
+ if (modalMode === ModalMode.EDIT && selectedCourtDate) {
73
+ setEditedData(selectedCourtDate);
74
+ setEditedCases(selectedCases);
75
+ }
76
+ }, [modalMode, selectedCourtDate, selectedCases]);
77
+
78
+ useEffect(() => {
79
+ if (modalMode === ModalMode.CREATE) {
80
+ // Initialize with blank data for create mode, using selectedCourtDate.CourtDate if available
81
+ setEditedData({
82
+ CourtDateID: 0,
83
+ CourtDate: selectedCourtDate?.CourtDate ? new Date(selectedCourtDate.CourtDate) : new Date(),
84
+ MuniCode: '',
85
+ UploadDeadline: null,
86
+ CourtCases: 0,
87
+ Lifecycle: Lifecycle.SCHEDULED,
88
+ HearingTime: '',
89
+ HearingLink: null,
90
+ Source: SourceType.MANUAL,
91
+ Type: null,
92
+ FirstChair: null,
93
+ SecondChair: null,
94
+ HearingOfficer: null,
95
+ EnterDate: new Date(),
96
+ LastUpdateDate: null,
97
+ AdjournmentDate: null,
98
+ IsAdjourned: false,
99
+ Notes: null,
100
+ NoticeFile: null,
101
+ });
102
+ setEditedCases([
103
+ {
104
+ ParcelID: '',
105
+ Municipality: '',
106
+ Negotiator: 0,
107
+ Year: new Date().getFullYear(),
108
+ property_data: { Address: '', PropertyOwnerFull: '' },
109
+ evidence: null,
110
+ SCARIndexNumber: '',
111
+ SCARDeterminationAction: '',
112
+ SCARSettleDate: null,
113
+ SCARFileDate: null,
114
+ VillageSCARIndexNumber: '',
115
+ VillageSCARDeterminationAction: '',
116
+ VillageSCARSettleDate: null,
117
+ VillageSCARFileDate: null,
118
+ DateCompleted: null,
119
+ },
120
+ ]);
121
+ }
122
+ }, [modalMode, selectedCourtDate]);
123
+
124
+ // #region Handlers
125
+ function validateEditedData(): { valid: boolean; message?: string } {
126
+ if (!editedData) return { valid: false, message: 'No data to validate' };
127
+
128
+ // REQUIRED FIELDS
129
+ if (!editedData.CourtDate) return { valid: false, message: 'CourtDate is required' };
130
+ if (!editedData.MuniCode || editedData.MuniCode.trim() === '')
131
+ return { valid: false, message: 'MuniCode is required' };
132
+
133
+ // validation passed
134
+ return { valid: true };
135
+ }
136
+
137
+ async function handleSave() {
138
+ if (!editedData) return;
139
+ const validation = validateEditedData();
140
+ if (!validation.valid) {
141
+ alert(`Validation failed: ${validation.message}`);
142
+ return;
143
+ }
144
+ if (modalMode === ModalMode.CREATE) {
145
+ await handleCreate();
146
+ return;
147
+ }
148
+ await updateCourtDate(
149
+ editedData.CourtDateID,
150
+ editedData,
151
+ apiKey,
152
+ editedCases.map((c) => c.SCARIndexNumber),
153
+ );
154
+ // Update cache after save
155
+ const courtDateId = editedData.CourtDateID;
156
+ if (courtDateId) {
157
+ updateCases({ [courtDateId.toString()]: editedCases });
158
+ }
159
+ setIsOpen(false); // TODO notification
160
+ updateCourtDateInMemory(editedData);
161
+ }
162
+
163
+ async function handleCreate() {
164
+ if (!editedData) return;
165
+ const validation = validateEditedData();
166
+ if (!validation.valid) {
167
+ alert(`Validation failed: ${validation.message}`);
168
+ return;
169
+ }
170
+ const id = await createCourtDate(
171
+ editedData.CourtDate,
172
+ editedData.MuniCode,
173
+ apiKey,
174
+ editedCases.map((c) => c.SCARIndexNumber),
175
+ );
176
+ if (id) {
177
+ const updatedData = { ...editedData, CourtDateID: id };
178
+ setEditedData(updatedData);
179
+ await updateCourtDate(id, updatedData, apiKey);
180
+ updateCourtDateInMemory(updatedData);
181
+ // Update cache after create
182
+ updateCases({ [id.toString()]: editedCases });
183
+ }
184
+ setIsOpen(false); // TODO notification
185
+ }
186
+
187
+ async function handleDelete() {
188
+ const res = window.confirm('Are you sure you want to delete this court date?');
189
+ if (res) {
190
+ await deleteCourtDate(selectedCourtDate!.CourtDateID, apiKey);
191
+ updateCourtDateInMemory(selectedCourtDate!, true);
192
+ // Remove from cache after delete
193
+ const courtDateId = selectedCourtDate!.CourtDateID;
194
+ if (courtDateId) {
195
+ deleteCases(courtDateId.toString());
196
+ }
197
+ setIsOpen(false); // TODO notification
198
+ }
199
+ }
200
+ // #endregion
201
+
202
+ // #region render
203
+ return (
204
+ <Dialog
205
+ open={modalIsOpen}
206
+ onClose={() => setIsOpen(false)}
207
+ fullWidth
208
+ maxWidth='lg'
209
+ slotProps={{
210
+ paper: {
211
+ className: 'modal-appear-animate',
212
+ sx: {
213
+ background:
214
+ 'linear-gradient(to bottom, rgba(255,255,255,0.05), rgba(255,255,255,0)) !important',
215
+ },
216
+ },
217
+ }}
218
+ >
219
+ <DialogTitle
220
+ className='themed'
221
+ style={{
222
+ display: 'flex',
223
+ justifyContent: 'space-between',
224
+ alignItems: 'center',
225
+ }}
226
+ >
227
+ {modalMode === ModalMode.DETAILS
228
+ ? 'Court Date Details'
229
+ : modalMode === ModalMode.CREATE
230
+ ? 'Create Court Date'
231
+ : modalMode === ModalMode.EDIT
232
+ ? 'Edit Court Date'
233
+ : modalMode === ModalMode.CASE_DETAILS
234
+ ? 'Case Details'
235
+ : ''}
236
+ <IconButton aria-label='close' onClick={() => setIsOpen(false)} edge='end' size='small'>
237
+ <CloseIcon />
238
+ </IconButton>
239
+ </DialogTitle>
240
+ <DialogContent dividers className='themed' sx={{ position: 'relative' }}>
241
+ <LocalizationProvider dateAdapter={AdapterDayjs}>
242
+ <div
243
+ style={{
244
+ opacity: modalMode === ModalMode.DETAILS ? 1 : 0,
245
+ pointerEvents: modalMode === ModalMode.DETAILS ? 'auto' : 'none',
246
+ position: modalMode === ModalMode.DETAILS ? 'relative' : 'absolute',
247
+ top: 0,
248
+ left: 0,
249
+ right: 0,
250
+ transition: modalMode === ModalMode.DETAILS ? 'opacity 0.15s ease-in-out' : 'none',
251
+ overflow: modalMode === ModalMode.DETAILS ? 'visible' : 'hidden',
252
+ maxHeight: modalMode === ModalMode.DETAILS ? 'none' : 0,
253
+ }}
254
+ >
255
+ <DateDetails selectedCourtDate={selectedCourtDate} apiKey={apiKey} />
256
+ <CaseViewer
257
+ cases={selectedCases}
258
+ isFetchingCases={isFetchingCases}
259
+ filterCtx={filterCtx}
260
+ setFilterCtx={setFilterCtx}
261
+ setClickedCase={setClickedCase}
262
+ isVillage={isVillageDate(selectedCourtDate ? selectedCourtDate.MuniCode : '')}
263
+ />
264
+ </div>
265
+ {(modalMode === ModalMode.EDIT || modalMode === ModalMode.CREATE) && editedData && (
266
+ <div
267
+ style={{
268
+ opacity: modalMode === ModalMode.EDIT || modalMode === ModalMode.CREATE ? 1 : 0,
269
+ pointerEvents:
270
+ modalMode === ModalMode.EDIT || modalMode === ModalMode.CREATE ? 'auto' : 'none',
271
+ position:
272
+ modalMode === ModalMode.EDIT || modalMode === ModalMode.CREATE
273
+ ? 'relative'
274
+ : 'absolute',
275
+ top: 0,
276
+ left: 0,
277
+ right: 0,
278
+ transition:
279
+ modalMode === ModalMode.EDIT || modalMode === ModalMode.CREATE
280
+ ? 'opacity 0.15s ease-in-out'
281
+ : 'none',
282
+ }}
283
+ >
284
+ <CreateEditCase
285
+ edited={editedData}
286
+ setEdited={setEditedData}
287
+ editedCases={editedCases}
288
+ setEditedCases={setEditedCases}
289
+ modalMode={modalMode}
290
+ />
291
+ </div>
292
+ )}
293
+ {clickedCase !== null && (
294
+ <div
295
+ style={{
296
+ opacity: modalMode === ModalMode.CASE_DETAILS ? 1 : 0,
297
+ pointerEvents: modalMode === ModalMode.CASE_DETAILS ? 'auto' : 'none',
298
+ position: modalMode === ModalMode.CASE_DETAILS ? 'relative' : 'absolute',
299
+ top: 0,
300
+ left: 0,
301
+ right: 0,
302
+ transition: modalMode === ModalMode.CASE_DETAILS ? 'opacity 0.15s ease-in-out' : 'none',
303
+ overflow: modalMode === ModalMode.CASE_DETAILS ? 'visible' : 'hidden',
304
+ maxHeight: modalMode === ModalMode.CASE_DETAILS ? 'none' : 0,
305
+ }}
306
+ >
307
+ <CaseDetails
308
+ selectedCase={clickedCase}
309
+ isVillage={isVillageDate(selectedCourtDate ? selectedCourtDate.MuniCode : '')}
310
+ />
311
+ </div>
312
+ )}
313
+ </LocalizationProvider>
314
+ </DialogContent>
315
+ <ModalActions
316
+ modalMode={modalMode}
317
+ onDelete={handleDelete}
318
+ onSave={handleSave}
319
+ setModalMode={setModalMode}
320
+ setIsOpen={setIsOpen}
321
+ />
322
+ </Dialog>
323
+ );
324
+ // #endregion
325
+ }
@@ -0,0 +1,99 @@
1
+ import Button from '@mui/material/Button';
2
+ import DialogActions from '@mui/material/DialogActions';
3
+ import { ModalMode } from '@/types';
4
+ import { useRef, useState } from 'react';
5
+
6
+ export default function ModalActions({
7
+ modalMode,
8
+ onDelete,
9
+ onSave,
10
+ setModalMode,
11
+ setIsOpen,
12
+ }: {
13
+ modalMode: ModalMode;
14
+ onDelete: () => void;
15
+ onSave: () => void;
16
+ setModalMode: (mode: ModalMode) => void;
17
+ setIsOpen: (isOpen: boolean) => void;
18
+ }) {
19
+ const [showDelete, setShowDelete] = useState(false);
20
+ const clickTimes = useRef<number[]>([]);
21
+
22
+ const handleDivClick = () => {
23
+ const now = Date.now();
24
+ clickTimes.current = clickTimes.current.filter((t) => now - t < 600);
25
+ clickTimes.current.push(now);
26
+ if (clickTimes.current.length >= 3) {
27
+ setShowDelete(true);
28
+ clickTimes.current = [];
29
+ }
30
+ };
31
+
32
+ return (
33
+ <>
34
+ <DialogActions
35
+ className='modal-footer'
36
+ style={{
37
+ display: 'flex',
38
+ justifyContent: 'space-between',
39
+ alignItems: 'center',
40
+ padding: '15px 15px',
41
+ borderTop: '1px solid var(--fc-border-color)',
42
+ backgroundColor: 'var(--bg) !important',
43
+ }}
44
+ >
45
+ <Button
46
+ className=''
47
+ onClick={
48
+ modalMode === ModalMode.DETAILS || modalMode === ModalMode.CREATE
49
+ ? () => setIsOpen(false)
50
+ : () => setModalMode(ModalMode.DETAILS)
51
+ }
52
+ color='error'
53
+ variant='outlined'
54
+ style={{ margin: '0' }}
55
+ >
56
+ {modalMode === ModalMode.DETAILS || modalMode === ModalMode.CREATE
57
+ ? 'Close'
58
+ : modalMode === ModalMode.EDIT
59
+ ? 'Cancel'
60
+ : 'Back'}
61
+ </Button>
62
+ <div
63
+ style={{
64
+ width: 90,
65
+ height: 40,
66
+ display: 'flex',
67
+ alignItems: 'center',
68
+ justifyContent: 'center',
69
+ cursor: 'unset',
70
+ userSelect: 'none',
71
+ }}
72
+ onClick={handleDivClick}
73
+ >
74
+ {showDelete && (
75
+ <Button
76
+ className=''
77
+ onClick={onDelete}
78
+ color='error'
79
+ variant='outlined'
80
+ style={{ margin: '0' }}
81
+ >
82
+ Delete
83
+ </Button>
84
+ )}
85
+ </div>
86
+ <Button
87
+ className=''
88
+ onClick={modalMode === ModalMode.DETAILS ? () => setModalMode(ModalMode.EDIT) : onSave}
89
+ color='primary'
90
+ variant='contained'
91
+ style={{ margin: '0' }}
92
+ disabled={modalMode === ModalMode.CASE_DETAILS}
93
+ >
94
+ {modalMode === ModalMode.DETAILS ? 'Edit' : 'Save'}
95
+ </Button>
96
+ </DialogActions>
97
+ </>
98
+ );
99
+ }
@@ -0,0 +1,81 @@
1
+ import Box from '@mui/material/Box';
2
+ import type { CalendarFilterCtx } from '@/types';
3
+ import SearchBar from '../../Shared/SearchBar';
4
+ import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
5
+ import ToggleButton from '@mui/material/ToggleButton';
6
+ import Tooltip from '@mui/material/Tooltip';
7
+ import PendingActionsIcon from '@mui/icons-material/PendingActions';
8
+ import WarningAmberIcon from '@mui/icons-material/WarningAmber';
9
+ import ManageSearchIcon from '@mui/icons-material/ManageSearch';
10
+
11
+ export default function CaseToolbar({
12
+ filterCtx,
13
+ setFilterCtx,
14
+ }: {
15
+ filterCtx: CalendarFilterCtx;
16
+ setFilterCtx: (ctx: CalendarFilterCtx) => void;
17
+ }) {
18
+ return (
19
+ <Box display='flex' alignItems='center' justifyContent='space-between' sx={{ p: 0, gap: 2 }} className='themed'>
20
+ {/* search bar */}
21
+ <SearchBar
22
+ searchTerm={filterCtx.searchTerm}
23
+ setSearchTerm={(term) => setFilterCtx({ ...filterCtx, searchTerm: term })}
24
+ />
25
+
26
+ {/* show only unsettled */}
27
+ <ToggleButtonGroup>
28
+ <Tooltip title={`Show Only Unsettled`} arrow placement='top'>
29
+ <ToggleButton
30
+ value='unsettled'
31
+ selected={filterCtx.showOnlyUnsettled}
32
+ onClick={() =>
33
+ setFilterCtx({
34
+ ...filterCtx,
35
+ showOnlyUnsettled: !filterCtx.showOnlyUnsettled,
36
+ })
37
+ }
38
+ >
39
+ <PendingActionsIcon fontSize='small' />
40
+ </ToggleButton>
41
+ </Tooltip>
42
+ </ToggleButtonGroup>
43
+
44
+ {/* show only without evidence */}
45
+ <ToggleButtonGroup>
46
+ <Tooltip title={`Show Only Without Evidence`} arrow placement='top'>
47
+ <ToggleButton
48
+ value='withoutEvidence'
49
+ selected={filterCtx.showOnlyWithoutEvidence}
50
+ onClick={() =>
51
+ setFilterCtx({
52
+ ...filterCtx,
53
+ showOnlyWithoutEvidence: !filterCtx.showOnlyWithoutEvidence,
54
+ })
55
+ }
56
+ >
57
+ <WarningAmberIcon fontSize='small' />
58
+ </ToggleButton>
59
+ </Tooltip>
60
+ </ToggleButtonGroup>
61
+
62
+ {/* show only unreviewed */}
63
+ <ToggleButtonGroup>
64
+ <Tooltip title={`Show Only Unreviewed`} arrow placement='top'>
65
+ <ToggleButton
66
+ value='unreviewed'
67
+ selected={filterCtx.showOnlyUnreviewed}
68
+ onClick={() =>
69
+ setFilterCtx({
70
+ ...filterCtx,
71
+ showOnlyUnreviewed: !filterCtx.showOnlyUnreviewed,
72
+ })
73
+ }
74
+ >
75
+ <ManageSearchIcon fontSize='small' />
76
+ </ToggleButton>
77
+ </Tooltip>
78
+ </ToggleButtonGroup>
79
+ </Box>
80
+ );
81
+ }