@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.
Files changed (118) hide show
  1. package/.env.example +8 -0
  2. package/app/components/actions/case-export/core-export.ts +14 -8
  3. package/app/components/actions/case-export/data-processing.ts +1 -0
  4. package/app/components/actions/case-export/download-handlers.ts +7 -0
  5. package/app/components/actions/case-export/metadata-helpers.ts +2 -1
  6. package/app/components/actions/case-import/confirmation-import.ts +12 -2
  7. package/app/components/actions/case-import/orchestrator.ts +78 -32
  8. package/app/components/actions/case-import/storage-operations.ts +97 -8
  9. package/app/components/actions/case-import/zip-processing.ts +159 -86
  10. package/app/components/actions/case-manage.ts +430 -8
  11. package/app/components/actions/confirm-export.ts +13 -4
  12. package/app/components/actions/generate-pdf.ts +10 -2
  13. package/app/components/actions/image-manage.ts +77 -44
  14. package/app/components/audit/user-audit-viewer.tsx +137 -945
  15. package/app/components/audit/user-audit.module.css +41 -0
  16. package/app/components/audit/viewer/audit-activity-summary.tsx +52 -0
  17. package/app/components/audit/viewer/audit-entries-list.tsx +207 -0
  18. package/app/components/audit/viewer/audit-filters-panel.tsx +307 -0
  19. package/app/components/audit/viewer/audit-user-info-card.tsx +44 -0
  20. package/app/components/audit/viewer/audit-viewer-header.tsx +55 -0
  21. package/app/components/audit/viewer/audit-viewer-utils.ts +123 -0
  22. package/app/components/audit/viewer/types.ts +1 -0
  23. package/app/components/audit/viewer/use-audit-viewer-data.ts +186 -0
  24. package/app/components/audit/viewer/use-audit-viewer-export.ts +176 -0
  25. package/app/components/audit/viewer/use-audit-viewer-filters.ts +141 -0
  26. package/app/components/auth/mfa-enrollment.module.css +13 -5
  27. package/app/components/auth/mfa-verification.module.css +13 -5
  28. package/app/components/canvas/box-annotations/box-annotations.module.css +22 -18
  29. package/app/components/canvas/box-annotations/box-annotations.tsx +15 -0
  30. package/app/components/canvas/canvas.module.css +64 -54
  31. package/app/components/canvas/canvas.tsx +17 -16
  32. package/app/components/canvas/confirmation/confirmation.module.css +1 -0
  33. package/app/components/canvas/confirmation/confirmation.tsx +17 -47
  34. package/app/components/navbar/case-modals/archive-case-modal.module.css +110 -0
  35. package/app/components/navbar/case-modals/archive-case-modal.tsx +129 -0
  36. package/app/components/navbar/case-modals/open-case-modal.module.css +81 -0
  37. package/app/components/navbar/case-modals/open-case-modal.tsx +120 -0
  38. package/app/components/navbar/case-modals/rename-case-modal.module.css +81 -0
  39. package/app/components/navbar/case-modals/rename-case-modal.tsx +107 -0
  40. package/app/components/navbar/navbar.module.css +447 -0
  41. package/app/components/navbar/navbar.tsx +377 -0
  42. package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +2 -0
  43. package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +21 -51
  44. package/app/components/sidebar/case-export/case-export.module.css +1 -0
  45. package/app/components/sidebar/case-export/case-export.tsx +14 -77
  46. package/app/components/sidebar/case-import/case-import.module.css +25 -0
  47. package/app/components/sidebar/case-import/case-import.tsx +64 -40
  48. package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +20 -1
  49. package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +15 -0
  50. package/app/components/sidebar/cases/case-sidebar.tsx +25 -519
  51. package/app/components/sidebar/cases/cases-modal.module.css +45 -9
  52. package/app/components/sidebar/cases/cases-modal.tsx +16 -16
  53. package/app/components/sidebar/cases/cases.module.css +62 -21
  54. package/app/components/sidebar/files/files-modal.module.css +46 -10
  55. package/app/components/sidebar/files/files-modal.tsx +22 -23
  56. package/app/components/sidebar/notes/notes-editor-modal.module.css +49 -0
  57. package/app/components/sidebar/notes/notes-editor-modal.tsx +66 -0
  58. package/app/components/sidebar/notes/notes-modal.tsx +18 -17
  59. package/app/components/sidebar/notes/notes-sidebar.tsx +199 -113
  60. package/app/components/sidebar/notes/notes.module.css +155 -0
  61. package/app/components/sidebar/sidebar-container.tsx +15 -28
  62. package/app/components/sidebar/sidebar.module.css +7 -71
  63. package/app/components/sidebar/sidebar.tsx +24 -125
  64. package/app/components/sidebar/upload/image-upload-zone.module.css +13 -13
  65. package/app/components/toast/toast.module.css +2 -1
  66. package/app/components/toast/toast.tsx +16 -11
  67. package/app/components/user/delete-account.tsx +10 -31
  68. package/app/components/user/inactivity-warning.module.css +9 -6
  69. package/app/components/user/inactivity-warning.tsx +15 -2
  70. package/app/components/user/manage-profile.module.css +2 -0
  71. package/app/components/user/manage-profile.tsx +108 -40
  72. package/app/hooks/useOverlayDismiss.ts +116 -0
  73. package/app/routes/auth/login.example.tsx +19 -8
  74. package/app/routes/auth/login.tsx +785 -774
  75. package/app/routes/auth/passwordReset.module.css +23 -13
  76. package/app/routes/striae/striae.module.css +10 -3
  77. package/app/routes/striae/striae.tsx +477 -31
  78. package/app/routes.ts +7 -0
  79. package/app/services/audit/audit-export-csv.ts +2 -0
  80. package/app/services/audit/audit.service.ts +202 -32
  81. package/app/services/audit/builders/audit-entry-builder.ts +2 -1
  82. package/app/services/audit/builders/audit-event-builders-case-file.ts +43 -0
  83. package/app/services/audit/builders/audit-event-builders-user-security.ts +4 -2
  84. package/app/services/audit/builders/audit-event-builders-workflow.ts +8 -0
  85. package/app/services/audit/builders/index.ts +1 -0
  86. package/app/types/audit.ts +5 -2
  87. package/app/types/case.ts +29 -0
  88. package/app/types/import.ts +3 -0
  89. package/app/types/user.ts +1 -0
  90. package/app/utils/data/permissions.ts +17 -1
  91. package/app/utils/forensics/audit-export-signature.ts +5 -1
  92. package/app/utils/forensics/confirmation-signature.ts +3 -0
  93. package/app/utils/forensics/export-verification.ts +497 -22
  94. package/functions/api/pdf/[[path]].ts +32 -1
  95. package/load-context.ts +9 -0
  96. package/package.json +6 -2
  97. package/primershear.emails.example +6 -0
  98. package/scripts/deploy-pages-secrets.sh +6 -0
  99. package/scripts/deploy-primershear-emails.sh +167 -0
  100. package/worker-configuration.d.ts +7493 -7491
  101. package/workers/audit-worker/worker-configuration.d.ts +7448 -11323
  102. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  103. package/workers/data-worker/worker-configuration.d.ts +7448 -11323
  104. package/workers/data-worker/wrangler.jsonc.example +1 -1
  105. package/workers/image-worker/worker-configuration.d.ts +7447 -11322
  106. package/workers/image-worker/wrangler.jsonc.example +1 -1
  107. package/workers/keys-worker/worker-configuration.d.ts +7447 -11322
  108. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  109. package/workers/pdf-worker/src/formats/format-striae.ts +8 -7
  110. package/workers/pdf-worker/src/pdf-worker.example.ts +3 -0
  111. package/workers/pdf-worker/src/report-types.ts +3 -0
  112. package/workers/pdf-worker/worker-configuration.d.ts +7448 -11323
  113. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  114. package/workers/user-worker/src/user-worker.example.ts +6 -1
  115. package/workers/user-worker/worker-configuration.d.ts +7448 -11323
  116. package/workers/user-worker/wrangler.jsonc.example +1 -1
  117. package/wrangler.toml.example +1 -1
  118. 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(135deg,
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(90deg, var(--error), color-mix(in lab, var(--error) 60%, transparent));
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%, 100% {
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(135deg,
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(90deg, var(--error), color-mix(in lab, var(--error) 60%, transparent));
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%, 100% {
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: -25px;
62
+ bottom: -30px;
63
63
  left: 0;
64
64
  background: rgba(0, 0, 0, 0.8);
65
65
  color: white;
66
- padding: 2px 6px;
67
- font-size: 12px;
68
- border-radius: 3px;
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: 200px;
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: 12px;
86
+ padding: 16px;
86
87
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
87
- min-width: 200px;
88
+ min-width: 250px;
88
89
  }
89
90
 
90
91
  .labelDialogTitle {
91
- font-size: 14px;
92
+ font-size: 15px;
92
93
  font-weight: 600;
93
- margin-bottom: 6px;
94
+ margin-bottom: 8px;
94
95
  color: #333;
95
96
  }
96
97
 
97
98
  .labelDialogNote {
98
- font-size: 12px;
99
+ font-size: 13px;
99
100
  color: #666;
100
- margin-bottom: 10px;
101
+ margin-bottom: 12px;
101
102
  font-style: italic;
102
- line-height: 1.3;
103
+ line-height: 1.4;
103
104
  }
104
105
 
105
106
  .labelInput {
106
107
  width: 100%;
107
- padding: 6px 8px;
108
+ padding: 8px 10px;
108
109
  border: 1px solid #ccc;
109
110
  border-radius: 4px;
110
111
  font-size: 14px;
111
- margin-bottom: 10px;
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: 8px;
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: 6px 12px;
131
+ padding: 10px 18px;
130
132
  border: none;
131
133
  border-radius: 4px;
132
- font-size: 12px;
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
- position: relative;
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: 100%;
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: 'Inter', sans-serif;
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: #FFDE21;
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, .loading {
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: 0.75rem 1rem;
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: 0.75rem 1rem;
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: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
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
- top: -3rem;
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: 0.75rem 1.5rem;
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: 'Inter', sans-serif;
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: 'Inter', sans-serif;
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: 'Inter', sans-serif;
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: column;
286
- align-items: center;
287
- gap: 1rem;
288
- max-width: 100%;
289
- max-height: 100%;
278
+ flex-direction: row;
279
+ align-items: stretch;
280
+ align-self: stretch;
281
+ width: 100%;
290
282
  }
291
283
 
292
- /* Additional Notes Display */
293
- .additionalNotesContainer {
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
- justify-content: center;
296
- width: 100%;
297
- max-width: 600px;
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
- background: #ffffff;
302
- color: #000000;
303
- padding: 1rem 1.5rem;
304
- border-radius: 8px;
305
- border: 2px solid #d0d0d0;
306
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
307
- font-family: 'Inter', sans-serif;
308
- font-size: 0.95rem;
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
- max-width: 100%;
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 - Show when box tool is active for visibility */}
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
- </div>
401
+ </div>{/* end imageWrapper */}
402
+ </div>{/* end imageContainer */}
404
403
 
405
- {/* Additional Notes - Below Image */}
404
+ {/* Additional Notes - Right Panel */}
406
405
  {activeAnnotations?.has('notes') && annotationData?.additionalNotes && (
407
- <div className={styles.additionalNotesContainer}>
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
- </div>
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>
@@ -11,6 +11,7 @@
11
11
  }
12
12
 
13
13
  .modal {
14
+ position: relative;
14
15
  background: var(--backgroundLight);
15
16
  border-radius: var(--spaceXS);
16
17
  width: 90%;