@striae-org/striae 5.3.0 → 5.3.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 +3 -0
- package/app/components/actions/case-export/download-handlers.ts +1 -1
- package/app/components/actions/case-import/confirmation-import.ts +62 -22
- package/app/components/actions/case-import/confirmation-package.ts +68 -1
- package/app/components/actions/case-import/index.ts +1 -1
- package/app/components/actions/case-import/orchestrator.ts +78 -53
- package/app/components/actions/case-import/zip-processing.ts +157 -407
- package/app/components/navbar/case-modals/export-case-modal.module.css +27 -0
- package/app/components/navbar/case-modals/export-case-modal.tsx +132 -0
- package/app/components/navbar/case-modals/export-confirmations-modal.module.css +24 -0
- package/app/components/navbar/case-modals/export-confirmations-modal.tsx +108 -0
- package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +1 -9
- package/app/components/sidebar/case-import/components/ConfirmationPreviewSection.tsx +36 -5
- package/app/components/sidebar/case-import/hooks/useFilePreview.ts +5 -9
- package/app/components/sidebar/case-import/index.ts +1 -4
- package/app/routes/auth/login.tsx +22 -103
- package/app/routes/striae/striae.tsx +77 -13
- package/app/types/case.ts +1 -0
- package/app/types/export.ts +1 -0
- package/app/types/import.ts +10 -0
- package/package.json +1 -1
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
|
@@ -5,6 +5,8 @@ import { Navbar } from '~/components/navbar/navbar';
|
|
|
5
5
|
import { RenameCaseModal } from '~/components/navbar/case-modals/rename-case-modal';
|
|
6
6
|
import { ArchiveCaseModal } from '~/components/navbar/case-modals/archive-case-modal';
|
|
7
7
|
import { OpenCaseModal } from '~/components/navbar/case-modals/open-case-modal';
|
|
8
|
+
import { ExportCaseModal } from '~/components/navbar/case-modals/export-case-modal';
|
|
9
|
+
import { ExportConfirmationsModal } from '~/components/navbar/case-modals/export-confirmations-modal';
|
|
8
10
|
import { Toolbar } from '~/components/toolbar/toolbar';
|
|
9
11
|
import { Canvas } from '~/components/canvas/canvas';
|
|
10
12
|
import { Toast, type ToastType } from '~/components/toast/toast';
|
|
@@ -20,7 +22,7 @@ import { fetchUserApi } from '~/utils/api';
|
|
|
20
22
|
import { type AnnotationData, type FileData } from '~/types';
|
|
21
23
|
import { validateCaseNumber, renameCase, deleteCase, checkExistingCase, createNewCase, archiveCase, getCaseArchiveDetails } from '~/components/actions/case-manage';
|
|
22
24
|
import { checkReadOnlyCaseExists, deleteReadOnlyCase } from '~/components/actions/case-review';
|
|
23
|
-
import { canCreateCase } from '~/utils/data';
|
|
25
|
+
import { canCreateCase, getCaseConfirmationSummary } from '~/utils/data';
|
|
24
26
|
import {
|
|
25
27
|
resolveEarliestAnnotationTimestamp,
|
|
26
28
|
CREATE_READ_ONLY_CASE_EXISTS_ERROR,
|
|
@@ -91,6 +93,14 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
91
93
|
const [isOpeningCase, setIsOpeningCase] = useState(false);
|
|
92
94
|
const [openCaseHelperText, setOpenCaseHelperText] = useState('');
|
|
93
95
|
const [isArchiveCaseModalOpen, setIsArchiveCaseModalOpen] = useState(false);
|
|
96
|
+
const [isExportCaseModalOpen, setIsExportCaseModalOpen] = useState(false);
|
|
97
|
+
const [isExportingCase, setIsExportingCase] = useState(false);
|
|
98
|
+
const [isExportConfirmationsModalOpen, setIsExportConfirmationsModalOpen] = useState(false);
|
|
99
|
+
const [isExportingConfirmations, setIsExportingConfirmations] = useState(false);
|
|
100
|
+
const [exportConfirmationStats, setExportConfirmationStats] = useState<{
|
|
101
|
+
confirmedCount: number;
|
|
102
|
+
unconfirmedCount: number;
|
|
103
|
+
} | null>(null);
|
|
94
104
|
const [archiveDetails, setArchiveDetails] = useState<{
|
|
95
105
|
archived: boolean;
|
|
96
106
|
archivedAt?: string;
|
|
@@ -276,6 +286,7 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
276
286
|
|
|
277
287
|
const handleExport = async (
|
|
278
288
|
exportCaseNumber: string,
|
|
289
|
+
designatedReviewerEmail?: string,
|
|
279
290
|
onProgress?: (progress: number, label: string) => void
|
|
280
291
|
) => {
|
|
281
292
|
if (!exportCaseNumber) {
|
|
@@ -288,15 +299,20 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
288
299
|
try {
|
|
289
300
|
const caseExportActions = await loadCaseExportActions();
|
|
290
301
|
|
|
291
|
-
await caseExportActions.downloadCaseAsZip(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
302
|
+
await caseExportActions.downloadCaseAsZip(
|
|
303
|
+
user,
|
|
304
|
+
exportCaseNumber,
|
|
305
|
+
(progress) => {
|
|
306
|
+
const roundedProgress = Math.round(progress);
|
|
307
|
+
const label = getExportProgressLabel(progress);
|
|
308
|
+
setToastType('loading');
|
|
309
|
+
setToastMessage(`Exporting case ${exportCaseNumber}... ${label} (${roundedProgress}%)`);
|
|
310
|
+
setToastDuration(0);
|
|
311
|
+
setShowToast(true);
|
|
312
|
+
onProgress?.(roundedProgress, label);
|
|
313
|
+
},
|
|
314
|
+
{ designatedReviewerEmail }
|
|
315
|
+
);
|
|
300
316
|
|
|
301
317
|
showNotification(`Case ${exportCaseNumber} exported successfully.`, 'success');
|
|
302
318
|
} catch (error) {
|
|
@@ -304,16 +320,47 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
304
320
|
}
|
|
305
321
|
};
|
|
306
322
|
|
|
323
|
+
const handleExportCaseModalSubmit = async (designatedReviewerEmail: string | undefined) => {
|
|
324
|
+
setIsExportingCase(true);
|
|
325
|
+
setIsExportCaseModalOpen(false);
|
|
326
|
+
try {
|
|
327
|
+
await handleExport(currentCase || '', designatedReviewerEmail);
|
|
328
|
+
} finally {
|
|
329
|
+
setIsExportingCase(false);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const handleOpenExportConfirmationsModal = async () => {
|
|
334
|
+
if (!currentCase || !user) return;
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
const summary = await getCaseConfirmationSummary(user, currentCase);
|
|
338
|
+
const filesById = summary?.filesById ?? {};
|
|
339
|
+
const values = Object.values(filesById);
|
|
340
|
+
const confirmedCount = values.filter((f) => f.includeConfirmation && f.isConfirmed).length;
|
|
341
|
+
const unconfirmedCount = values.filter((f) => f.includeConfirmation && !f.isConfirmed).length;
|
|
342
|
+
setExportConfirmationStats({ confirmedCount, unconfirmedCount });
|
|
343
|
+
} catch {
|
|
344
|
+
setExportConfirmationStats({ confirmedCount: 0, unconfirmedCount: 0 });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
setIsExportConfirmationsModalOpen(true);
|
|
348
|
+
};
|
|
349
|
+
|
|
307
350
|
const handleExportConfirmations = async () => {
|
|
308
351
|
if (!currentCase || !user) return;
|
|
309
352
|
|
|
353
|
+
setIsExportingConfirmations(true);
|
|
310
354
|
showNotification(`Exporting confirmations for case ${currentCase}...`, 'loading', 0);
|
|
311
355
|
|
|
312
356
|
try {
|
|
313
357
|
await exportConfirmationData(user, currentCase);
|
|
358
|
+
setIsExportConfirmationsModalOpen(false);
|
|
314
359
|
showNotification(`Confirmations for case ${currentCase} exported successfully.`, 'success');
|
|
315
360
|
} catch (e) {
|
|
316
361
|
showNotification(e instanceof Error ? e.message : 'Confirmation export failed. Please try again.', 'error');
|
|
362
|
+
} finally {
|
|
363
|
+
setIsExportingConfirmations(false);
|
|
317
364
|
}
|
|
318
365
|
};
|
|
319
366
|
|
|
@@ -764,9 +811,9 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
764
811
|
onOpenListAllCases={() => setIsListCasesModalOpen(true)}
|
|
765
812
|
onOpenCaseExport={() => {
|
|
766
813
|
if (isReadOnlyCase) {
|
|
767
|
-
void
|
|
814
|
+
void handleOpenExportConfirmationsModal();
|
|
768
815
|
} else {
|
|
769
|
-
|
|
816
|
+
setIsExportCaseModalOpen(true);
|
|
770
817
|
}
|
|
771
818
|
}}
|
|
772
819
|
onOpenAuditTrail={() => setIsAuditTrailOpen(true)}
|
|
@@ -791,7 +838,7 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
791
838
|
onOpenCase={() => {
|
|
792
839
|
void handleOpenCaseModal();
|
|
793
840
|
}}
|
|
794
|
-
onOpenCaseExport={() => void
|
|
841
|
+
onOpenCaseExport={() => void handleOpenExportConfirmationsModal()}
|
|
795
842
|
imageId={imageId}
|
|
796
843
|
currentCase={currentCase}
|
|
797
844
|
imageLoaded={imageLoaded}
|
|
@@ -904,6 +951,23 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
904
951
|
onClose={() => setIsArchiveCaseModalOpen(false)}
|
|
905
952
|
onSubmit={handleArchiveCaseSubmit}
|
|
906
953
|
/>
|
|
954
|
+
<ExportCaseModal
|
|
955
|
+
isOpen={isExportCaseModalOpen}
|
|
956
|
+
caseNumber={currentCase || ''}
|
|
957
|
+
currentUserEmail={user.email ?? undefined}
|
|
958
|
+
isSubmitting={isExportingCase}
|
|
959
|
+
onClose={() => setIsExportCaseModalOpen(false)}
|
|
960
|
+
onSubmit={handleExportCaseModalSubmit}
|
|
961
|
+
/>
|
|
962
|
+
<ExportConfirmationsModal
|
|
963
|
+
isOpen={isExportConfirmationsModalOpen}
|
|
964
|
+
caseNumber={currentCase || ''}
|
|
965
|
+
confirmedCount={exportConfirmationStats?.confirmedCount ?? 0}
|
|
966
|
+
unconfirmedCount={exportConfirmationStats?.unconfirmedCount ?? 0}
|
|
967
|
+
isSubmitting={isExportingConfirmations}
|
|
968
|
+
onClose={() => setIsExportConfirmationsModalOpen(false)}
|
|
969
|
+
onConfirm={() => void handleExportConfirmations()}
|
|
970
|
+
/>
|
|
907
971
|
<Toast
|
|
908
972
|
message={toastMessage}
|
|
909
973
|
type={toastType}
|
package/app/types/case.ts
CHANGED
package/app/types/export.ts
CHANGED
package/app/types/import.ts
CHANGED
|
@@ -78,6 +78,16 @@ export interface ConfirmationImportData {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
export interface ConfirmationImportPreview {
|
|
82
|
+
caseNumber: string;
|
|
83
|
+
exportedBy: string;
|
|
84
|
+
exportedByName: string;
|
|
85
|
+
exportedByCompany: string;
|
|
86
|
+
exportedByBadgeId?: string;
|
|
87
|
+
exportDate: string;
|
|
88
|
+
totalConfirmations: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
81
91
|
export interface CaseImportPreview {
|
|
82
92
|
caseNumber: string;
|
|
83
93
|
archived?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@striae-org/striae",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Striae is a specialized, cloud-native platform designed to streamline forensic firearms identification by providing an intuitive environment for digital comparison image annotation, authenticated confirmations, and automated report generation.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/wrangler.toml.example
CHANGED