@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,414 @@
1
+ import { useState, memo } from 'react';
2
+ import Box from '@mui/material/Box';
3
+ import Stack from '@mui/material/Stack';
4
+ import Grid from '@mui/material/Grid';
5
+ import Typography from '@mui/material/Typography';
6
+ import Paper from '@mui/material/Paper';
7
+ import Chip from '@mui/material/Chip';
8
+ import Card from '@mui/material/Card';
9
+ import CardContent from '@mui/material/CardContent';
10
+ import Tooltip from '@mui/material/Tooltip';
11
+ import InfoIcon from '@mui/icons-material/Info';
12
+ import DescriptionIcon from '@mui/icons-material/Description';
13
+ import HomeIcon from '@mui/icons-material/Home';
14
+ import EvidenceSection from './EvidenceSection';
15
+ import type { Case } from '@/types';
16
+ import { allUsers } from '@/helpers/people';
17
+
18
+ const CaseDetails = memo(function CaseDetails({
19
+ selectedCase,
20
+ isVillage,
21
+ }: {
22
+ selectedCase: Case | null;
23
+ isVillage: boolean;
24
+ }) {
25
+ const [tooltipText, setTooltipText] = useState('Copy');
26
+
27
+ if (!selectedCase) {
28
+ return (
29
+ <Box className='themed' p={4} textAlign='center'>
30
+ <Typography variant='h6'>No case selected</Typography>
31
+ </Box>
32
+ );
33
+ }
34
+
35
+ const {
36
+ ParcelID,
37
+ Year,
38
+ Municipality,
39
+ Negotiator,
40
+ SCARIndexNumber,
41
+ VillageSCARIndexNumber,
42
+ SCARDeterminationAction,
43
+ VillageSCARDeterminationAction,
44
+ SCARSettleDate,
45
+ SCARFileDate,
46
+ VillageSCARSettleDate,
47
+ VillageSCARFileDate,
48
+ property_data,
49
+ evidence,
50
+ } = selectedCase;
51
+
52
+ const handleCopyParcel = async () => {
53
+ try {
54
+ await navigator.clipboard.writeText(ParcelID);
55
+ setTooltipText('Copied!');
56
+ setTimeout(() => setTooltipText('Copy'), 2000);
57
+ } catch (err) {
58
+ console.error('Failed to copy:', err);
59
+ }
60
+ };
61
+
62
+ const isReviewed = () => {
63
+ return selectedCase.DateCompleted !== null;
64
+ };
65
+
66
+ return (
67
+ <Stack spacing={4} mt={1}>
68
+ {/* Property Header Section */}
69
+ <Box
70
+ sx={{
71
+ position: 'relative',
72
+ border: '1px solid',
73
+ borderColor: 'divider',
74
+ borderRadius: 1,
75
+ mt: 2,
76
+ p: 2,
77
+ }}
78
+ >
79
+ <Stack
80
+ direction='row'
81
+ alignItems='center'
82
+ spacing={1}
83
+ sx={{
84
+ position: 'absolute',
85
+ top: 0,
86
+ left: 12,
87
+ transform: 'translateY(-50%)',
88
+ px: 1,
89
+ background: 'var(--bg)',
90
+ }}
91
+ >
92
+ <HomeIcon color='primary' />
93
+ <Typography variant='body1' fontWeight={600}>
94
+ Property Information
95
+ </Typography>
96
+ </Stack>
97
+ <Stack direction='row' spacing={2} alignItems='center'>
98
+ <Box>
99
+ <img
100
+ src={`https://aventine-photos.s3.us-east-1.amazonaws.com/${ParcelID}.jpg`}
101
+ alt={`property ${ParcelID} photo`}
102
+ height={120}
103
+ width={140}
104
+ style={{
105
+ minWidth: 140,
106
+ maxWidth: 140,
107
+ minHeight: 120,
108
+ maxHeight: 120,
109
+ objectFit: 'cover',
110
+ borderRadius: 8,
111
+ border: '2px solid rgba(255,255,255,0.8)',
112
+ background: '#f8f8f8',
113
+ boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
114
+ }}
115
+ loading='lazy'
116
+ />
117
+ </Box>
118
+ <Stack spacing={1} flex={1}>
119
+ <Typography variant='h6' fontWeight={600}>
120
+ {property_data?.Address || 'Unknown Address'}
121
+ </Typography>
122
+ <Typography variant='subtitle1' fontWeight={500}>
123
+ Owner: {property_data?.PropertyOwnerFull || 'Unknown'}
124
+ </Typography>
125
+ <Stack direction='row' spacing={1.5}>
126
+ <Tooltip title={tooltipText} arrow placement='top'>
127
+ <Chip
128
+ label={`${ParcelID}`}
129
+ size='medium'
130
+ variant='outlined'
131
+ onClick={handleCopyParcel}
132
+ sx={{
133
+ cursor: 'pointer',
134
+ '&:hover': {
135
+ backgroundColor: 'var(--fc-today-bg-color) !important',
136
+ },
137
+ transition: 'background-color 0.2s ease-in-out',
138
+ }}
139
+ />
140
+ </Tooltip>
141
+ <Chip label={`${Year}`} size='medium' variant='outlined' />
142
+ <Chip
143
+ label={`${isReviewed() ? 'Reviewed' : 'Unreviewed'}`}
144
+ size='medium'
145
+ variant='outlined'
146
+ sx={{
147
+ '&.MuiChip-outlined': {
148
+ borderColor: isReviewed() ? 'green !important' : 'red !important',
149
+ },
150
+ }}
151
+ />
152
+ </Stack>
153
+ </Stack>
154
+ </Stack>
155
+ </Box>
156
+
157
+ {/* Case Information Section */}
158
+ <Box
159
+ sx={{
160
+ position: 'relative',
161
+ border: '1px solid',
162
+ borderColor: 'divider',
163
+ borderRadius: 1,
164
+ mt: 1.5,
165
+ p: 1.5,
166
+ }}
167
+ >
168
+ <Stack
169
+ direction='row'
170
+ alignItems='center'
171
+ spacing={1}
172
+ sx={{
173
+ position: 'absolute',
174
+ top: 0,
175
+ left: 12,
176
+ transform: 'translateY(-50%)',
177
+ px: 1,
178
+ }}
179
+ >
180
+ <InfoIcon color='primary' />
181
+ <Typography variant='body1' fontWeight={600}>
182
+ Case Information
183
+ </Typography>
184
+ </Stack>
185
+ <Grid container spacing={1.5}>
186
+ <Grid size={{ xs: 12, sm: 6 }}>
187
+ <Card elevation={0} sx={{ height: '100%' }}>
188
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
189
+ <Typography
190
+ variant='caption'
191
+ fontWeight={600}
192
+ textTransform='uppercase'
193
+ letterSpacing={0.5}
194
+ >
195
+ Municipality
196
+ </Typography>
197
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
198
+ {Municipality || '—'}
199
+ </Typography>
200
+ </CardContent>
201
+ </Card>
202
+ </Grid>
203
+ <Grid size={{ xs: 12, sm: 6 }}>
204
+ <Card elevation={0} sx={{ height: '100%' }}>
205
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
206
+ <Typography
207
+ variant='caption'
208
+ fontWeight={600}
209
+ textTransform='uppercase'
210
+ letterSpacing={0.5}
211
+ >
212
+ Negotiator
213
+ </Typography>
214
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
215
+ {Negotiator && !isNaN(Negotiator)
216
+ ? `${allUsers[Negotiator].UserFirstName} ${allUsers[Negotiator].UserLastName}`
217
+ : '—'}
218
+ </Typography>
219
+ </CardContent>
220
+ </Card>
221
+ </Grid>
222
+ {!isVillage && (
223
+ <Grid size={{ xs: 12, sm: 6 }}>
224
+ <Card elevation={0} sx={{ height: '100%' }}>
225
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
226
+ <Typography
227
+ variant='caption'
228
+ fontWeight={600}
229
+ textTransform='uppercase'
230
+ letterSpacing={0.5}
231
+ >
232
+ SCAR Index #
233
+ </Typography>
234
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
235
+ {SCARIndexNumber || '—'}
236
+ </Typography>
237
+ </CardContent>
238
+ </Card>
239
+ </Grid>
240
+ )}
241
+ {isVillage && (
242
+ <Grid size={{ xs: 12, sm: 6 }}>
243
+ <Card elevation={0} sx={{ height: '100%' }}>
244
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
245
+ <Typography
246
+ variant='caption'
247
+ fontWeight={600}
248
+ textTransform='uppercase'
249
+ letterSpacing={0.5}
250
+ >
251
+ Village SCAR Index #
252
+ </Typography>
253
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
254
+ {VillageSCARIndexNumber || '—'}
255
+ </Typography>
256
+ </CardContent>
257
+ </Card>
258
+ </Grid>
259
+ )}
260
+ {!isVillage && (
261
+ <Grid size={{ xs: 12, sm: 6 }}>
262
+ <Card elevation={0} sx={{ height: '100%' }}>
263
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
264
+ <Typography
265
+ variant='caption'
266
+ fontWeight={600}
267
+ textTransform='uppercase'
268
+ letterSpacing={0.5}
269
+ >
270
+ SCAR Determination
271
+ </Typography>
272
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
273
+ {SCARDeterminationAction || '—'}
274
+ </Typography>
275
+ </CardContent>
276
+ </Card>
277
+ </Grid>
278
+ )}
279
+ {isVillage && (
280
+ <Grid size={{ xs: 12, sm: 6 }}>
281
+ <Card elevation={0} sx={{ height: '100%' }}>
282
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
283
+ <Typography
284
+ variant='caption'
285
+ fontWeight={600}
286
+ textTransform='uppercase'
287
+ letterSpacing={0.5}
288
+ >
289
+ Village SCAR Determination
290
+ </Typography>
291
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
292
+ {VillageSCARDeterminationAction || '—'}
293
+ </Typography>
294
+ </CardContent>
295
+ </Card>
296
+ </Grid>
297
+ )}
298
+ {!isVillage && (
299
+ <>
300
+ <Grid size={{ xs: 12, sm: 6 }}>
301
+ <Card elevation={0} sx={{ height: '100%' }}>
302
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
303
+ <Typography
304
+ variant='caption'
305
+ fontWeight={600}
306
+ textTransform='uppercase'
307
+ letterSpacing={0.5}
308
+ >
309
+ SCAR File Date
310
+ </Typography>
311
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
312
+ {SCARFileDate ? new Date(SCARFileDate).toLocaleDateString() : '—'}
313
+ </Typography>
314
+ </CardContent>
315
+ </Card>
316
+ </Grid>
317
+ <Grid size={{ xs: 12, sm: 6 }}>
318
+ <Card elevation={0} sx={{ height: '100%' }}>
319
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
320
+ <Typography
321
+ variant='caption'
322
+ fontWeight={600}
323
+ textTransform='uppercase'
324
+ letterSpacing={0.5}
325
+ >
326
+ SCAR Settle Date
327
+ </Typography>
328
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
329
+ {SCARSettleDate ? new Date(SCARSettleDate).toLocaleDateString() : '—'}
330
+ </Typography>
331
+ </CardContent>
332
+ </Card>
333
+ </Grid>
334
+ </>
335
+ )}
336
+ {isVillage && (
337
+ <Grid size={{ xs: 12, sm: 6 }}>
338
+ <Card elevation={0} sx={{ height: '100%' }}>
339
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
340
+ <Typography
341
+ variant='caption'
342
+ fontWeight={600}
343
+ textTransform='uppercase'
344
+ letterSpacing={0.5}
345
+ >
346
+ Village SCAR File Date
347
+ </Typography>
348
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
349
+ {VillageSCARFileDate ? new Date(VillageSCARFileDate).toLocaleDateString() : '—'}
350
+ </Typography>
351
+ </CardContent>
352
+ </Card>
353
+ </Grid>
354
+ )}
355
+ {isVillage && (
356
+ <Grid size={{ xs: 12, sm: 6 }}>
357
+ <Card elevation={0} sx={{ height: '100%' }}>
358
+ <CardContent sx={{ py: 1, px: 1.5, '&:last-child': { pb: 1 } }}>
359
+ <Typography
360
+ variant='caption'
361
+ fontWeight={600}
362
+ textTransform='uppercase'
363
+ letterSpacing={0.5}
364
+ >
365
+ Village SCAR Settle Date
366
+ </Typography>
367
+ <Typography variant='body1' fontWeight={500} mt={0.25}>
368
+ {VillageSCARSettleDate
369
+ ? new Date(VillageSCARSettleDate).toLocaleDateString()
370
+ : '—'}
371
+ </Typography>
372
+ </CardContent>
373
+ </Card>
374
+ </Grid>
375
+ )}
376
+ </Grid>
377
+ </Box>
378
+
379
+ {/* Evidence Section */}
380
+ <Box
381
+ sx={{
382
+ position: 'relative',
383
+ border: '1px solid',
384
+ borderColor: 'divider',
385
+ borderRadius: 1,
386
+ mt: 2,
387
+ p: 2,
388
+ }}
389
+ >
390
+ <Stack
391
+ direction='row'
392
+ alignItems='center'
393
+ spacing={1}
394
+ sx={{
395
+ position: 'absolute',
396
+ top: 0,
397
+ left: 12,
398
+ transform: 'translateY(-50%)',
399
+ px: 1,
400
+ background: 'var(--bg)',
401
+ }}
402
+ >
403
+ <DescriptionIcon color='primary' />
404
+ <Typography variant='body1' fontWeight={600}>
405
+ Evidence
406
+ </Typography>
407
+ </Stack>
408
+ <EvidenceSection evidence={evidence} />
409
+ </Box>
410
+ </Stack>
411
+ );
412
+ });
413
+
414
+ export default CaseDetails;
@@ -0,0 +1,83 @@
1
+ import Grid from '@mui/material/Grid';
2
+ import Typography from '@mui/material/Typography';
3
+ import Chip from '@mui/material/Chip';
4
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
5
+ import Stack from '@mui/material/Stack';
6
+ import IconButton from '@mui/material/IconButton';
7
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
8
+
9
+ export default function EvidenceRow({
10
+ label,
11
+ url,
12
+ user,
13
+ uploadDate,
14
+ createdDate,
15
+ isUploaded = false,
16
+ }: {
17
+ label: string;
18
+ url: string;
19
+ user: string;
20
+ uploadDate: string;
21
+ createdDate: string;
22
+ isUploaded?: boolean;
23
+ }) {
24
+ function handleRowClick() {
25
+ if (url) {
26
+ window.open(url, '_blank', 'noopener,noreferrer');
27
+ }
28
+ }
29
+
30
+ return (
31
+ <Grid
32
+ container
33
+ spacing={2}
34
+ onClick={handleRowClick}
35
+ className='evidence-row'
36
+ sx={{
37
+ padding: '8px',
38
+ backgroundColor: isUploaded ? 'rgba(76, 175, 80, 0.08)' : 'transparent',
39
+ borderRadius: '4px',
40
+ cursor: url ? 'pointer' : 'default',
41
+ '&:hover, &:hover div': url
42
+ ? {
43
+ backgroundColor: isUploaded
44
+ ? 'rgba(76, 175, 80, 0.15)'
45
+ : 'var(--fc-today-bg-color) !important',
46
+ }
47
+ : {},
48
+ }}
49
+ >
50
+ <Grid size={3}>
51
+ <Stack direction='row' spacing={1} alignItems='center'>
52
+ <Typography variant='body2' fontWeight={isUploaded ? 600 : 400}>
53
+ {label}
54
+ </Typography>
55
+ {isUploaded && (
56
+ <Chip
57
+ label='Uploaded'
58
+ size='small'
59
+ color='success'
60
+ icon={<CheckCircleIcon />}
61
+ sx={{ height: 20, fontSize: '0.7rem' }}
62
+ />
63
+ )}
64
+ </Stack>
65
+ </Grid>
66
+ <Grid size={3.5}>
67
+ <Typography variant='body2'>{`Created on ${createdDate}`}</Typography>
68
+ </Grid>
69
+ <Grid size={3.5}>
70
+ <Typography variant='body2'>{isUploaded ? `Uploaded on ${uploadDate}` : 'Not uploaded'}</Typography>
71
+ </Grid>
72
+ <Grid size={1}>
73
+ <Typography variant='body2'>{user}</Typography>
74
+ </Grid>
75
+
76
+ <Grid size={1}>
77
+ <Stack direction='row' justifyContent='flex-end'>
78
+ {url && <OpenInNewIcon fontSize='small' sx={{ opacity: 0.6 }} />}
79
+ </Stack>
80
+ </Grid>
81
+ </Grid>
82
+ );
83
+ }
@@ -0,0 +1,94 @@
1
+ import type { Evidence } from '@/types';
2
+ import Stack from '@mui/material/Stack';
3
+ import EvidenceRow from './EvidenceRow';
4
+ import { allUsers } from '@/helpers/people';
5
+
6
+ export default function EvidenceSection({ evidence }: { evidence: Evidence | null }) {
7
+ console.log('evidence in EvidenceSection:', evidence);
8
+
9
+ // Check if uploaded evidence exists
10
+ const hasUploadedEvidence = evidence?.Uploaded?.Evidence;
11
+
12
+ // Parse evidence types
13
+ const parseEvidenceTypes = (evidenceStr: string | undefined): string[] => {
14
+ if (!evidenceStr) return [];
15
+ try {
16
+ const parsed = JSON.parse(evidenceStr);
17
+ return Array.isArray(parsed) ? parsed.map((e) => e.toLowerCase()) : [];
18
+ } catch {
19
+ return evidenceStr
20
+ .toLowerCase()
21
+ .split(',')
22
+ .map((e) => e.trim());
23
+ }
24
+ };
25
+
26
+ // Get evidence types to display
27
+ const evidenceTypes = hasUploadedEvidence
28
+ ? parseEvidenceTypes(evidence.Uploaded.Evidence)
29
+ : parseEvidenceTypes(evidence?.Evidence);
30
+
31
+ const hasSales = evidenceTypes.some((type) => type.includes('sales') || type.includes('unequal'));
32
+ const hasEquity = evidenceTypes.some((type) => type.includes('excessive') || type.includes('equity'));
33
+ const hasAnyEvidence = hasSales || hasEquity;
34
+
35
+ // Format upload date if available
36
+ const uploadDate = evidence?.Uploaded?.UploadDate
37
+ ? new Date(evidence.Uploaded.UploadDate).toLocaleDateString()
38
+ : '';
39
+
40
+ // format creation date if available
41
+ const creationDate = evidence?.CreatedDate ? new Date(evidence.CreatedDate).toLocaleDateString() : '';
42
+
43
+ // format user if available
44
+ const user = evidence?.User ? allUsers[evidence?.User] : null;
45
+ const userName = user ? `${user.UserFirstName} ${user.UserLastName}` : '';
46
+
47
+ // Generate S3 URL for evidence
48
+ const generateEvidenceUrl = (reportType: 'sales' | 'equity'): string => {
49
+ if (!evidence?.ParcelID || !evidence?.Year) return '';
50
+
51
+ // for later in case of changes, these can be weird with unequal/excessive
52
+ const reportTypeMap: Record<string, string> = {
53
+ sales: 'sales',
54
+ equity: 'equity',
55
+ };
56
+
57
+ const mappedType = reportTypeMap[reportType];
58
+ const baseUrl = 'https://aventine-court-docs.s3.amazonaws.com';
59
+ const path = `residential/evidence/${evidence.Year}/${evidence.ParcelID}/${mappedType}_comps_fnma.pdf`;
60
+
61
+ return `${baseUrl}/${path}`;
62
+ };
63
+
64
+ return (
65
+ <Stack spacing={1} direction='column'>
66
+ {/* sales */}
67
+ {hasSales && (
68
+ <EvidenceRow
69
+ label='Sales Records'
70
+ url={generateEvidenceUrl('sales')}
71
+ user={userName}
72
+ uploadDate={uploadDate}
73
+ createdDate={creationDate}
74
+ isUploaded={!!hasUploadedEvidence}
75
+ />
76
+ )}
77
+
78
+ {/* equity */}
79
+ {hasEquity && (
80
+ <EvidenceRow
81
+ label='Equity Records'
82
+ url={generateEvidenceUrl('equity')}
83
+ user={userName}
84
+ uploadDate={uploadDate}
85
+ createdDate={creationDate}
86
+ isUploaded={!!hasUploadedEvidence}
87
+ />
88
+ )}
89
+
90
+ {/* none */}
91
+ {!hasAnyEvidence && <EvidenceRow label='No Records' url={''} user={''} uploadDate={''} createdDate={''} />}
92
+ </Stack>
93
+ );
94
+ }