@striae-org/striae 4.0.3 → 4.2.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.
- package/.env.example +8 -0
- package/app/components/actions/case-export/core-export.ts +14 -8
- package/app/components/actions/case-export/data-processing.ts +1 -0
- package/app/components/actions/case-export/download-handlers.ts +7 -0
- package/app/components/actions/case-export/metadata-helpers.ts +2 -1
- package/app/components/actions/case-import/confirmation-import.ts +12 -2
- package/app/components/actions/case-import/orchestrator.ts +78 -32
- package/app/components/actions/case-import/storage-operations.ts +97 -8
- package/app/components/actions/case-import/zip-processing.ts +159 -86
- package/app/components/actions/case-manage.ts +430 -8
- package/app/components/actions/confirm-export.ts +13 -4
- package/app/components/actions/generate-pdf.ts +10 -2
- package/app/components/actions/image-manage.ts +77 -44
- package/app/components/audit/user-audit-viewer.tsx +137 -945
- package/app/components/audit/user-audit.module.css +41 -0
- package/app/components/audit/viewer/audit-activity-summary.tsx +52 -0
- package/app/components/audit/viewer/audit-entries-list.tsx +207 -0
- package/app/components/audit/viewer/audit-filters-panel.tsx +307 -0
- package/app/components/audit/viewer/audit-user-info-card.tsx +44 -0
- package/app/components/audit/viewer/audit-viewer-header.tsx +55 -0
- package/app/components/audit/viewer/audit-viewer-utils.ts +123 -0
- package/app/components/audit/viewer/types.ts +1 -0
- package/app/components/audit/viewer/use-audit-viewer-data.ts +186 -0
- package/app/components/audit/viewer/use-audit-viewer-export.ts +176 -0
- package/app/components/audit/viewer/use-audit-viewer-filters.ts +141 -0
- package/app/components/auth/mfa-enrollment.module.css +13 -5
- package/app/components/auth/mfa-verification.module.css +13 -5
- package/app/components/canvas/box-annotations/box-annotations.module.css +22 -18
- package/app/components/canvas/box-annotations/box-annotations.tsx +15 -0
- package/app/components/canvas/canvas.module.css +64 -54
- package/app/components/canvas/canvas.tsx +17 -16
- package/app/components/canvas/confirmation/confirmation.module.css +1 -0
- package/app/components/canvas/confirmation/confirmation.tsx +17 -47
- package/app/components/navbar/case-modals/archive-case-modal.module.css +110 -0
- package/app/components/navbar/case-modals/archive-case-modal.tsx +129 -0
- package/app/components/navbar/case-modals/open-case-modal.module.css +81 -0
- package/app/components/navbar/case-modals/open-case-modal.tsx +120 -0
- package/app/components/navbar/case-modals/rename-case-modal.module.css +81 -0
- package/app/components/navbar/case-modals/rename-case-modal.tsx +107 -0
- package/app/components/navbar/navbar.module.css +447 -0
- package/app/components/navbar/navbar.tsx +377 -0
- package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +2 -0
- package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +21 -51
- package/app/components/sidebar/case-export/case-export.module.css +1 -0
- package/app/components/sidebar/case-export/case-export.tsx +14 -77
- package/app/components/sidebar/case-import/case-import.module.css +25 -0
- package/app/components/sidebar/case-import/case-import.tsx +64 -40
- package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +20 -1
- package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +15 -0
- package/app/components/sidebar/cases/case-sidebar.tsx +25 -519
- package/app/components/sidebar/cases/cases-modal.module.css +45 -9
- package/app/components/sidebar/cases/cases-modal.tsx +16 -16
- package/app/components/sidebar/cases/cases.module.css +62 -21
- package/app/components/sidebar/files/files-modal.module.css +46 -10
- package/app/components/sidebar/files/files-modal.tsx +22 -23
- package/app/components/sidebar/notes/notes-editor-modal.module.css +49 -0
- package/app/components/sidebar/notes/notes-editor-modal.tsx +66 -0
- package/app/components/sidebar/notes/notes-modal.tsx +18 -17
- package/app/components/sidebar/notes/notes-sidebar.tsx +199 -113
- package/app/components/sidebar/notes/notes.module.css +155 -0
- package/app/components/sidebar/sidebar-container.tsx +15 -28
- package/app/components/sidebar/sidebar.module.css +7 -71
- package/app/components/sidebar/sidebar.tsx +24 -125
- package/app/components/sidebar/upload/image-upload-zone.module.css +13 -13
- package/app/components/toast/toast.module.css +2 -1
- package/app/components/toast/toast.tsx +16 -11
- package/app/components/user/delete-account.tsx +10 -31
- package/app/components/user/inactivity-warning.module.css +9 -6
- package/app/components/user/inactivity-warning.tsx +15 -2
- package/app/components/user/manage-profile.module.css +2 -0
- package/app/components/user/manage-profile.tsx +108 -40
- package/app/hooks/useOverlayDismiss.ts +116 -0
- package/app/routes/auth/login.example.tsx +19 -8
- package/app/routes/auth/login.tsx +785 -774
- package/app/routes/auth/passwordReset.module.css +23 -13
- package/app/routes/striae/striae.module.css +10 -3
- package/app/routes/striae/striae.tsx +477 -31
- package/app/routes.ts +7 -0
- package/app/services/audit/audit-export-csv.ts +2 -0
- package/app/services/audit/audit.service.ts +202 -32
- package/app/services/audit/builders/audit-entry-builder.ts +2 -1
- package/app/services/audit/builders/audit-event-builders-case-file.ts +43 -0
- package/app/services/audit/builders/audit-event-builders-user-security.ts +4 -2
- package/app/services/audit/builders/audit-event-builders-workflow.ts +8 -0
- package/app/services/audit/builders/index.ts +1 -0
- package/app/types/audit.ts +5 -2
- package/app/types/case.ts +29 -0
- package/app/types/import.ts +3 -0
- package/app/types/user.ts +1 -0
- package/app/utils/data/permissions.ts +17 -1
- package/app/utils/forensics/audit-export-signature.ts +5 -1
- package/app/utils/forensics/confirmation-signature.ts +3 -0
- package/app/utils/forensics/export-verification.ts +497 -22
- package/functions/api/pdf/[[path]].ts +32 -1
- package/load-context.ts +9 -0
- package/package.json +6 -2
- package/primershear.emails.example +6 -0
- package/scripts/deploy-pages-secrets.sh +6 -0
- package/scripts/deploy-primershear-emails.sh +167 -0
- package/worker-configuration.d.ts +7493 -7491
- package/workers/audit-worker/worker-configuration.d.ts +7448 -11323
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/worker-configuration.d.ts +7448 -11323
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/worker-configuration.d.ts +7447 -11322
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/keys-worker/worker-configuration.d.ts +7447 -11322
- package/workers/keys-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/src/formats/format-striae.ts +8 -7
- package/workers/pdf-worker/src/pdf-worker.example.ts +3 -0
- package/workers/pdf-worker/src/report-types.ts +3 -0
- package/workers/pdf-worker/worker-configuration.d.ts +7448 -11323
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/src/user-worker.example.ts +6 -1
- package/workers/user-worker/worker-configuration.d.ts +7448 -11323
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
- package/public/.well-known/keybase.txt +0 -56
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
justify-content: center;
|
|
11
11
|
z-index: 1000;
|
|
12
12
|
padding: 1rem;
|
|
13
|
+
cursor: default;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
.modal {
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
max-height: 90vh;
|
|
22
23
|
overflow-y: auto;
|
|
23
24
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
25
|
+
cursor: default;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
.header {
|
|
@@ -149,7 +151,8 @@
|
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
.errorMessage {
|
|
152
|
-
background: linear-gradient(
|
|
154
|
+
background: linear-gradient(
|
|
155
|
+
135deg,
|
|
153
156
|
color-mix(in lab, var(--error) 12%, transparent),
|
|
154
157
|
color-mix(in lab, var(--error) 8%, transparent)
|
|
155
158
|
);
|
|
@@ -169,13 +172,17 @@
|
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
.errorMessage::before {
|
|
172
|
-
content:
|
|
175
|
+
content: "";
|
|
173
176
|
position: absolute;
|
|
174
177
|
top: 0;
|
|
175
178
|
left: 0;
|
|
176
179
|
right: 0;
|
|
177
180
|
height: 2px;
|
|
178
|
-
background: linear-gradient(
|
|
181
|
+
background: linear-gradient(
|
|
182
|
+
90deg,
|
|
183
|
+
var(--error),
|
|
184
|
+
color-mix(in lab, var(--error) 60%, transparent)
|
|
185
|
+
);
|
|
179
186
|
animation: shimmer 2s ease-in-out infinite;
|
|
180
187
|
}
|
|
181
188
|
|
|
@@ -246,7 +253,8 @@
|
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
@keyframes shimmer {
|
|
249
|
-
0%,
|
|
256
|
+
0%,
|
|
257
|
+
100% {
|
|
250
258
|
opacity: 0.6;
|
|
251
259
|
transform: translateX(-100%);
|
|
252
260
|
}
|
|
@@ -261,7 +269,7 @@
|
|
|
261
269
|
.errorMessage {
|
|
262
270
|
animation: none;
|
|
263
271
|
}
|
|
264
|
-
|
|
272
|
+
|
|
265
273
|
.errorMessage::before {
|
|
266
274
|
animation: none;
|
|
267
275
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
justify-content: center;
|
|
10
10
|
align-items: center;
|
|
11
11
|
z-index: 1000;
|
|
12
|
+
cursor: default;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
.modal {
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
width: 100%;
|
|
19
20
|
max-width: 400px;
|
|
20
21
|
box-shadow: 0 var(--spaceM) var(--spaceXL) rgba(0, 0, 0, 0.2);
|
|
22
|
+
cursor: default;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
.title {
|
|
@@ -63,7 +65,8 @@
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
.errorMessage {
|
|
66
|
-
background: linear-gradient(
|
|
68
|
+
background: linear-gradient(
|
|
69
|
+
135deg,
|
|
67
70
|
color-mix(in lab, var(--error) 12%, transparent),
|
|
68
71
|
color-mix(in lab, var(--error) 8%, transparent)
|
|
69
72
|
);
|
|
@@ -83,13 +86,17 @@
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
.errorMessage::before {
|
|
86
|
-
content:
|
|
89
|
+
content: "";
|
|
87
90
|
position: absolute;
|
|
88
91
|
top: 0;
|
|
89
92
|
left: 0;
|
|
90
93
|
right: 0;
|
|
91
94
|
height: 2px;
|
|
92
|
-
background: linear-gradient(
|
|
95
|
+
background: linear-gradient(
|
|
96
|
+
90deg,
|
|
97
|
+
var(--error),
|
|
98
|
+
color-mix(in lab, var(--error) 60%, transparent)
|
|
99
|
+
);
|
|
93
100
|
animation: shimmer 2s ease-in-out infinite;
|
|
94
101
|
}
|
|
95
102
|
|
|
@@ -229,7 +236,8 @@
|
|
|
229
236
|
}
|
|
230
237
|
|
|
231
238
|
@keyframes shimmer {
|
|
232
|
-
0%,
|
|
239
|
+
0%,
|
|
240
|
+
100% {
|
|
233
241
|
opacity: 0.6;
|
|
234
242
|
transform: translateX(-100%);
|
|
235
243
|
}
|
|
@@ -244,7 +252,7 @@
|
|
|
244
252
|
.errorMessage {
|
|
245
253
|
animation: none;
|
|
246
254
|
}
|
|
247
|
-
|
|
255
|
+
|
|
248
256
|
.errorMessage::before {
|
|
249
257
|
animation: none;
|
|
250
258
|
}
|
|
@@ -59,16 +59,17 @@
|
|
|
59
59
|
|
|
60
60
|
.annotationLabel {
|
|
61
61
|
position: absolute;
|
|
62
|
-
bottom: -
|
|
62
|
+
bottom: -30px;
|
|
63
63
|
left: 0;
|
|
64
64
|
background: rgba(0, 0, 0, 0.8);
|
|
65
65
|
color: white;
|
|
66
|
-
padding:
|
|
67
|
-
font-size:
|
|
68
|
-
|
|
66
|
+
padding: 4px 10px;
|
|
67
|
+
font-size: 13px;
|
|
68
|
+
line-height: 1.3;
|
|
69
|
+
border-radius: 4px;
|
|
69
70
|
white-space: nowrap;
|
|
70
71
|
pointer-events: none;
|
|
71
|
-
max-width:
|
|
72
|
+
max-width: 260px;
|
|
72
73
|
overflow: hidden;
|
|
73
74
|
text-overflow: ellipsis;
|
|
74
75
|
}
|
|
@@ -82,33 +83,33 @@
|
|
|
82
83
|
background: white;
|
|
83
84
|
border: 1px solid #ccc;
|
|
84
85
|
border-radius: 6px;
|
|
85
|
-
padding:
|
|
86
|
+
padding: 16px;
|
|
86
87
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
87
|
-
min-width:
|
|
88
|
+
min-width: 250px;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
.labelDialogTitle {
|
|
91
|
-
font-size:
|
|
92
|
+
font-size: 15px;
|
|
92
93
|
font-weight: 600;
|
|
93
|
-
margin-bottom:
|
|
94
|
+
margin-bottom: 8px;
|
|
94
95
|
color: #333;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
.labelDialogNote {
|
|
98
|
-
font-size:
|
|
99
|
+
font-size: 13px;
|
|
99
100
|
color: #666;
|
|
100
|
-
margin-bottom:
|
|
101
|
+
margin-bottom: 12px;
|
|
101
102
|
font-style: italic;
|
|
102
|
-
line-height: 1.
|
|
103
|
+
line-height: 1.4;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
.labelInput {
|
|
106
107
|
width: 100%;
|
|
107
|
-
padding:
|
|
108
|
+
padding: 8px 10px;
|
|
108
109
|
border: 1px solid #ccc;
|
|
109
110
|
border-radius: 4px;
|
|
110
111
|
font-size: 14px;
|
|
111
|
-
margin-bottom:
|
|
112
|
+
margin-bottom: 12px;
|
|
112
113
|
box-sizing: border-box;
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -120,18 +121,21 @@
|
|
|
120
121
|
|
|
121
122
|
.labelDialogButtons {
|
|
122
123
|
display: flex;
|
|
123
|
-
gap:
|
|
124
|
+
gap: 10px;
|
|
124
125
|
justify-content: flex-end;
|
|
126
|
+
margin-top: 8px;
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
.labelConfirmButton,
|
|
128
130
|
.labelCancelButton {
|
|
129
|
-
padding:
|
|
131
|
+
padding: 10px 18px;
|
|
130
132
|
border: none;
|
|
131
133
|
border-radius: 4px;
|
|
132
|
-
font-size:
|
|
134
|
+
font-size: 14px;
|
|
135
|
+
font-weight: 500;
|
|
133
136
|
cursor: pointer;
|
|
134
137
|
transition: background-color 0.2s ease;
|
|
138
|
+
min-width: 88px;
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
.labelConfirmButton {
|
|
@@ -167,4 +171,4 @@
|
|
|
167
171
|
|
|
168
172
|
.readOnlyAnnotation:hover::after {
|
|
169
173
|
display: none !important; /* Hide the delete button for read-only annotations */
|
|
170
|
-
}
|
|
174
|
+
}
|
|
@@ -89,6 +89,7 @@ export const BoxAnnotations = ({
|
|
|
89
89
|
|
|
90
90
|
// Ref to track if component is mounted to prevent state updates after unmount
|
|
91
91
|
const isMountedRef = useRef(true);
|
|
92
|
+
const labelInputRef = useRef<HTMLInputElement>(null);
|
|
92
93
|
|
|
93
94
|
useEffect(() => {
|
|
94
95
|
return () => {
|
|
@@ -96,6 +97,19 @@ export const BoxAnnotations = ({
|
|
|
96
97
|
};
|
|
97
98
|
}, []);
|
|
98
99
|
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (!labelDialog.isVisible) return;
|
|
102
|
+
|
|
103
|
+
const focusFrame = window.requestAnimationFrame(() => {
|
|
104
|
+
labelInputRef.current?.focus();
|
|
105
|
+
labelInputRef.current?.select();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return () => {
|
|
109
|
+
window.cancelAnimationFrame(focusFrame);
|
|
110
|
+
};
|
|
111
|
+
}, [labelDialog.isVisible]);
|
|
112
|
+
|
|
99
113
|
// Memoized function to get relative coordinates (more stable reference)
|
|
100
114
|
const getRelativeCoordinates = useCallback((e: React.MouseEvent): { x: number; y: number } => {
|
|
101
115
|
const imageElement = imageRef.current;
|
|
@@ -602,6 +616,7 @@ export const BoxAnnotations = ({
|
|
|
602
616
|
}
|
|
603
617
|
</div>
|
|
604
618
|
<input
|
|
619
|
+
ref={labelInputRef}
|
|
605
620
|
type="text"
|
|
606
621
|
value={labelDialog.label}
|
|
607
622
|
onChange={handleLabelChange}
|
|
@@ -12,12 +12,21 @@
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
.imageContainer {
|
|
15
|
-
|
|
15
|
+
flex: 1;
|
|
16
|
+
min-width: 0;
|
|
16
17
|
display: flex;
|
|
17
18
|
justify-content: center;
|
|
18
19
|
align-items: center;
|
|
20
|
+
overflow: visible;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Tight wrapper sized to the rendered image — all overlays position relative to this */
|
|
24
|
+
.imageWrapper {
|
|
25
|
+
position: relative;
|
|
26
|
+
display: inline-block;
|
|
27
|
+
line-height: 0;
|
|
19
28
|
max-width: 100%;
|
|
20
|
-
max-height:
|
|
29
|
+
max-height: 80vh;
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
.toolbarWrapper {
|
|
@@ -34,7 +43,7 @@
|
|
|
34
43
|
top: 1rem;
|
|
35
44
|
z-index: 15;
|
|
36
45
|
color: #e0e0e0;
|
|
37
|
-
font-family:
|
|
46
|
+
font-family: "Inter", sans-serif;
|
|
38
47
|
font-size: 1rem;
|
|
39
48
|
font-weight: 500;
|
|
40
49
|
pointer-events: none;
|
|
@@ -47,7 +56,7 @@
|
|
|
47
56
|
|
|
48
57
|
.confirmationIncluded {
|
|
49
58
|
font-size: 0.8rem;
|
|
50
|
-
color: #
|
|
59
|
+
color: #ffde21;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
.confirmationConfirmed {
|
|
@@ -103,31 +112,15 @@
|
|
|
103
112
|
box-shadow: 0 2px 6px rgba(0, 123, 255, 0.4);
|
|
104
113
|
}
|
|
105
114
|
|
|
106
|
-
/* Company Display */
|
|
107
|
-
.companyDisplay {
|
|
108
|
-
position: absolute;
|
|
109
|
-
right: 2rem;
|
|
110
|
-
top: 1rem;
|
|
111
|
-
z-index: 15;
|
|
112
|
-
color: #e0e0e0;
|
|
113
|
-
font-family: 'Inter', sans-serif;
|
|
114
|
-
font-size: 1.5rem;
|
|
115
|
-
font-weight: 500;
|
|
116
|
-
pointer-events: none;
|
|
117
|
-
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
|
118
|
-
white-space: nowrap;
|
|
119
|
-
overflow: hidden;
|
|
120
|
-
text-overflow: ellipsis;
|
|
121
|
-
max-width: calc(100vw - 16rem);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
115
|
.image {
|
|
116
|
+
display: block;
|
|
125
117
|
max-width: 100%;
|
|
126
118
|
max-height: 80vh;
|
|
127
119
|
object-fit: contain;
|
|
128
120
|
}
|
|
129
121
|
|
|
130
|
-
.placeholder,
|
|
122
|
+
.placeholder,
|
|
123
|
+
.loading {
|
|
131
124
|
color: #e0e0e0;
|
|
132
125
|
font-size: 1.1rem;
|
|
133
126
|
text-align: center;
|
|
@@ -155,7 +148,7 @@
|
|
|
155
148
|
.leftAnnotation,
|
|
156
149
|
.rightAnnotation {
|
|
157
150
|
position: absolute;
|
|
158
|
-
padding:
|
|
151
|
+
padding: 1rem 1.4rem;
|
|
159
152
|
background: rgba(0, 0, 0, 0.7);
|
|
160
153
|
border-radius: 6px;
|
|
161
154
|
backdrop-filter: blur(4px);
|
|
@@ -177,7 +170,7 @@
|
|
|
177
170
|
position: absolute;
|
|
178
171
|
bottom: 1rem;
|
|
179
172
|
left: 1rem;
|
|
180
|
-
padding:
|
|
173
|
+
padding: 1rem 1.4rem;
|
|
181
174
|
background: rgba(0, 0, 0, 0.7);
|
|
182
175
|
border-radius: 6px;
|
|
183
176
|
backdrop-filter: blur(4px);
|
|
@@ -186,7 +179,7 @@
|
|
|
186
179
|
}
|
|
187
180
|
|
|
188
181
|
.caseText {
|
|
189
|
-
font-family:
|
|
182
|
+
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
|
|
190
183
|
font-size: 1.1rem;
|
|
191
184
|
font-weight: 700;
|
|
192
185
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
|
@@ -197,7 +190,7 @@
|
|
|
197
190
|
/* Class Characteristics Display */
|
|
198
191
|
.classCharacteristics {
|
|
199
192
|
position: absolute;
|
|
200
|
-
|
|
193
|
+
bottom: calc(100% + 0.5rem);
|
|
201
194
|
left: 50%;
|
|
202
195
|
transform: translateX(-50%);
|
|
203
196
|
z-index: 15;
|
|
@@ -205,14 +198,14 @@
|
|
|
205
198
|
}
|
|
206
199
|
|
|
207
200
|
.classText {
|
|
208
|
-
padding:
|
|
201
|
+
padding: 1rem 2rem;
|
|
209
202
|
background: rgba(0, 0, 0, 0.8);
|
|
210
203
|
color: #ffffff;
|
|
211
204
|
border-radius: 8px;
|
|
212
205
|
backdrop-filter: blur(6px);
|
|
213
206
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
214
207
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
215
|
-
font-family:
|
|
208
|
+
font-family: "Inter", sans-serif;
|
|
216
209
|
font-size: 1.1rem;
|
|
217
210
|
font-weight: 600;
|
|
218
211
|
text-align: center;
|
|
@@ -237,7 +230,7 @@
|
|
|
237
230
|
backdrop-filter: blur(6px);
|
|
238
231
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
239
232
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
240
|
-
font-family:
|
|
233
|
+
font-family: "Inter", sans-serif;
|
|
241
234
|
font-size: 1.1rem;
|
|
242
235
|
font-weight: 700;
|
|
243
236
|
text-align: center;
|
|
@@ -249,8 +242,8 @@
|
|
|
249
242
|
/* Subclass Warning Display */
|
|
250
243
|
.subclassWarning {
|
|
251
244
|
position: absolute;
|
|
252
|
-
bottom: 1rem;
|
|
253
|
-
right: 2rem;
|
|
245
|
+
bottom: 1rem;
|
|
246
|
+
right: 2rem;
|
|
254
247
|
z-index: 20;
|
|
255
248
|
pointer-events: none;
|
|
256
249
|
}
|
|
@@ -263,7 +256,7 @@
|
|
|
263
256
|
backdrop-filter: blur(6px);
|
|
264
257
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
265
258
|
box-shadow: 0 4px 16px rgba(220, 53, 69, 0.4);
|
|
266
|
-
font-family:
|
|
259
|
+
font-family: "Inter", sans-serif;
|
|
267
260
|
font-size: 1rem;
|
|
268
261
|
font-weight: 700;
|
|
269
262
|
text-align: center;
|
|
@@ -282,33 +275,50 @@
|
|
|
282
275
|
/* Image and Notes Container */
|
|
283
276
|
.imageAndNotesContainer {
|
|
284
277
|
display: flex;
|
|
285
|
-
flex-direction:
|
|
286
|
-
align-items:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
max-height: 100%;
|
|
278
|
+
flex-direction: row;
|
|
279
|
+
align-items: stretch;
|
|
280
|
+
align-self: stretch;
|
|
281
|
+
width: 100%;
|
|
290
282
|
}
|
|
291
283
|
|
|
292
|
-
/*
|
|
293
|
-
.
|
|
284
|
+
/* Notes Panel - fixed-width right-side column */
|
|
285
|
+
.notesPanel {
|
|
286
|
+
width: 260px;
|
|
287
|
+
flex-shrink: 0;
|
|
288
|
+
/* Opt out of stretch so the explicit height takes effect, stopping the panel
|
|
289
|
+
before the Subclass badge (bottom:1rem, ~2.5rem tall = 3.5rem from canvas bottom).
|
|
290
|
+
calc(100% - 4rem) lands ~4rem from the canvas bottom, clearing the badge. */
|
|
291
|
+
align-self: flex-start;
|
|
292
|
+
height: calc(100% - 4rem);
|
|
294
293
|
display: flex;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
294
|
+
flex-direction: column;
|
|
295
|
+
background: rgba(20, 20, 20, 0.55);
|
|
296
|
+
border-left: 1px solid rgba(255, 255, 255, 0.1);
|
|
297
|
+
overflow: hidden;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.notesPanelHeader {
|
|
301
|
+
padding: 0.625rem 1rem;
|
|
302
|
+
color: #adb5bd;
|
|
303
|
+
font-size: 0.75rem;
|
|
304
|
+
font-weight: 600;
|
|
305
|
+
text-transform: uppercase;
|
|
306
|
+
letter-spacing: 0.5px;
|
|
307
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
308
|
+
flex-shrink: 0;
|
|
309
|
+
font-family: "Inter", sans-serif;
|
|
298
310
|
}
|
|
299
311
|
|
|
300
312
|
.additionalNotesBox {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
font-
|
|
308
|
-
|
|
309
|
-
line-height: 1.4;
|
|
313
|
+
flex: 1;
|
|
314
|
+
overflow-y: auto;
|
|
315
|
+
background: transparent;
|
|
316
|
+
color: #e0e0e0;
|
|
317
|
+
padding: 1rem;
|
|
318
|
+
font-family: "Inter", sans-serif;
|
|
319
|
+
font-size: 0.875rem;
|
|
320
|
+
line-height: 1.5;
|
|
310
321
|
text-align: left;
|
|
311
322
|
white-space: pre-wrap;
|
|
312
323
|
word-wrap: break-word;
|
|
313
|
-
|
|
314
|
-
}
|
|
324
|
+
}
|
|
@@ -10,6 +10,7 @@ interface CanvasProps {
|
|
|
10
10
|
imageUrl?: string;
|
|
11
11
|
filename?: string;
|
|
12
12
|
company?: string;
|
|
13
|
+
badgeId?: string;
|
|
13
14
|
firstName?: string;
|
|
14
15
|
error?: string;
|
|
15
16
|
activeAnnotations?: Set<string>;
|
|
@@ -18,6 +19,7 @@ interface CanvasProps {
|
|
|
18
19
|
isBoxAnnotationMode?: boolean;
|
|
19
20
|
boxAnnotationColor?: string;
|
|
20
21
|
isReadOnly?: boolean;
|
|
22
|
+
isArchivedCase?: boolean;
|
|
21
23
|
// Confirmation data for storing case-level confirmations
|
|
22
24
|
caseNumber: string; // Required for audit logging
|
|
23
25
|
currentImageId?: string;
|
|
@@ -31,7 +33,8 @@ type ImageLoadError = {
|
|
|
31
33
|
export const Canvas = ({
|
|
32
34
|
imageUrl,
|
|
33
35
|
filename,
|
|
34
|
-
company,
|
|
36
|
+
company,
|
|
37
|
+
badgeId,
|
|
35
38
|
firstName,
|
|
36
39
|
error,
|
|
37
40
|
activeAnnotations,
|
|
@@ -40,6 +43,7 @@ export const Canvas = ({
|
|
|
40
43
|
isBoxAnnotationMode = false,
|
|
41
44
|
boxAnnotationColor = '#FF0000',
|
|
42
45
|
isReadOnly = false,
|
|
46
|
+
isArchivedCase = false,
|
|
43
47
|
caseNumber,
|
|
44
48
|
currentImageId
|
|
45
49
|
}: CanvasProps) => {
|
|
@@ -274,7 +278,7 @@ export const Canvas = ({
|
|
|
274
278
|
<div className={styles.confirmationIncluded}>
|
|
275
279
|
{isReadOnly ? 'Confirmation Requested' : 'Confirmation Field Included'}
|
|
276
280
|
</div>
|
|
277
|
-
{isReadOnly && (
|
|
281
|
+
{isReadOnly && !isArchivedCase && (
|
|
278
282
|
<button
|
|
279
283
|
className={styles.confirmButton}
|
|
280
284
|
onClick={() => setIsConfirmationModalOpen(true)}
|
|
@@ -289,13 +293,6 @@ export const Canvas = ({
|
|
|
289
293
|
</div>
|
|
290
294
|
)}
|
|
291
295
|
|
|
292
|
-
{/* Company Display - Upper Right */}
|
|
293
|
-
{company && (
|
|
294
|
-
<div className={styles.companyDisplay}>
|
|
295
|
-
{isReadOnly ? 'CASE REVIEW ONLY' : company}
|
|
296
|
-
</div>
|
|
297
|
-
)}
|
|
298
|
-
|
|
299
296
|
{(loadError || error) ? (
|
|
300
297
|
<p className={styles.error}>{getErrorMessage()}</p>
|
|
301
298
|
) : isLoading ? (
|
|
@@ -303,6 +300,7 @@ export const Canvas = ({
|
|
|
303
300
|
) : imageUrl && imageUrl !== '/clear.jpg' ? (
|
|
304
301
|
<div className={styles.imageAndNotesContainer}>
|
|
305
302
|
<div className={styles.imageContainer}>
|
|
303
|
+
<div className={styles.imageWrapper}>
|
|
306
304
|
{/* Class Characteristics - Above Image */}
|
|
307
305
|
{activeAnnotations?.has('class') && annotationData && (annotationData.customClass || annotationData.classType) && (
|
|
308
306
|
<div className={styles.classCharacteristics}>
|
|
@@ -331,7 +329,7 @@ export const Canvas = ({
|
|
|
331
329
|
draggable={false}
|
|
332
330
|
/>
|
|
333
331
|
|
|
334
|
-
{/* Box Annotations Component -
|
|
332
|
+
{/* Box Annotations Component - contained within imageWrapper */}
|
|
335
333
|
{activeAnnotations?.has('box') && (
|
|
336
334
|
<BoxAnnotations
|
|
337
335
|
imageRef={imageRef}
|
|
@@ -348,7 +346,7 @@ export const Canvas = ({
|
|
|
348
346
|
/>
|
|
349
347
|
)}
|
|
350
348
|
|
|
351
|
-
{/* Annotations Overlay */}
|
|
349
|
+
{/* Annotations Overlay - contained within imageWrapper */}
|
|
352
350
|
{activeAnnotations?.has('number') && annotationData && (
|
|
353
351
|
<div className={styles.annotationsOverlay}>
|
|
354
352
|
{/* Left side case and item numbers */}
|
|
@@ -383,7 +381,7 @@ export const Canvas = ({
|
|
|
383
381
|
</div>
|
|
384
382
|
)}
|
|
385
383
|
|
|
386
|
-
{/* Index Number Overlay */}
|
|
384
|
+
{/* Index Number Overlay - contained within imageWrapper */}
|
|
387
385
|
{activeAnnotations?.has('index') && annotationData?.indexType === 'number' && annotationData?.indexNumber && (
|
|
388
386
|
<div className={styles.annotationsOverlay}>
|
|
389
387
|
<div
|
|
@@ -400,15 +398,17 @@ export const Canvas = ({
|
|
|
400
398
|
</div>
|
|
401
399
|
</div>
|
|
402
400
|
)}
|
|
403
|
-
|
|
401
|
+
</div>{/* end imageWrapper */}
|
|
402
|
+
</div>{/* end imageContainer */}
|
|
404
403
|
|
|
405
|
-
{/* Additional Notes -
|
|
404
|
+
{/* Additional Notes - Right Panel */}
|
|
406
405
|
{activeAnnotations?.has('notes') && annotationData?.additionalNotes && (
|
|
407
|
-
<
|
|
406
|
+
<aside className={styles.notesPanel} aria-label="Additional notes">
|
|
407
|
+
<div className={styles.notesPanelHeader}>Notes</div>
|
|
408
408
|
<div className={styles.additionalNotesBox}>
|
|
409
409
|
{annotationData.additionalNotes}
|
|
410
410
|
</div>
|
|
411
|
-
</
|
|
411
|
+
</aside>
|
|
412
412
|
)}
|
|
413
413
|
</div>
|
|
414
414
|
) : (
|
|
@@ -453,6 +453,7 @@ export const Canvas = ({
|
|
|
453
453
|
onClose={() => setIsConfirmationModalOpen(false)}
|
|
454
454
|
onConfirm={handleConfirmation}
|
|
455
455
|
company={company}
|
|
456
|
+
defaultBadgeId={badgeId}
|
|
456
457
|
existingConfirmation={annotationData?.confirmationData || null}
|
|
457
458
|
/>
|
|
458
459
|
</div>
|