@propriety/court-calendar 1.0.21 → 1.0.23
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.
- package/dist/_components/List/CalendarList.d.ts +2 -1
- package/dist/_components/Modal/CreateEdit/TextFieldList.d.ts +1 -3
- package/dist/_components/Modal/CreateEdit/ToggleableTextField.d.ts +3 -1
- package/dist/_components/Toolbar/Toolbar.d.ts +2 -1
- package/dist/court-calendar.css +1 -1
- package/dist/index.mjs +4561 -4530
- package/package.json +1 -1
- package/src/_components/CCalendar.css +68 -0
- package/src/_components/CCalendar.tsx +18 -0
- package/src/_components/List/CalendarList.tsx +5 -1
- package/src/_components/Modal/CreateEdit/CreateEditCase.tsx +0 -1
- package/src/_components/Modal/CreateEdit/TextFieldList.tsx +28 -6
- package/src/_components/Modal/CreateEdit/ToggleableTextField.tsx +4 -0
- package/src/_components/Toolbar/Toolbar.tsx +6 -0
- package/src/helpers/cases.ts +2 -2
package/package.json
CHANGED
|
@@ -467,3 +467,71 @@
|
|
|
467
467
|
transform: scale(1.1);
|
|
468
468
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
469
469
|
}
|
|
470
|
+
|
|
471
|
+
/* Print styles */
|
|
472
|
+
@page {
|
|
473
|
+
size: landscape;
|
|
474
|
+
margin: 0.5in;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@media print {
|
|
478
|
+
/* Hide toolbar, tooltips, modal */
|
|
479
|
+
#ccalendar-container > .MuiStack-root:first-child,
|
|
480
|
+
#event-tooltip,
|
|
481
|
+
.ReactModal__Overlay,
|
|
482
|
+
.MuiDataGrid-footerContainer {
|
|
483
|
+
display: none !important;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Force calendar to fit page width */
|
|
487
|
+
#ccalendar-container {
|
|
488
|
+
width: 100% !important;
|
|
489
|
+
overflow: visible !important;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.fc.fc-media-screen,
|
|
493
|
+
.fc .fc-scrollgrid,
|
|
494
|
+
.fc .fc-scrollgrid-section > td,
|
|
495
|
+
.fc .fc-daygrid-body,
|
|
496
|
+
.fc table {
|
|
497
|
+
width: 100% !important;
|
|
498
|
+
max-width: 100% !important;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.fc .fc-scrollgrid {
|
|
502
|
+
overflow: visible !important;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* Let columns size naturally instead of fixed pixel widths */
|
|
506
|
+
.fc colgroup col {
|
|
507
|
+
width: auto !important;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* Remove scroll constraints on calendar day cells */
|
|
511
|
+
.fc-daygrid-day-events {
|
|
512
|
+
max-height: none !important;
|
|
513
|
+
min-height: auto !important;
|
|
514
|
+
overflow: visible !important;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.month-day-cell {
|
|
518
|
+
height: auto !important;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* Remove fixed height on table container */
|
|
522
|
+
#ccalendar-container .MuiDataGrid-root {
|
|
523
|
+
height: auto !important;
|
|
524
|
+
max-height: none !important;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
#ccalendar-container .MuiDataGrid-virtualScroller {
|
|
528
|
+
overflow: visible !important;
|
|
529
|
+
height: auto !important;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/* Ensure colors print */
|
|
533
|
+
* {
|
|
534
|
+
-webkit-print-color-adjust: exact !important;
|
|
535
|
+
print-color-adjust: exact !important;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
@@ -81,6 +81,7 @@ function CCalendarInner({ apiKey, activeUser }: { apiKey: string; activeUser: nu
|
|
|
81
81
|
const [selectedCases, setSelectedCases] = useState<Case[]>([]);
|
|
82
82
|
const [isFetchingCases, setIsFetchingCases] = useState<boolean>(false);
|
|
83
83
|
const [searchedDateIDs, setSearchedDateIDs] = useState<Array<number>>([]);
|
|
84
|
+
const [isPrinting, setIsPrinting] = useState(false);
|
|
84
85
|
// Track previously rendered event IDs to avoid reanimating unchanged events
|
|
85
86
|
const prevEventIdsRef = useRef<Set<string>>(new Set());
|
|
86
87
|
// Track previous view to detect actual view changes vs calendarApi remounts
|
|
@@ -600,6 +601,21 @@ function CCalendarInner({ apiKey, activeUser }: { apiKey: string; activeUser: nu
|
|
|
600
601
|
}, [currentView, calendarApi]);
|
|
601
602
|
// #endregion
|
|
602
603
|
|
|
604
|
+
// #region printing
|
|
605
|
+
function handlePrint() {
|
|
606
|
+
setIsPrinting(true);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
useEffect(() => {
|
|
610
|
+
if (!isPrinting) return;
|
|
611
|
+
const timeout = setTimeout(() => {
|
|
612
|
+
window.print();
|
|
613
|
+
setIsPrinting(false);
|
|
614
|
+
}, 100);
|
|
615
|
+
return () => clearTimeout(timeout);
|
|
616
|
+
}, [isPrinting]);
|
|
617
|
+
// #endregion
|
|
618
|
+
|
|
603
619
|
// #region Event Mounting (Tooltips & Animations)
|
|
604
620
|
function handleEventMount(info: EventMountArg) {
|
|
605
621
|
// biome-ignore lint/suspicious/noExplicitAny: i dont know the type
|
|
@@ -684,6 +700,7 @@ function CCalendarInner({ apiKey, activeUser }: { apiKey: string; activeUser: nu
|
|
|
684
700
|
filterCtx={filterCtx}
|
|
685
701
|
setFilterCtx={setFilterCtx}
|
|
686
702
|
handleCreateClick={() => createCourtDate(calendarApi?.getDate() || currentDate || new Date())}
|
|
703
|
+
onPrint={handlePrint}
|
|
687
704
|
currentView={currentView}
|
|
688
705
|
setCurrentView={setCurrentView}
|
|
689
706
|
currentDate={currentDate}
|
|
@@ -732,6 +749,7 @@ function CCalendarInner({ apiKey, activeUser }: { apiKey: string; activeUser: nu
|
|
|
732
749
|
currentDate={currentDate}
|
|
733
750
|
onUpdateChair={handleUpdateChair}
|
|
734
751
|
allCases={allCases}
|
|
752
|
+
isPrinting={isPrinting}
|
|
735
753
|
/>
|
|
736
754
|
)}
|
|
737
755
|
<Modal
|
|
@@ -11,12 +11,14 @@ export default function CalendarList({
|
|
|
11
11
|
currentDate,
|
|
12
12
|
onUpdateChair,
|
|
13
13
|
allCases,
|
|
14
|
+
isPrinting,
|
|
14
15
|
}: {
|
|
15
16
|
filteredDates: CourtDate[];
|
|
16
17
|
setSelectedDate: (date: CourtDate) => void;
|
|
17
18
|
currentDate: Date;
|
|
18
19
|
onUpdateChair?: (courtDateId: number, position: 'first' | 'second', userId: number | null) => void;
|
|
19
20
|
allCases: Record<string, Case[]>;
|
|
21
|
+
isPrinting?: boolean;
|
|
20
22
|
}) {
|
|
21
23
|
const { getTownshipName, getCountyName } = useReferenceData();
|
|
22
24
|
const countyColors: Record<string, string> = {
|
|
@@ -264,11 +266,13 @@ export default function CalendarList({
|
|
|
264
266
|
}));
|
|
265
267
|
|
|
266
268
|
return (
|
|
267
|
-
<div style={{ height: 600, width: '100%' }}>
|
|
269
|
+
<div style={{ height: isPrinting ? 'auto' : 600, width: '100%' }}>
|
|
268
270
|
<DataGrid
|
|
269
271
|
rows={rows}
|
|
270
272
|
columns={columns}
|
|
271
273
|
className='themed'
|
|
274
|
+
autoHeight={isPrinting}
|
|
275
|
+
hideFooter={isPrinting}
|
|
272
276
|
initialState={{
|
|
273
277
|
sorting: {
|
|
274
278
|
sortModel: [{ field: 'courtDate', sort: 'asc' }],
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import Stack from '@mui/material/Stack';
|
|
2
|
-
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import React, { useRef, useState, useCallback, useEffect } from 'react';
|
|
3
3
|
import TrashIcon from '@mui/icons-material/Delete';
|
|
4
4
|
import AddIcon from '@mui/icons-material/Add';
|
|
5
5
|
import TextField from '@mui/material/TextField';
|
|
6
6
|
import Fab from '@mui/material/Fab';
|
|
7
7
|
import Typography from '@mui/material/Typography';
|
|
8
|
-
import { ModalMode } from '@/types';
|
|
9
8
|
import ToggleableTextField from './ToggleableTextField';
|
|
10
9
|
import Box from '@mui/material/Box';
|
|
11
10
|
|
|
@@ -14,7 +13,6 @@ export default function TextFieldList({
|
|
|
14
13
|
setTextFields,
|
|
15
14
|
label,
|
|
16
15
|
fieldLabelPrefix,
|
|
17
|
-
modalMode,
|
|
18
16
|
toggleStates,
|
|
19
17
|
setToggleStates,
|
|
20
18
|
alternateFieldLabelPrefix,
|
|
@@ -23,7 +21,6 @@ export default function TextFieldList({
|
|
|
23
21
|
setTextFields: (fields: string[], toggles?: boolean[]) => void;
|
|
24
22
|
label: string;
|
|
25
23
|
fieldLabelPrefix: string;
|
|
26
|
-
modalMode: ModalMode;
|
|
27
24
|
toggleStates?: boolean[];
|
|
28
25
|
setToggleStates?: (toggles: boolean[]) => void;
|
|
29
26
|
alternateFieldLabelPrefix?: string;
|
|
@@ -102,6 +99,29 @@ export default function TextFieldList({
|
|
|
102
99
|
[setToggleStates, toggleStates],
|
|
103
100
|
);
|
|
104
101
|
|
|
102
|
+
const handlePaste = useCallback(
|
|
103
|
+
(index: number, e: React.ClipboardEvent<HTMLDivElement>) => {
|
|
104
|
+
const lines = e.clipboardData.getData('text').split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
105
|
+
if (lines.length <= 1) return;
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
|
|
108
|
+
const newFields = [...localFields.slice(0, index), ...lines, ...localFields.slice(index + 1)];
|
|
109
|
+
const newToggles = toggleStates
|
|
110
|
+
? [...toggleStates.slice(0, index), ...lines.map(() => false), ...toggleStates.slice(index + 1)]
|
|
111
|
+
: undefined;
|
|
112
|
+
|
|
113
|
+
setLocalFields(newFields);
|
|
114
|
+
setNumFields(newFields.length);
|
|
115
|
+
setTextFields(newFields, newToggles ?? []);
|
|
116
|
+
if (setToggleStates && newToggles) setToggleStates(newToggles);
|
|
117
|
+
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
if (boxRef.current) boxRef.current.scrollTop = boxRef.current.scrollHeight;
|
|
120
|
+
}, 0);
|
|
121
|
+
},
|
|
122
|
+
[localFields, toggleStates, setTextFields, setToggleStates],
|
|
123
|
+
);
|
|
124
|
+
|
|
105
125
|
return (
|
|
106
126
|
<Stack spacing={2}>
|
|
107
127
|
<Stack direction='row' spacing={2} alignItems='center'>
|
|
@@ -109,7 +129,7 @@ export default function TextFieldList({
|
|
|
109
129
|
color='primary'
|
|
110
130
|
size='small'
|
|
111
131
|
onClick={addField}
|
|
112
|
-
disabled={numFields > textFields.length
|
|
132
|
+
disabled={numFields > Math.max(textFields.length, 1)}
|
|
113
133
|
style={{
|
|
114
134
|
boxShadow: '1px 1px 3px rgba(0,0,0,0.2)',
|
|
115
135
|
minWidth: 40,
|
|
@@ -145,7 +165,7 @@ export default function TextFieldList({
|
|
|
145
165
|
<Fab
|
|
146
166
|
color='error'
|
|
147
167
|
size='small'
|
|
148
|
-
disabled={
|
|
168
|
+
disabled={numFields <= 1}
|
|
149
169
|
onClick={() => removeField(index)}
|
|
150
170
|
style={{
|
|
151
171
|
minWidth: 40,
|
|
@@ -161,6 +181,7 @@ export default function TextFieldList({
|
|
|
161
181
|
value={localFields[index] || ''}
|
|
162
182
|
onChange={(value) => updateField(index, value)}
|
|
163
183
|
onBlur={syncToParent}
|
|
184
|
+
onPaste={(e) => handlePaste(index, e)}
|
|
164
185
|
isToggled={isToggled || false}
|
|
165
186
|
onToggleChange={(checked) => handleToggleChange(index, checked)}
|
|
166
187
|
fieldLabelPrefix={fieldLabelPrefix}
|
|
@@ -173,6 +194,7 @@ export default function TextFieldList({
|
|
|
173
194
|
value={localFields[index] || ''}
|
|
174
195
|
onChange={(e) => updateField(index, e.target.value)}
|
|
175
196
|
onBlur={syncToParent}
|
|
197
|
+
onPaste={(e) => handlePaste(index, e)}
|
|
176
198
|
fullWidth
|
|
177
199
|
/>
|
|
178
200
|
)}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import TextField from '@mui/material/TextField';
|
|
2
3
|
import Box from '@mui/material/Box';
|
|
3
4
|
import Typography from '@mui/material/Typography';
|
|
@@ -7,6 +8,7 @@ export default function ToggleableTextField({
|
|
|
7
8
|
value,
|
|
8
9
|
onChange,
|
|
9
10
|
onBlur,
|
|
11
|
+
onPaste,
|
|
10
12
|
isToggled,
|
|
11
13
|
onToggleChange,
|
|
12
14
|
fieldLabelPrefix,
|
|
@@ -16,6 +18,7 @@ export default function ToggleableTextField({
|
|
|
16
18
|
value: string;
|
|
17
19
|
onChange: (value: string) => void;
|
|
18
20
|
onBlur?: () => void;
|
|
21
|
+
onPaste?: React.ClipboardEventHandler<HTMLDivElement>;
|
|
19
22
|
isToggled: boolean;
|
|
20
23
|
onToggleChange: (checked: boolean) => void;
|
|
21
24
|
fieldLabelPrefix: string;
|
|
@@ -70,6 +73,7 @@ export default function ToggleableTextField({
|
|
|
70
73
|
value={value}
|
|
71
74
|
onChange={(e) => onChange(e.target.value)}
|
|
72
75
|
onBlur={onBlur}
|
|
76
|
+
onPaste={onPaste}
|
|
73
77
|
fullWidth
|
|
74
78
|
slotProps={{
|
|
75
79
|
inputLabel: {
|
|
@@ -3,6 +3,7 @@ import type { Calendar } from '@fullcalendar/core/index.js';
|
|
|
3
3
|
import NavigateNext from '@mui/icons-material/NavigateNext';
|
|
4
4
|
import NavigateBefore from '@mui/icons-material/NavigateBefore';
|
|
5
5
|
import Add from '@mui/icons-material/Add';
|
|
6
|
+
import Print from '@mui/icons-material/Print';
|
|
6
7
|
import Box from '@mui/material/Box';
|
|
7
8
|
import Button from '@mui/material/Button';
|
|
8
9
|
import Typography from '@mui/material/Typography';
|
|
@@ -22,6 +23,7 @@ export default function Toolbar({
|
|
|
22
23
|
filterCtx,
|
|
23
24
|
setFilterCtx,
|
|
24
25
|
handleCreateClick,
|
|
26
|
+
onPrint,
|
|
25
27
|
currentView,
|
|
26
28
|
setCurrentView,
|
|
27
29
|
currentDate,
|
|
@@ -34,6 +36,7 @@ export default function Toolbar({
|
|
|
34
36
|
filterCtx: CalendarFilterCtx;
|
|
35
37
|
setFilterCtx: (ctx: CalendarFilterCtx) => void;
|
|
36
38
|
handleCreateClick: () => void;
|
|
39
|
+
onPrint: () => void;
|
|
37
40
|
currentView: string;
|
|
38
41
|
setCurrentView: (view: string) => void;
|
|
39
42
|
currentDate: Date;
|
|
@@ -165,6 +168,9 @@ export default function Toolbar({
|
|
|
165
168
|
<Button variant='outlined' size='medium' onClick={goToToday}>
|
|
166
169
|
Today
|
|
167
170
|
</Button>
|
|
171
|
+
<Button size='medium' onClick={onPrint}>
|
|
172
|
+
<Print fontSize='medium' />
|
|
173
|
+
</Button>
|
|
168
174
|
<Button size='medium' onClick={handlePrev}>
|
|
169
175
|
<NavigateBefore fontSize='medium' />
|
|
170
176
|
</Button>
|
package/src/helpers/cases.ts
CHANGED
|
@@ -61,7 +61,7 @@ export async function* fetchAllCasesPaginated(apiKey: string, pageSize = 20) {
|
|
|
61
61
|
export function isCaseSettled(c: Case, isVillage: boolean): boolean {
|
|
62
62
|
if (!isVillage) {
|
|
63
63
|
// town
|
|
64
|
-
if (c.SCARDeterminationAction
|
|
64
|
+
if (!c.SCARDeterminationAction) return false;
|
|
65
65
|
if (
|
|
66
66
|
c.SCARDeterminationAction.toLowerCase().includes('s') ||
|
|
67
67
|
c.SCARDeterminationAction.toLowerCase().includes('w') ||
|
|
@@ -70,7 +70,7 @@ export function isCaseSettled(c: Case, isVillage: boolean): boolean {
|
|
|
70
70
|
return true;
|
|
71
71
|
} else {
|
|
72
72
|
// village
|
|
73
|
-
if (c.VillageSCARDeterminationAction
|
|
73
|
+
if (!c.VillageSCARDeterminationAction) return false;
|
|
74
74
|
if (
|
|
75
75
|
c.VillageSCARDeterminationAction.toLowerCase().includes('s') ||
|
|
76
76
|
c.VillageSCARDeterminationAction.toLowerCase().includes('w') ||
|