@striae-org/striae 6.0.1 → 6.1.1
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/app/components/actions/case-export/core-export.ts +11 -2
- package/app/components/actions/case-export/download-handlers.ts +3 -1
- package/app/components/canvas/canvas.module.css +1 -1
- package/app/components/canvas/canvas.tsx +32 -11
- package/app/components/colors/colors.module.css +19 -0
- package/app/components/colors/colors.tsx +5 -1
- package/app/components/icon/icons.svg +1 -1
- package/app/components/icon/manifest.json +1 -1
- package/app/components/navbar/navbar.tsx +32 -16
- package/app/components/sidebar/cases/case-sidebar.tsx +27 -25
- package/app/components/sidebar/files/files-modal.tsx +39 -15
- package/app/components/sidebar/notes/addl-notes-modal.tsx +9 -2
- package/app/components/sidebar/notes/{class-details/class-details-fields.tsx → item-details/item-details-fields.tsx} +10 -10
- package/app/components/sidebar/notes/{class-details/class-details-modal.tsx → item-details/item-details-modal.tsx} +20 -22
- package/app/components/sidebar/notes/{class-details/class-details-sections.tsx → item-details/item-details-sections.tsx} +16 -16
- package/app/components/sidebar/notes/{class-details/class-details-shared.ts → item-details/item-details-shared.ts} +4 -3
- package/app/components/sidebar/notes/{class-details/use-class-details-state.ts → item-details/use-item-details-state.ts} +4 -4
- package/app/components/sidebar/notes/notes-editor-form.tsx +357 -146
- package/app/components/sidebar/notes/notes-editor-modal.tsx +3 -0
- package/app/components/sidebar/notes/notes.module.css +40 -20
- package/app/components/sidebar/sidebar-container.tsx +1 -1
- package/app/components/sidebar/sidebar.tsx +3 -3
- package/app/components/toolbar/toolbar.tsx +5 -5
- package/app/hooks/useFileListPreferences.ts +22 -17
- package/app/routes/striae/striae.tsx +6 -13
- package/app/types/annotations.ts +29 -5
- package/app/utils/data/confirmation-summary/summary-core.ts +40 -8
- package/app/utils/data/file-filters.ts +39 -17
- package/app/utils/data/permissions.ts +123 -0
- package/package.json +12 -12
- package/workers/audit-worker/package.json +2 -2
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/package.json +2 -2
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/package.json +2 -2
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/package.json +2 -2
- package/workers/pdf-worker/src/formats/format-striae.ts +65 -8
- package/workers/pdf-worker/src/report-types.ts +18 -4
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/package.json +2 -2
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
|
@@ -2,10 +2,10 @@ import { useState, useEffect, useCallback, useLayoutEffect } from 'react';
|
|
|
2
2
|
import type { User } from 'firebase/auth';
|
|
3
3
|
import { ColorSelector } from '~/components/colors/colors';
|
|
4
4
|
import { AddlNotesModal } from './addl-notes-modal';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { ItemDetailsModal } from './item-details/item-details-modal';
|
|
6
|
+
import { buildItemDetailsSummary } from './item-details/item-details-shared';
|
|
7
7
|
import { getNotes, saveNotes } from '~/components/actions/notes-manage';
|
|
8
|
-
import { type AnnotationData, type BulletAnnotationData, type CartridgeCaseAnnotationData, type ShotshellAnnotationData } from '~/types/annotations';
|
|
8
|
+
import { type AnnotationData, type BulletAnnotationData, type CartridgeCaseAnnotationData, type ShotshellAnnotationData, type ItemType, type SupportLevel, type IndexType } from '~/types/annotations';
|
|
9
9
|
import { resolveEarliestAnnotationTimestamp } from '~/utils/ui';
|
|
10
10
|
import { auditService } from '~/services/audit';
|
|
11
11
|
import styles from './notes.module.css';
|
|
@@ -17,33 +17,41 @@ interface NotesEditorFormProps {
|
|
|
17
17
|
onAnnotationRefresh?: () => void;
|
|
18
18
|
originalFileName?: string;
|
|
19
19
|
isUploading?: boolean;
|
|
20
|
+
isReadOnly?: boolean;
|
|
20
21
|
showNotification?: (message: string, type: 'success' | 'error' | 'warning') => void;
|
|
21
22
|
onDirtyChange?: (isDirty: boolean) => void;
|
|
22
23
|
onRegisterSaveHandler?: (saveHandler: (() => Promise<boolean>) | null) => void;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
type SupportLevel = 'ID' | 'Exclusion' | 'Inconclusive';
|
|
26
|
-
type ClassType = 'Bullet' | 'Cartridge Case' | 'Shotshell' | 'Other';
|
|
27
|
-
type IndexType = 'number' | 'color';
|
|
28
|
-
|
|
29
26
|
interface NotesFormSnapshot {
|
|
30
27
|
leftCase: string;
|
|
31
28
|
rightCase: string;
|
|
32
29
|
leftItem: string;
|
|
33
30
|
rightItem: string;
|
|
34
31
|
caseFontColor: string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
// Left item class characteristics
|
|
33
|
+
leftItemType: ItemType | '';
|
|
34
|
+
leftCustomClass: string;
|
|
35
|
+
leftClassNote: string;
|
|
36
|
+
leftHasSubclass: boolean;
|
|
37
|
+
leftBulletData: BulletAnnotationData | undefined;
|
|
38
|
+
leftCartridgeCaseData: CartridgeCaseAnnotationData | undefined;
|
|
39
|
+
leftShotshellData: ShotshellAnnotationData | undefined;
|
|
40
|
+
// Right item class characteristics
|
|
41
|
+
rightItemType: ItemType | '';
|
|
42
|
+
rightCustomClass: string;
|
|
43
|
+
rightClassNote: string;
|
|
44
|
+
rightHasSubclass: boolean;
|
|
45
|
+
rightBulletData: BulletAnnotationData | undefined;
|
|
46
|
+
rightCartridgeCaseData: CartridgeCaseAnnotationData | undefined;
|
|
47
|
+
rightShotshellData: ShotshellAnnotationData | undefined;
|
|
42
48
|
indexType: IndexType;
|
|
43
49
|
indexNumber: string;
|
|
44
50
|
indexColor: string;
|
|
45
51
|
supportLevel: SupportLevel | '';
|
|
46
52
|
includeConfirmation: boolean;
|
|
53
|
+
leftAdditionalNotes: string;
|
|
54
|
+
rightAdditionalNotes: string;
|
|
47
55
|
additionalNotes: string;
|
|
48
56
|
}
|
|
49
57
|
|
|
@@ -73,16 +81,19 @@ const normalizeNestedAnnotationData = <T extends object>(data: T | undefined): T
|
|
|
73
81
|
|
|
74
82
|
const normalizeNotesSnapshot = (snapshot: NotesFormSnapshot): NotesFormSnapshot => ({
|
|
75
83
|
...snapshot,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
leftBulletData: normalizeNestedAnnotationData(snapshot.leftBulletData),
|
|
85
|
+
leftCartridgeCaseData: normalizeNestedAnnotationData(snapshot.leftCartridgeCaseData),
|
|
86
|
+
leftShotshellData: normalizeNestedAnnotationData(snapshot.leftShotshellData),
|
|
87
|
+
rightBulletData: normalizeNestedAnnotationData(snapshot.rightBulletData),
|
|
88
|
+
rightCartridgeCaseData: normalizeNestedAnnotationData(snapshot.rightCartridgeCaseData),
|
|
89
|
+
rightShotshellData: normalizeNestedAnnotationData(snapshot.rightShotshellData),
|
|
79
90
|
});
|
|
80
91
|
|
|
81
92
|
const serializeNotesSnapshot = (snapshot: NotesFormSnapshot): string => JSON.stringify(normalizeNotesSnapshot(snapshot));
|
|
82
93
|
const DIRTY_CHECK_DEBOUNCE_MS = 180;
|
|
83
94
|
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
84
95
|
|
|
85
|
-
export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefresh, originalFileName, isUploading = false, showNotification: externalShowNotification, onDirtyChange, onRegisterSaveHandler }: NotesEditorFormProps) => {
|
|
96
|
+
export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefresh, originalFileName, isUploading = false, isReadOnly = false, showNotification: externalShowNotification, onDirtyChange, onRegisterSaveHandler }: NotesEditorFormProps) => {
|
|
86
97
|
// Loading/Saving Notes States
|
|
87
98
|
const [isLoading, setIsLoading] = useState(false);
|
|
88
99
|
const [loadError, setLoadError] = useState<string>();
|
|
@@ -96,14 +107,27 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
96
107
|
const [useCurrentCaseRight, setUseCurrentCaseRight] = useState(false);
|
|
97
108
|
const [caseFontColor, setCaseFontColor] = useState('');
|
|
98
109
|
|
|
99
|
-
// Class characteristics state
|
|
100
|
-
const [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const [
|
|
104
|
-
const [
|
|
105
|
-
const [
|
|
106
|
-
const [
|
|
110
|
+
// Class characteristics state - selected item indicator
|
|
111
|
+
const [selectedItem, setSelectedItem] = useState<'left' | 'right'>('left');
|
|
112
|
+
|
|
113
|
+
// Left item class characteristics state
|
|
114
|
+
const [leftItemType, setLeftItemType] = useState<ItemType | ''>('');
|
|
115
|
+
const [leftCustomClass, setLeftCustomClass] = useState('');
|
|
116
|
+
const [leftClassNote, setLeftClassNote] = useState('');
|
|
117
|
+
const [leftHasSubclass, setLeftHasSubclass] = useState(false);
|
|
118
|
+
const [leftBulletData, setLeftBulletData] = useState<BulletAnnotationData | undefined>(undefined);
|
|
119
|
+
const [leftCartridgeCaseData, setLeftCartridgeCaseData] = useState<CartridgeCaseAnnotationData | undefined>(undefined);
|
|
120
|
+
const [leftShotshellData, setLeftShotshellData] = useState<ShotshellAnnotationData | undefined>(undefined);
|
|
121
|
+
|
|
122
|
+
// Right item class characteristics state
|
|
123
|
+
const [rightItemType, setRightItemType] = useState<ItemType | ''>('');
|
|
124
|
+
const [rightCustomClass, setRightCustomClass] = useState('');
|
|
125
|
+
const [rightClassNote, setRightClassNote] = useState('');
|
|
126
|
+
const [rightHasSubclass, setRightHasSubclass] = useState(false);
|
|
127
|
+
const [rightBulletData, setRightBulletData] = useState<BulletAnnotationData | undefined>(undefined);
|
|
128
|
+
const [rightCartridgeCaseData, setRightCartridgeCaseData] = useState<CartridgeCaseAnnotationData | undefined>(undefined);
|
|
129
|
+
const [rightShotshellData, setRightShotshellData] = useState<ShotshellAnnotationData | undefined>(undefined);
|
|
130
|
+
|
|
107
131
|
const [isClassDetailsOpen, setIsClassDetailsOpen] = useState(false);
|
|
108
132
|
|
|
109
133
|
// Index state
|
|
@@ -117,6 +141,8 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
117
141
|
|
|
118
142
|
// Additional Notes Modal
|
|
119
143
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
144
|
+
const [leftAdditionalNotes, setLeftAdditionalNotes] = useState('');
|
|
145
|
+
const [rightAdditionalNotes, setRightAdditionalNotes] = useState('');
|
|
120
146
|
const [additionalNotes, setAdditionalNotes] = useState('');
|
|
121
147
|
const [isCaseInfoOpen, setIsCaseInfoOpen] = useState(true);
|
|
122
148
|
const [isClassOpen, setIsClassOpen] = useState(true);
|
|
@@ -125,7 +151,9 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
125
151
|
const [savedSnapshot, setSavedSnapshot] = useState<string>('');
|
|
126
152
|
const [hasLoadedSnapshot, setHasLoadedSnapshot] = useState(false);
|
|
127
153
|
const [isDirty, setIsDirty] = useState(false);
|
|
128
|
-
const
|
|
154
|
+
const areEditsDisabled = isUploading || isReadOnly || isConfirmedImage;
|
|
155
|
+
const isReadOnlyMode = isConfirmedImage || isReadOnly;
|
|
156
|
+
const canOpenModals = !isUploading;
|
|
129
157
|
|
|
130
158
|
const notificationHandler = useCallback((message: string, type: 'success' | 'error' | 'warning' = 'success') => {
|
|
131
159
|
if (externalShowNotification) {
|
|
@@ -133,6 +161,58 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
133
161
|
}
|
|
134
162
|
}, [externalShowNotification]);
|
|
135
163
|
|
|
164
|
+
// Helper functions for selected item data access
|
|
165
|
+
const getSelectedItemData = useCallback(() => {
|
|
166
|
+
if (selectedItem === 'left') {
|
|
167
|
+
return {
|
|
168
|
+
itemType: leftItemType,
|
|
169
|
+
customClass: leftCustomClass,
|
|
170
|
+
classNote: leftClassNote,
|
|
171
|
+
hasSubclass: leftHasSubclass,
|
|
172
|
+
bulletData: leftBulletData,
|
|
173
|
+
cartridgeCaseData: leftCartridgeCaseData,
|
|
174
|
+
shotshellData: leftShotshellData,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
itemType: rightItemType,
|
|
179
|
+
customClass: rightCustomClass,
|
|
180
|
+
classNote: rightClassNote,
|
|
181
|
+
hasSubclass: rightHasSubclass,
|
|
182
|
+
bulletData: rightBulletData,
|
|
183
|
+
cartridgeCaseData: rightCartridgeCaseData,
|
|
184
|
+
shotshellData: rightShotshellData,
|
|
185
|
+
};
|
|
186
|
+
}, [selectedItem, leftItemType, leftCustomClass, leftClassNote, leftHasSubclass, leftBulletData, leftCartridgeCaseData, leftShotshellData, rightItemType, rightCustomClass, rightClassNote, rightHasSubclass, rightBulletData, rightCartridgeCaseData, rightShotshellData]);
|
|
187
|
+
|
|
188
|
+
const setSelectedItemData = useCallback((newData: {
|
|
189
|
+
itemType?: ItemType | '';
|
|
190
|
+
customClass?: string;
|
|
191
|
+
classNote?: string;
|
|
192
|
+
hasSubclass?: boolean;
|
|
193
|
+
bulletData?: BulletAnnotationData;
|
|
194
|
+
cartridgeCaseData?: CartridgeCaseAnnotationData;
|
|
195
|
+
shotshellData?: ShotshellAnnotationData;
|
|
196
|
+
}) => {
|
|
197
|
+
if (selectedItem === 'left') {
|
|
198
|
+
if (newData.itemType !== undefined) setLeftItemType(newData.itemType);
|
|
199
|
+
if (newData.customClass !== undefined) setLeftCustomClass(newData.customClass);
|
|
200
|
+
if (newData.classNote !== undefined) setLeftClassNote(newData.classNote);
|
|
201
|
+
if (newData.hasSubclass !== undefined) setLeftHasSubclass(newData.hasSubclass);
|
|
202
|
+
if (newData.bulletData !== undefined) setLeftBulletData(newData.bulletData);
|
|
203
|
+
if (newData.cartridgeCaseData !== undefined) setLeftCartridgeCaseData(newData.cartridgeCaseData);
|
|
204
|
+
if (newData.shotshellData !== undefined) setLeftShotshellData(newData.shotshellData);
|
|
205
|
+
} else {
|
|
206
|
+
if (newData.itemType !== undefined) setRightItemType(newData.itemType);
|
|
207
|
+
if (newData.customClass !== undefined) setRightCustomClass(newData.customClass);
|
|
208
|
+
if (newData.classNote !== undefined) setRightClassNote(newData.classNote);
|
|
209
|
+
if (newData.hasSubclass !== undefined) setRightHasSubclass(newData.hasSubclass);
|
|
210
|
+
if (newData.bulletData !== undefined) setRightBulletData(newData.bulletData);
|
|
211
|
+
if (newData.cartridgeCaseData !== undefined) setRightCartridgeCaseData(newData.cartridgeCaseData);
|
|
212
|
+
if (newData.shotshellData !== undefined) setRightShotshellData(newData.shotshellData);
|
|
213
|
+
}
|
|
214
|
+
}, [selectedItem]);
|
|
215
|
+
|
|
136
216
|
useEffect(() => {
|
|
137
217
|
if (!hasLoadedSnapshot) {
|
|
138
218
|
return;
|
|
@@ -145,18 +225,27 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
145
225
|
leftItem,
|
|
146
226
|
rightItem,
|
|
147
227
|
caseFontColor,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
228
|
+
leftItemType,
|
|
229
|
+
leftCustomClass,
|
|
230
|
+
leftClassNote,
|
|
231
|
+
leftHasSubclass,
|
|
232
|
+
leftBulletData,
|
|
233
|
+
leftCartridgeCaseData,
|
|
234
|
+
leftShotshellData,
|
|
235
|
+
rightItemType,
|
|
236
|
+
rightCustomClass,
|
|
237
|
+
rightClassNote,
|
|
238
|
+
rightHasSubclass,
|
|
239
|
+
rightBulletData,
|
|
240
|
+
rightCartridgeCaseData,
|
|
241
|
+
rightShotshellData,
|
|
155
242
|
indexType,
|
|
156
243
|
indexNumber,
|
|
157
244
|
indexColor,
|
|
158
245
|
supportLevel,
|
|
159
246
|
includeConfirmation,
|
|
247
|
+
leftAdditionalNotes,
|
|
248
|
+
rightAdditionalNotes,
|
|
160
249
|
additionalNotes,
|
|
161
250
|
});
|
|
162
251
|
|
|
@@ -168,24 +257,33 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
168
257
|
};
|
|
169
258
|
}, [
|
|
170
259
|
additionalNotes,
|
|
171
|
-
bulletData,
|
|
172
|
-
cartridgeCaseData,
|
|
173
|
-
caseFontColor,
|
|
174
|
-
classNote,
|
|
175
|
-
classType,
|
|
176
|
-
customClass,
|
|
177
260
|
hasLoadedSnapshot,
|
|
178
|
-
hasSubclass,
|
|
179
261
|
includeConfirmation,
|
|
180
262
|
indexColor,
|
|
181
263
|
indexNumber,
|
|
182
264
|
indexType,
|
|
265
|
+
leftBulletData,
|
|
266
|
+
leftCartridgeCaseData,
|
|
183
267
|
leftCase,
|
|
268
|
+
leftClassNote,
|
|
269
|
+
leftCustomClass,
|
|
270
|
+
leftHasSubclass,
|
|
271
|
+
leftItemType,
|
|
184
272
|
leftItem,
|
|
273
|
+
leftShotshellData,
|
|
274
|
+
rightBulletData,
|
|
275
|
+
rightCartridgeCaseData,
|
|
185
276
|
rightCase,
|
|
277
|
+
rightClassNote,
|
|
278
|
+
rightCustomClass,
|
|
279
|
+
rightHasSubclass,
|
|
280
|
+
rightItemType,
|
|
186
281
|
rightItem,
|
|
282
|
+
rightShotshellData,
|
|
283
|
+
caseFontColor,
|
|
187
284
|
savedSnapshot,
|
|
188
|
-
|
|
285
|
+
leftAdditionalNotes,
|
|
286
|
+
rightAdditionalNotes,
|
|
189
287
|
supportLevel,
|
|
190
288
|
]);
|
|
191
289
|
|
|
@@ -213,19 +311,42 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
213
311
|
setLeftItem(existingNotes.leftItem);
|
|
214
312
|
setRightItem(existingNotes.rightItem);
|
|
215
313
|
setCaseFontColor(existingNotes.caseFontColor || '');
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
314
|
+
|
|
315
|
+
// Migration: if old single-set fields exist, map to left item; otherwise use new left/right fields
|
|
316
|
+
const migratedLeftItemType = existingNotes.leftItemType || existingNotes.itemType || (existingNotes.classType as ItemType | undefined) || '';
|
|
317
|
+
const migratedLeftCustomClass = existingNotes.leftCustomClass || existingNotes.customClass || '';
|
|
318
|
+
const migratedLeftClassNote = existingNotes.leftClassNote || existingNotes.classNote || '';
|
|
319
|
+
const migratedLeftHasSubclass = existingNotes.leftHasSubclass ?? existingNotes.hasSubclass ?? false;
|
|
320
|
+
const migratedLeftBulletData = existingNotes.leftBulletData || existingNotes.bulletData;
|
|
321
|
+
const migratedLeftCartridgeCaseData = existingNotes.leftCartridgeCaseData || existingNotes.cartridgeCaseData;
|
|
322
|
+
const migratedLeftShotshellData = existingNotes.leftShotshellData || existingNotes.shotshellData;
|
|
323
|
+
|
|
324
|
+
setLeftItemType(migratedLeftItemType);
|
|
325
|
+
setLeftCustomClass(migratedLeftCustomClass);
|
|
326
|
+
setLeftClassNote(migratedLeftClassNote);
|
|
327
|
+
setLeftHasSubclass(migratedLeftHasSubclass);
|
|
328
|
+
setLeftBulletData(migratedLeftBulletData);
|
|
329
|
+
setLeftCartridgeCaseData(migratedLeftCartridgeCaseData);
|
|
330
|
+
setLeftShotshellData(migratedLeftShotshellData);
|
|
331
|
+
|
|
332
|
+
// Set right item fields (new structure)
|
|
333
|
+
setRightItemType(existingNotes.rightItemType || existingNotes.itemType || (existingNotes.classType as ItemType | undefined) || '');
|
|
334
|
+
setRightCustomClass(existingNotes.rightCustomClass || '');
|
|
335
|
+
setRightClassNote(existingNotes.rightClassNote || '');
|
|
336
|
+
setRightHasSubclass(existingNotes.rightHasSubclass ?? false);
|
|
337
|
+
setRightBulletData(existingNotes.rightBulletData);
|
|
338
|
+
setRightCartridgeCaseData(existingNotes.rightCartridgeCaseData);
|
|
339
|
+
setRightShotshellData(existingNotes.rightShotshellData);
|
|
340
|
+
|
|
223
341
|
setIndexType(existingNotes.indexType || 'color');
|
|
224
342
|
setIndexNumber(existingNotes.indexNumber || '');
|
|
225
343
|
setIndexColor(existingNotes.indexColor || '');
|
|
226
344
|
setSupportLevel(existingNotes.supportLevel || '');
|
|
227
345
|
setIncludeConfirmation(existingNotes.includeConfirmation);
|
|
346
|
+
setLeftAdditionalNotes(existingNotes.leftAdditionalNotes || '');
|
|
347
|
+
setRightAdditionalNotes(existingNotes.rightAdditionalNotes || '');
|
|
228
348
|
setAdditionalNotes(existingNotes.additionalNotes || '');
|
|
349
|
+
setSelectedItem('left'); // Always default to left item
|
|
229
350
|
|
|
230
351
|
setSavedSnapshot(serializeNotesSnapshot({
|
|
231
352
|
leftCase: existingNotes.leftCase || '',
|
|
@@ -233,18 +354,27 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
233
354
|
leftItem: existingNotes.leftItem || '',
|
|
234
355
|
rightItem: existingNotes.rightItem || '',
|
|
235
356
|
caseFontColor: existingNotes.caseFontColor || '',
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
357
|
+
leftItemType: migratedLeftItemType,
|
|
358
|
+
leftCustomClass: migratedLeftCustomClass,
|
|
359
|
+
leftClassNote: migratedLeftClassNote,
|
|
360
|
+
leftHasSubclass: migratedLeftHasSubclass,
|
|
361
|
+
leftBulletData: migratedLeftBulletData,
|
|
362
|
+
leftCartridgeCaseData: migratedLeftCartridgeCaseData,
|
|
363
|
+
leftShotshellData: migratedLeftShotshellData,
|
|
364
|
+
rightItemType: existingNotes.rightItemType || '',
|
|
365
|
+
rightCustomClass: existingNotes.rightCustomClass || '',
|
|
366
|
+
rightClassNote: existingNotes.rightClassNote || '',
|
|
367
|
+
rightHasSubclass: existingNotes.rightHasSubclass ?? false,
|
|
368
|
+
rightBulletData: existingNotes.rightBulletData,
|
|
369
|
+
rightCartridgeCaseData: existingNotes.rightCartridgeCaseData,
|
|
370
|
+
rightShotshellData: existingNotes.rightShotshellData,
|
|
243
371
|
indexType: existingNotes.indexType || 'color',
|
|
244
372
|
indexNumber: existingNotes.indexNumber || '',
|
|
245
373
|
indexColor: existingNotes.indexColor || '',
|
|
246
374
|
supportLevel: existingNotes.supportLevel || '',
|
|
247
375
|
includeConfirmation: existingNotes.includeConfirmation,
|
|
376
|
+
leftAdditionalNotes: existingNotes.leftAdditionalNotes || '',
|
|
377
|
+
rightAdditionalNotes: existingNotes.rightAdditionalNotes || '',
|
|
248
378
|
additionalNotes: existingNotes.additionalNotes || ''
|
|
249
379
|
}));
|
|
250
380
|
} else {
|
|
@@ -256,18 +386,27 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
256
386
|
leftItem: '',
|
|
257
387
|
rightItem: '',
|
|
258
388
|
caseFontColor: '',
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
389
|
+
leftItemType: '',
|
|
390
|
+
leftCustomClass: '',
|
|
391
|
+
leftClassNote: '',
|
|
392
|
+
leftHasSubclass: false,
|
|
393
|
+
leftBulletData: undefined,
|
|
394
|
+
leftCartridgeCaseData: undefined,
|
|
395
|
+
leftShotshellData: undefined,
|
|
396
|
+
rightItemType: '',
|
|
397
|
+
rightCustomClass: '',
|
|
398
|
+
rightClassNote: '',
|
|
399
|
+
rightHasSubclass: false,
|
|
400
|
+
rightBulletData: undefined,
|
|
401
|
+
rightCartridgeCaseData: undefined,
|
|
402
|
+
rightShotshellData: undefined,
|
|
266
403
|
indexType: 'color',
|
|
267
404
|
indexNumber: '',
|
|
268
405
|
indexColor: '',
|
|
269
406
|
supportLevel: '',
|
|
270
407
|
includeConfirmation: false,
|
|
408
|
+
leftAdditionalNotes: '',
|
|
409
|
+
rightAdditionalNotes: '',
|
|
271
410
|
additionalNotes: ''
|
|
272
411
|
}));
|
|
273
412
|
}
|
|
@@ -303,6 +442,11 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
303
442
|
return false;
|
|
304
443
|
}
|
|
305
444
|
|
|
445
|
+
if (isReadOnly) {
|
|
446
|
+
notificationHandler('This case is read-only. Notes cannot be modified.', 'error');
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
306
450
|
let existingData: AnnotationData | null = null;
|
|
307
451
|
|
|
308
452
|
try {
|
|
@@ -315,9 +459,12 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
315
459
|
return false;
|
|
316
460
|
}
|
|
317
461
|
|
|
318
|
-
const
|
|
319
|
-
const
|
|
320
|
-
const
|
|
462
|
+
const normalizedLeftBulletData = normalizeNestedAnnotationData(leftBulletData);
|
|
463
|
+
const normalizedLeftCartridgeCaseData = normalizeNestedAnnotationData(leftCartridgeCaseData);
|
|
464
|
+
const normalizedLeftShotshellData = normalizeNestedAnnotationData(leftShotshellData);
|
|
465
|
+
const normalizedRightBulletData = normalizeNestedAnnotationData(rightBulletData);
|
|
466
|
+
const normalizedRightCartridgeCaseData = normalizeNestedAnnotationData(rightCartridgeCaseData);
|
|
467
|
+
const normalizedRightShotshellData = normalizeNestedAnnotationData(rightShotshellData);
|
|
321
468
|
|
|
322
469
|
// Create updated annotation data, preserving box annotations and earliest timestamp
|
|
323
470
|
const now = new Date().toISOString();
|
|
@@ -329,14 +476,23 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
329
476
|
rightItem: rightItem || '',
|
|
330
477
|
caseFontColor: caseFontColor || undefined,
|
|
331
478
|
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
479
|
+
// Left item class characteristics
|
|
480
|
+
leftItemType: leftItemType as ItemType || undefined,
|
|
481
|
+
leftCustomClass: leftCustomClass,
|
|
482
|
+
leftClassNote: leftClassNote || undefined,
|
|
483
|
+
leftHasSubclass: leftHasSubclass,
|
|
484
|
+
leftBulletData: normalizedLeftBulletData,
|
|
485
|
+
leftCartridgeCaseData: normalizedLeftCartridgeCaseData,
|
|
486
|
+
leftShotshellData: normalizedLeftShotshellData,
|
|
487
|
+
|
|
488
|
+
// Right item class characteristics
|
|
489
|
+
rightItemType: rightItemType as ItemType || undefined,
|
|
490
|
+
rightCustomClass: rightCustomClass,
|
|
491
|
+
rightClassNote: rightClassNote || undefined,
|
|
492
|
+
rightHasSubclass: rightHasSubclass,
|
|
493
|
+
rightBulletData: normalizedRightBulletData,
|
|
494
|
+
rightCartridgeCaseData: normalizedRightCartridgeCaseData,
|
|
495
|
+
rightShotshellData: normalizedRightShotshellData,
|
|
340
496
|
|
|
341
497
|
// Index Information
|
|
342
498
|
indexType: indexType,
|
|
@@ -348,7 +504,9 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
348
504
|
includeConfirmation: includeConfirmation,
|
|
349
505
|
|
|
350
506
|
// Additional Notes
|
|
351
|
-
|
|
507
|
+
leftAdditionalNotes: leftAdditionalNotes || undefined,
|
|
508
|
+
rightAdditionalNotes: rightAdditionalNotes || undefined,
|
|
509
|
+
additionalNotes: additionalNotes || undefined, // General notes (including box-annotation notes)
|
|
352
510
|
|
|
353
511
|
// Preserve existing box annotations
|
|
354
512
|
boxAnnotations: existingData?.boxAnnotations || [],
|
|
@@ -385,18 +543,27 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
385
543
|
leftItem,
|
|
386
544
|
rightItem,
|
|
387
545
|
caseFontColor,
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
546
|
+
leftItemType,
|
|
547
|
+
leftCustomClass,
|
|
548
|
+
leftClassNote,
|
|
549
|
+
leftHasSubclass,
|
|
550
|
+
leftBulletData,
|
|
551
|
+
leftCartridgeCaseData,
|
|
552
|
+
leftShotshellData,
|
|
553
|
+
rightItemType,
|
|
554
|
+
rightCustomClass,
|
|
555
|
+
rightClassNote,
|
|
556
|
+
rightHasSubclass,
|
|
557
|
+
rightBulletData,
|
|
558
|
+
rightCartridgeCaseData,
|
|
559
|
+
rightShotshellData,
|
|
395
560
|
indexType,
|
|
396
561
|
indexNumber,
|
|
397
562
|
indexColor,
|
|
398
563
|
supportLevel,
|
|
399
564
|
includeConfirmation,
|
|
565
|
+
leftAdditionalNotes,
|
|
566
|
+
rightAdditionalNotes,
|
|
400
567
|
additionalNotes,
|
|
401
568
|
}));
|
|
402
569
|
setIsDirty(false);
|
|
@@ -438,28 +605,38 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
438
605
|
}
|
|
439
606
|
}, [
|
|
440
607
|
additionalNotes,
|
|
441
|
-
|
|
442
|
-
|
|
608
|
+
leftBulletData,
|
|
609
|
+
leftCartridgeCaseData,
|
|
443
610
|
caseFontColor,
|
|
444
|
-
|
|
445
|
-
|
|
611
|
+
leftClassNote,
|
|
612
|
+
leftItemType,
|
|
446
613
|
currentCase,
|
|
447
|
-
|
|
448
|
-
|
|
614
|
+
leftCustomClass,
|
|
615
|
+
leftHasSubclass,
|
|
449
616
|
imageId,
|
|
450
617
|
includeConfirmation,
|
|
451
618
|
indexColor,
|
|
452
619
|
indexNumber,
|
|
453
620
|
indexType,
|
|
621
|
+
isReadOnly,
|
|
454
622
|
leftCase,
|
|
455
623
|
leftItem,
|
|
456
624
|
notificationHandler,
|
|
457
625
|
onAnnotationRefresh,
|
|
458
626
|
onDirtyChange,
|
|
459
627
|
originalFileName,
|
|
628
|
+
rightBulletData,
|
|
629
|
+
rightCartridgeCaseData,
|
|
460
630
|
rightCase,
|
|
631
|
+
rightClassNote,
|
|
632
|
+
rightCustomClass,
|
|
633
|
+
rightHasSubclass,
|
|
634
|
+
rightItemType,
|
|
461
635
|
rightItem,
|
|
462
|
-
|
|
636
|
+
rightShotshellData,
|
|
637
|
+
leftShotshellData,
|
|
638
|
+
leftAdditionalNotes,
|
|
639
|
+
rightAdditionalNotes,
|
|
463
640
|
supportLevel,
|
|
464
641
|
user,
|
|
465
642
|
]);
|
|
@@ -509,7 +686,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
509
686
|
type="text"
|
|
510
687
|
value={leftCase}
|
|
511
688
|
onChange={(e) => setLeftCase(e.target.value)}
|
|
512
|
-
disabled={useCurrentCaseLeft ||
|
|
689
|
+
disabled={useCurrentCaseLeft || areEditsDisabled}
|
|
513
690
|
/>
|
|
514
691
|
</div>
|
|
515
692
|
<label className={`${styles.checkboxLabel} mb-4`}>
|
|
@@ -518,7 +695,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
518
695
|
checked={useCurrentCaseLeft}
|
|
519
696
|
onChange={(e) => setUseCurrentCaseLeft(e.target.checked)}
|
|
520
697
|
className={styles.checkbox}
|
|
521
|
-
disabled={
|
|
698
|
+
disabled={areEditsDisabled}
|
|
522
699
|
/>
|
|
523
700
|
<span>Use current case number</span>
|
|
524
701
|
</label>
|
|
@@ -529,7 +706,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
529
706
|
type="text"
|
|
530
707
|
value={leftItem}
|
|
531
708
|
onChange={(e) => setLeftItem(e.target.value)}
|
|
532
|
-
disabled={
|
|
709
|
+
disabled={areEditsDisabled}
|
|
533
710
|
/>
|
|
534
711
|
</div>
|
|
535
712
|
</div>
|
|
@@ -542,7 +719,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
542
719
|
type="text"
|
|
543
720
|
value={rightCase}
|
|
544
721
|
onChange={(e) => setRightCase(e.target.value)}
|
|
545
|
-
disabled={useCurrentCaseRight ||
|
|
722
|
+
disabled={useCurrentCaseRight || areEditsDisabled}
|
|
546
723
|
/>
|
|
547
724
|
</div>
|
|
548
725
|
<label className={`${styles.checkboxLabel} mb-4`}>
|
|
@@ -551,7 +728,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
551
728
|
checked={useCurrentCaseRight}
|
|
552
729
|
onChange={(e) => setUseCurrentCaseRight(e.target.checked)}
|
|
553
730
|
className={styles.checkbox}
|
|
554
|
-
disabled={
|
|
731
|
+
disabled={areEditsDisabled}
|
|
555
732
|
/>
|
|
556
733
|
<span>Use current case number</span>
|
|
557
734
|
</label>
|
|
@@ -562,17 +739,18 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
562
739
|
type="text"
|
|
563
740
|
value={rightItem}
|
|
564
741
|
onChange={(e) => setRightItem(e.target.value)}
|
|
565
|
-
disabled={
|
|
742
|
+
disabled={areEditsDisabled}
|
|
566
743
|
/>
|
|
567
744
|
</div>
|
|
568
745
|
</div>
|
|
569
746
|
</div>
|
|
570
747
|
<hr />
|
|
571
748
|
<div className={styles.fontColorRow}>
|
|
572
|
-
<label htmlFor="colorSelect">Font</label>
|
|
749
|
+
<label htmlFor="colorSelect">Case & Item Font Color</label>
|
|
573
750
|
<ColorSelector
|
|
574
751
|
selectedColor={caseFontColor}
|
|
575
752
|
onColorSelect={setCaseFontColor}
|
|
753
|
+
disabled={areEditsDisabled}
|
|
576
754
|
/>
|
|
577
755
|
</div>
|
|
578
756
|
</>
|
|
@@ -587,68 +765,83 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
587
765
|
onClick={() => setIsClassOpen((prev) => !prev)}
|
|
588
766
|
aria-expanded={isClassOpen}
|
|
589
767
|
>
|
|
590
|
-
<span className={styles.sectionTitle}>Class Characteristics</span>
|
|
768
|
+
<span className={styles.sectionTitle}>Class Characteristics & GRC</span>
|
|
591
769
|
<span className={styles.sectionToggleIcon}>{isClassOpen ? '−' : '+'}</span>
|
|
592
770
|
</button>
|
|
593
771
|
{isClassOpen && (
|
|
594
772
|
<>
|
|
773
|
+
<hr />
|
|
774
|
+
<div className={styles.itemSelectorRow}>
|
|
775
|
+
<label htmlFor="itemSelector">Select Item</label>
|
|
776
|
+
<select
|
|
777
|
+
id="itemSelector"
|
|
778
|
+
aria-label="Select item to edit"
|
|
779
|
+
value={selectedItem}
|
|
780
|
+
onChange={(e) => setSelectedItem(e.target.value as 'left' | 'right')}
|
|
781
|
+
className={styles.select}
|
|
782
|
+
>
|
|
783
|
+
<option value="left">{`Case: ${leftCase || '—'} Item: ${leftItem || '—'}`}</option>
|
|
784
|
+
<option value="right" disabled={!rightItem && !rightCase}>
|
|
785
|
+
{`Case: ${rightCase || '—'} Item: ${rightItem || '—'}`}
|
|
786
|
+
</option>
|
|
787
|
+
</select>
|
|
788
|
+
</div>
|
|
595
789
|
<div className={styles.classCharacteristicsColumns}>
|
|
596
790
|
<div className={styles.classCharacteristicsMain}>
|
|
597
791
|
<div className={styles.classCharacteristics}>
|
|
598
792
|
<select
|
|
599
|
-
id="
|
|
600
|
-
aria-label="
|
|
601
|
-
value={
|
|
602
|
-
onChange={(e) =>
|
|
793
|
+
id="itemType"
|
|
794
|
+
aria-label="Item Type"
|
|
795
|
+
value={getSelectedItemData().itemType}
|
|
796
|
+
onChange={(e) => setSelectedItemData({ itemType: e.target.value as ItemType })}
|
|
603
797
|
className={styles.select}
|
|
604
|
-
disabled={
|
|
798
|
+
disabled={areEditsDisabled}
|
|
605
799
|
>
|
|
606
|
-
<option value="">Select
|
|
800
|
+
<option value="">Select item type...</option>
|
|
607
801
|
<option value="Bullet">Bullet</option>
|
|
608
802
|
<option value="Cartridge Case">Cartridge Case</option>
|
|
609
803
|
<option value="Shotshell">Shotshell</option>
|
|
610
804
|
<option value="Other">Other</option>
|
|
611
805
|
</select>
|
|
612
806
|
|
|
613
|
-
{
|
|
807
|
+
{getSelectedItemData().itemType === 'Other' && (
|
|
614
808
|
<input
|
|
615
809
|
type="text"
|
|
616
|
-
value={customClass}
|
|
617
|
-
onChange={(e) =>
|
|
810
|
+
value={getSelectedItemData().customClass}
|
|
811
|
+
onChange={(e) => setSelectedItemData({ customClass: e.target.value })}
|
|
618
812
|
placeholder="Specify object type"
|
|
619
|
-
disabled={
|
|
813
|
+
disabled={areEditsDisabled}
|
|
620
814
|
/>
|
|
621
815
|
)}
|
|
622
816
|
|
|
623
817
|
<textarea
|
|
624
|
-
value={classNote}
|
|
625
|
-
onChange={(e) =>
|
|
626
|
-
placeholder="Enter
|
|
818
|
+
value={getSelectedItemData().classNote}
|
|
819
|
+
onChange={(e) => setSelectedItemData({ classNote: e.target.value })}
|
|
820
|
+
placeholder="Enter item details..."
|
|
627
821
|
className={styles.textarea}
|
|
628
|
-
disabled={
|
|
822
|
+
disabled={areEditsDisabled}
|
|
629
823
|
/>
|
|
630
824
|
</div>
|
|
631
|
-
<label className={`${styles.checkboxLabel} mb-4`}>
|
|
632
|
-
<input
|
|
633
|
-
type="checkbox"
|
|
634
|
-
checked={hasSubclass}
|
|
635
|
-
onChange={(e) => setHasSubclass(e.target.checked)}
|
|
636
|
-
className={styles.checkbox}
|
|
637
|
-
disabled={areInputsDisabled}
|
|
638
|
-
/>
|
|
639
|
-
<span>Potential subclass?</span>
|
|
640
|
-
</label>
|
|
641
825
|
</div>
|
|
642
826
|
|
|
643
|
-
<div className={styles.
|
|
827
|
+
<div className={styles.itemDetailsPanel}>
|
|
644
828
|
<button
|
|
645
829
|
type="button"
|
|
646
830
|
onClick={() => setIsClassDetailsOpen(true)}
|
|
647
|
-
className={styles.
|
|
648
|
-
disabled={areInputsDisabled}
|
|
831
|
+
className={styles.itemDetailsButton}
|
|
649
832
|
>
|
|
650
|
-
|
|
833
|
+
Class Characteristics & GRC
|
|
651
834
|
</button>
|
|
835
|
+
<label className={`${styles.checkboxLabel} mb-4`}>
|
|
836
|
+
<input
|
|
837
|
+
type="checkbox"
|
|
838
|
+
checked={getSelectedItemData().hasSubclass}
|
|
839
|
+
onChange={(e) => setSelectedItemData({ hasSubclass: e.target.checked })}
|
|
840
|
+
className={styles.checkbox}
|
|
841
|
+
disabled={areEditsDisabled}
|
|
842
|
+
/>
|
|
843
|
+
<span>Potential subclass?</span>
|
|
844
|
+
</label>
|
|
652
845
|
</div>
|
|
653
846
|
</div>
|
|
654
847
|
</>
|
|
@@ -673,7 +866,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
673
866
|
type="radio"
|
|
674
867
|
checked={indexType === 'color'}
|
|
675
868
|
onChange={() => setIndexType('color')}
|
|
676
|
-
disabled={
|
|
869
|
+
disabled={areEditsDisabled}
|
|
677
870
|
/>
|
|
678
871
|
<span>Color</span>
|
|
679
872
|
</label>
|
|
@@ -682,7 +875,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
682
875
|
type="radio"
|
|
683
876
|
checked={indexType === 'number'}
|
|
684
877
|
onChange={() => setIndexType('number')}
|
|
685
|
-
disabled={
|
|
878
|
+
disabled={areEditsDisabled}
|
|
686
879
|
/>
|
|
687
880
|
<span>Number/Letter</span>
|
|
688
881
|
</label>
|
|
@@ -694,12 +887,13 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
694
887
|
value={indexNumber}
|
|
695
888
|
onChange={(e) => setIndexNumber(e.target.value)}
|
|
696
889
|
placeholder="Enter index number"
|
|
697
|
-
disabled={
|
|
890
|
+
disabled={areEditsDisabled}
|
|
698
891
|
/>
|
|
699
892
|
) : indexType === 'color' ? (
|
|
700
893
|
<ColorSelector
|
|
701
894
|
selectedColor={indexColor}
|
|
702
895
|
onColorSelect={setIndexColor}
|
|
896
|
+
disabled={areEditsDisabled}
|
|
703
897
|
/>
|
|
704
898
|
) : null}
|
|
705
899
|
</div>
|
|
@@ -733,7 +927,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
733
927
|
}
|
|
734
928
|
}}
|
|
735
929
|
className={styles.select}
|
|
736
|
-
disabled={
|
|
930
|
+
disabled={areEditsDisabled}
|
|
737
931
|
>
|
|
738
932
|
<option value="">Select support level...</option>
|
|
739
933
|
<option value="ID">Identification</option>
|
|
@@ -746,7 +940,7 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
746
940
|
checked={includeConfirmation}
|
|
747
941
|
onChange={(e) => setIncludeConfirmation(e.target.checked)}
|
|
748
942
|
className={styles.checkbox}
|
|
749
|
-
disabled={
|
|
943
|
+
disabled={areEditsDisabled}
|
|
750
944
|
/>
|
|
751
945
|
<span>Include confirmation field</span>
|
|
752
946
|
</label>
|
|
@@ -760,8 +954,8 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
760
954
|
<button
|
|
761
955
|
onClick={() => setIsModalOpen(true)}
|
|
762
956
|
className={styles.notesButton}
|
|
763
|
-
disabled={
|
|
764
|
-
title={
|
|
957
|
+
disabled={!canOpenModals}
|
|
958
|
+
title={isUploading ? "Cannot open notes while uploading" : undefined}
|
|
765
959
|
>
|
|
766
960
|
Additional Notes
|
|
767
961
|
</button>
|
|
@@ -771,8 +965,8 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
771
965
|
<button
|
|
772
966
|
onClick={handleSave}
|
|
773
967
|
className={styles.saveButton}
|
|
774
|
-
disabled={
|
|
775
|
-
title={isConfirmedImage ? "Cannot save notes
|
|
968
|
+
disabled={areEditsDisabled}
|
|
969
|
+
title={isConfirmedImage ? "Cannot save notes - image is confirmed" : isUploading ? "Cannot save notes while uploading" : isReadOnly ? "Cannot save notes - case is read-only" : undefined}
|
|
776
970
|
>
|
|
777
971
|
Save Notes
|
|
778
972
|
</button>
|
|
@@ -781,27 +975,44 @@ export const NotesEditorForm = ({ currentCase, user, imageId, onAnnotationRefres
|
|
|
781
975
|
isOpen={isModalOpen}
|
|
782
976
|
onClose={() => setIsModalOpen(false)}
|
|
783
977
|
notes={additionalNotes}
|
|
784
|
-
onSave={
|
|
978
|
+
onSave={(notes) => {
|
|
979
|
+
if (areEditsDisabled) {
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
setAdditionalNotes(notes);
|
|
984
|
+
}}
|
|
985
|
+
isReadOnly={isReadOnlyMode}
|
|
785
986
|
showNotification={notificationHandler}
|
|
786
987
|
/>
|
|
787
|
-
<
|
|
988
|
+
<ItemDetailsModal
|
|
788
989
|
isOpen={isClassDetailsOpen}
|
|
789
990
|
onClose={() => setIsClassDetailsOpen(false)}
|
|
790
|
-
|
|
791
|
-
bulletData={bulletData}
|
|
792
|
-
cartridgeCaseData={cartridgeCaseData}
|
|
793
|
-
shotshellData={shotshellData}
|
|
991
|
+
itemType={getSelectedItemData().itemType}
|
|
992
|
+
bulletData={getSelectedItemData().bulletData}
|
|
993
|
+
cartridgeCaseData={getSelectedItemData().cartridgeCaseData}
|
|
994
|
+
shotshellData={getSelectedItemData().shotshellData}
|
|
794
995
|
onSave={(b, c, s) => {
|
|
795
|
-
if (
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
996
|
+
if (areEditsDisabled) {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
setSelectedItemData({
|
|
1001
|
+
bulletData: b,
|
|
1002
|
+
cartridgeCaseData: c,
|
|
1003
|
+
shotshellData: s,
|
|
1004
|
+
});
|
|
1005
|
+
const summary = buildItemDetailsSummary(b, c, s, getSelectedItemData().itemType);
|
|
799
1006
|
if (summary) {
|
|
800
|
-
|
|
1007
|
+
if (selectedItem === 'left') {
|
|
1008
|
+
setLeftAdditionalNotes((prev) => (prev ? `${prev}\n${summary}` : summary));
|
|
1009
|
+
} else {
|
|
1010
|
+
setRightAdditionalNotes((prev) => (prev ? `${prev}\n${summary}` : summary));
|
|
1011
|
+
}
|
|
801
1012
|
}
|
|
802
1013
|
}}
|
|
803
1014
|
showNotification={notificationHandler}
|
|
804
|
-
isReadOnly={
|
|
1015
|
+
isReadOnly={isReadOnlyMode}
|
|
805
1016
|
/>
|
|
806
1017
|
</>
|
|
807
1018
|
)}
|