@striae-org/striae 4.0.2 → 4.1.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 (69) hide show
  1. package/app/components/actions/confirm-export.ts +4 -2
  2. package/app/components/actions/generate-pdf.ts +10 -2
  3. package/app/components/audit/user-audit-viewer.tsx +121 -940
  4. package/app/components/audit/user-audit.module.css +20 -0
  5. package/app/components/audit/viewer/audit-activity-summary.tsx +52 -0
  6. package/app/components/audit/viewer/audit-entries-list.tsx +200 -0
  7. package/app/components/audit/viewer/audit-filters-panel.tsx +306 -0
  8. package/app/components/audit/viewer/audit-user-info-card.tsx +44 -0
  9. package/app/components/audit/viewer/audit-viewer-header.tsx +55 -0
  10. package/app/components/audit/viewer/audit-viewer-utils.ts +121 -0
  11. package/app/components/audit/viewer/types.ts +1 -0
  12. package/app/components/audit/viewer/use-audit-viewer-data.ts +166 -0
  13. package/app/components/audit/viewer/use-audit-viewer-export.ts +176 -0
  14. package/app/components/audit/viewer/use-audit-viewer-filters.ts +141 -0
  15. package/app/components/auth/mfa-enrollment.module.css +13 -5
  16. package/app/components/auth/mfa-verification.module.css +13 -5
  17. package/app/components/canvas/canvas.tsx +3 -0
  18. package/app/components/canvas/confirmation/confirmation.tsx +13 -37
  19. package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +1 -0
  20. package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +8 -37
  21. package/app/components/sidebar/case-export/case-export.tsx +9 -34
  22. package/app/components/sidebar/case-import/case-import.module.css +2 -0
  23. package/app/components/sidebar/case-import/case-import.tsx +10 -34
  24. package/app/components/sidebar/cases/cases-modal.module.css +44 -9
  25. package/app/components/sidebar/cases/cases-modal.tsx +16 -14
  26. package/app/components/sidebar/files/files-modal.module.css +45 -10
  27. package/app/components/sidebar/files/files-modal.tsx +16 -16
  28. package/app/components/sidebar/notes/notes-modal.tsx +17 -15
  29. package/app/components/sidebar/notes/notes.module.css +2 -0
  30. package/app/components/sidebar/sidebar.module.css +2 -2
  31. package/app/components/toast/toast.module.css +2 -1
  32. package/app/components/toast/toast.tsx +16 -11
  33. package/app/components/user/delete-account.tsx +10 -31
  34. package/app/components/user/inactivity-warning.module.css +8 -6
  35. package/app/components/user/manage-profile.module.css +2 -0
  36. package/app/components/user/manage-profile.tsx +85 -30
  37. package/app/hooks/useOverlayDismiss.ts +68 -0
  38. package/app/routes/auth/login.example.tsx +786 -0
  39. package/app/routes/auth/login.module.example.css +523 -0
  40. package/app/routes/auth/login.tsx +1 -1
  41. package/app/routes/auth/passwordReset.module.css +23 -13
  42. package/app/routes/striae/striae.tsx +8 -1
  43. package/app/routes.ts +7 -0
  44. package/app/services/audit/audit-export-csv.ts +2 -0
  45. package/app/services/audit/audit.service.ts +29 -5
  46. package/app/services/audit/builders/audit-entry-builder.ts +2 -1
  47. package/app/services/audit/builders/audit-event-builders-user-security.ts +4 -2
  48. package/app/services/audit/builders/audit-event-builders-workflow.ts +6 -0
  49. package/app/types/audit.ts +2 -1
  50. package/app/types/user.ts +1 -0
  51. package/app/utils/data/permissions.ts +1 -0
  52. package/functions/api/pdf/[[path]].ts +32 -1
  53. package/load-context.ts +9 -0
  54. package/package.json +5 -1
  55. package/primershear.emails.example +6 -0
  56. package/scripts/deploy-config.sh +27 -0
  57. package/scripts/deploy-pages-secrets.sh +6 -0
  58. package/scripts/deploy-primershear-emails.sh +166 -0
  59. package/worker-configuration.d.ts +7493 -7491
  60. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  61. package/workers/data-worker/wrangler.jsonc.example +1 -1
  62. package/workers/image-worker/wrangler.jsonc.example +1 -1
  63. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  64. package/workers/pdf-worker/src/pdf-worker.example.ts +3 -0
  65. package/workers/pdf-worker/src/report-types.ts +3 -0
  66. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  67. package/workers/user-worker/src/user-worker.example.ts +6 -1
  68. package/workers/user-worker/wrangler.jsonc.example +1 -1
  69. package/wrangler.toml.example +1 -1
@@ -0,0 +1,523 @@
1
+ .container {
2
+ position: relative;
3
+ display: flex;
4
+ justify-content: center;
5
+ align-items: center;
6
+ min-height: 100vh;
7
+ padding: var(--spaceL);
8
+ z-index: var(--zIndex0);
9
+ }
10
+
11
+ .logo {
12
+ position: fixed;
13
+ top: var(--spaceL);
14
+ left: var(--spaceL);
15
+ width: 150px;
16
+ height: 150px;
17
+ background-image: url("/logo-dark.png");
18
+ background-size: contain;
19
+ background-repeat: no-repeat;
20
+ background-position: center;
21
+ z-index: var(--zIndex2);
22
+ transition: transform var(--durationM) var(--bezierFastoutSlowin);
23
+ }
24
+
25
+ .logo:hover {
26
+ transform: scale(1.05);
27
+ }
28
+
29
+ .formWrapper {
30
+ background-color: color-mix(in lab, var(--backgroundLight) 95%, transparent);
31
+ padding: var(--space2XL);
32
+ border-radius: var(--spaceXS);
33
+ box-shadow: 0 var(--spaceXS) var(--spaceM)
34
+ color-mix(in lab, var(--black) 10%, transparent);
35
+ width: 100%;
36
+ max-width: var(--maxWidthS);
37
+ }
38
+
39
+ .title {
40
+ text-align: center;
41
+ color: var(--textTitle);
42
+ margin-bottom: var(--spaceXL);
43
+ font-size: var(--fontSizeH4);
44
+ }
45
+
46
+ .form {
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: var(--spaceM);
50
+ }
51
+
52
+ .input {
53
+ padding: var(--spaceM);
54
+ border: 1.5px solid color-mix(in lab, var(--text) 20%, transparent);
55
+ border-radius: var(--spaceXS);
56
+ font-size: var(--fontSizeBodyM);
57
+ transition: all var(--durationS) var(--bezierFastoutSlowin);
58
+ }
59
+
60
+ .input:focus {
61
+ outline: none;
62
+ border-color: var(--primary);
63
+ box-shadow: 0 0 0 2px color-mix(in lab, var(--primary) 25%, transparent);
64
+ }
65
+
66
+ .input:disabled {
67
+ background-color: color-mix(in lab, var(--background) 95%, transparent);
68
+ cursor: not-allowed;
69
+ }
70
+
71
+ .passwordField {
72
+ position: relative;
73
+ display: flex;
74
+ align-items: center;
75
+ width: 100%;
76
+ }
77
+
78
+ .passwordField .input {
79
+ padding-right: var(--space2XL);
80
+ width: 100%;
81
+ }
82
+
83
+ .passwordToggle {
84
+ position: absolute;
85
+ right: var(--spaceM);
86
+ background: none;
87
+ border: none;
88
+ cursor: pointer;
89
+ font-size: 16px;
90
+ padding: var(--spaceXS);
91
+ border-radius: var(--spaceXS);
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ transition: background-color var(--durationS) var(--bezierFastoutSlowin);
96
+ z-index: 1;
97
+ height: auto;
98
+ min-width: 24px;
99
+ }
100
+
101
+ .passwordToggle:hover {
102
+ background-color: color-mix(in lab, var(--text) 10%, transparent);
103
+ }
104
+
105
+ .passwordToggle:focus {
106
+ outline: 2px solid var(--primary);
107
+ outline-offset: 2px;
108
+ }
109
+
110
+ .button {
111
+ padding: var(--spaceM) var(--spaceL);
112
+ border-radius: var(--spaceXS);
113
+ font-size: var(--fontSizeBodyS);
114
+ cursor: pointer;
115
+ background-color: var(--primary);
116
+ color: var(--white);
117
+ border: none;
118
+ transition: all var(--durationS) var(--bezierFastoutSlowin);
119
+ }
120
+
121
+ .button:hover:not(:disabled) {
122
+ background-color: color-mix(in lab, var(--primary) 85%, var(--black));
123
+ }
124
+
125
+ .button:disabled {
126
+ background-color: color-mix(in lab, var(--background) 95%, transparent);
127
+ color: var(--textLight);
128
+ cursor: not-allowed;
129
+ }
130
+
131
+ .error {
132
+ color: color-mix(in lab, var(--error) 90%, var(--black));
133
+ text-align: left;
134
+ margin: var(--spaceXS) 0 var(--spaceS) 0;
135
+ font-size: var(--fontSizeBodyXS);
136
+ padding: var(--spaceS) var(--spaceM);
137
+ background: linear-gradient(
138
+ 135deg,
139
+ color-mix(in lab, var(--error) 12%, transparent),
140
+ color-mix(in lab, var(--error) 8%, transparent)
141
+ );
142
+ border-radius: var(--spaceXS);
143
+ border: 1px solid color-mix(in lab, var(--error) 30%, transparent);
144
+ border-left: 3px solid var(--error);
145
+ animation: slideInError var(--durationS) var(--bezierFastoutSlowin);
146
+ position: relative;
147
+ overflow: hidden;
148
+ box-shadow: 0 2px 8px color-mix(in lab, var(--text) 8%, transparent);
149
+ }
150
+
151
+ .error::before {
152
+ content: "";
153
+ position: absolute;
154
+ top: 0;
155
+ left: 0;
156
+ bottom: 0;
157
+ width: 3px;
158
+ background: var(--error);
159
+ animation: errorPulse 1.5s ease-in-out infinite;
160
+ }
161
+
162
+ .success {
163
+ color: color-mix(in lab, var(--success) 90%, var(--black));
164
+ text-align: left;
165
+ margin: var(--spaceXS) 0 var(--spaceS) 0;
166
+ font-size: var(--fontSizeBodyXS);
167
+ padding: var(--spaceS) var(--spaceM);
168
+ background: linear-gradient(
169
+ 135deg,
170
+ color-mix(in lab, var(--success) 12%, transparent),
171
+ color-mix(in lab, var(--success) 8%, transparent)
172
+ );
173
+ border-radius: var(--spaceXS);
174
+ border: 1px solid color-mix(in lab, var(--success) 30%, transparent);
175
+ border-left: 3px solid var(--success);
176
+ animation: slideInSuccess var(--durationS) var(--bezierFastoutSlowin);
177
+ position: relative;
178
+ overflow: hidden;
179
+ box-shadow: 0 2px 8px color-mix(in lab, var(--text) 8%, transparent);
180
+ }
181
+
182
+ .success::before {
183
+ content: "";
184
+ position: absolute;
185
+ top: 0;
186
+ left: 0;
187
+ bottom: 0;
188
+ width: 3px;
189
+ background: var(--success);
190
+ animation: successPulse 1.5s ease-in-out infinite;
191
+ }
192
+
193
+ .hint {
194
+ color: var(--textLight);
195
+ text-align: center;
196
+ margin: calc(var(--spaceS) * -0.5) 0 var(--spaceS) 0;
197
+ font-size: var(--fontSizeBodyXS);
198
+ line-height: 1.4;
199
+ }
200
+
201
+ .toggle {
202
+ text-align: center;
203
+ margin-top: var(--spaceL);
204
+ color: var(--textLight);
205
+ }
206
+
207
+ .toggleButton {
208
+ background: none;
209
+ border: none;
210
+ color: var(--primary);
211
+ cursor: pointer;
212
+ font-size: inherit;
213
+ padding: 0;
214
+ text-decoration: underline;
215
+ transition: color var(--durationS) var(--bezierFastoutSlowin);
216
+ }
217
+
218
+ .toggleButton:hover:not(:disabled) {
219
+ color: color-mix(in lab, var(--primary) 85%, var(--black));
220
+ }
221
+
222
+ .toggleButton:disabled {
223
+ color: var(--textLight);
224
+ cursor: not-allowed;
225
+ }
226
+
227
+ .resetLink {
228
+ background: none;
229
+ border: none;
230
+ color: var(--primary);
231
+ cursor: pointer;
232
+ font-size: inherit;
233
+ padding: 0;
234
+ text-decoration: underline;
235
+ transition: color var(--durationS) var(--bezierFastoutSlowin);
236
+ }
237
+
238
+ .resetLink:hover {
239
+ color: color-mix(in lab, var(--primary) 85%, var(--black));
240
+ }
241
+
242
+ .passwordStrength {
243
+ font-size: var(--fontSizeBodyXS);
244
+ color: var(--textLight);
245
+ margin-top: var(--spaceS);
246
+ white-space: pre-line;
247
+ }
248
+
249
+ .caseReviewToggleSection {
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: space-between;
253
+ padding: var(--spaceM);
254
+ background: var(--backgroundLight);
255
+ border-radius: var(--spaceXS);
256
+ border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
257
+ margin: var(--spaceM) 0;
258
+ }
259
+
260
+ .caseReviewLabel {
261
+ font-size: var(--fontSizeBodyS);
262
+ font-weight: var(--fontWeightMedium);
263
+ color: var(--textTitle);
264
+ margin: 0;
265
+ background: none;
266
+ border: none;
267
+ padding: 0;
268
+ cursor: pointer;
269
+ text-decoration: underline;
270
+ text-decoration-color: color-mix(in lab, var(--primary) 50%, transparent);
271
+ transition: color var(--durationS) var(--bezierFastoutSlowin);
272
+ }
273
+
274
+ .caseReviewLabel:hover {
275
+ color: var(--primary);
276
+ text-decoration-color: var(--primary);
277
+ }
278
+
279
+ .caseReviewToggle {
280
+ display: flex;
281
+ background: color-mix(in lab, var(--primary) 10%, transparent);
282
+ border: 1px solid color-mix(in lab, var(--primary) 20%, transparent);
283
+ border-radius: var(--spaceXS);
284
+ overflow: hidden;
285
+ box-shadow: 0 1px 3px color-mix(in lab, var(--primary) 15%, transparent);
286
+ }
287
+
288
+ .caseReviewOption {
289
+ background: transparent;
290
+ border: none;
291
+ padding: var(--spaceS) var(--spaceM);
292
+ font-size: var(--fontSizeBodyS);
293
+ font-weight: var(--fontWeightMedium);
294
+ color: var(--primary);
295
+ cursor: pointer;
296
+ transition: all var(--durationS) var(--bezierFastoutSlowin);
297
+ position: relative;
298
+ min-width: 50px;
299
+ }
300
+
301
+ .caseReviewOption:hover:not(:disabled) {
302
+ background: color-mix(in lab, var(--primary) 15%, transparent);
303
+ color: var(--primary);
304
+ }
305
+
306
+ .caseReviewOption:disabled {
307
+ opacity: 0.5;
308
+ cursor: not-allowed;
309
+ }
310
+
311
+ .caseReviewOptionActive {
312
+ background: var(--primary) !important;
313
+ color: white !important;
314
+ box-shadow: 0 1px 3px color-mix(in lab, var(--primary) 30%, transparent);
315
+ }
316
+
317
+ .caseReviewOptionActive:hover:not(:disabled) {
318
+ background: color-mix(in lab, var(--primary) 85%, var(--black)) !important;
319
+ }
320
+
321
+ /* Animations */
322
+ @keyframes slideInError {
323
+ from {
324
+ opacity: 0;
325
+ transform: translateY(calc(-1 * var(--spaceM))) scaleY(0.8);
326
+ max-height: 0;
327
+ padding-top: 0;
328
+ padding-bottom: 0;
329
+ margin-top: 0;
330
+ margin-bottom: 0;
331
+ }
332
+ to {
333
+ opacity: 1;
334
+ transform: translateY(0) scaleY(1);
335
+ max-height: 200px;
336
+ padding-top: var(--spaceS);
337
+ padding-bottom: var(--spaceS);
338
+ margin-top: var(--spaceXS);
339
+ margin-bottom: var(--spaceS);
340
+ }
341
+ }
342
+
343
+ @keyframes errorPulse {
344
+ 0%,
345
+ 100% {
346
+ opacity: 1;
347
+ box-shadow: 0 0 0 0 color-mix(in lab, var(--error) 40%, transparent);
348
+ }
349
+ 50% {
350
+ opacity: 0.7;
351
+ box-shadow: 0 0 0 4px color-mix(in lab, var(--error) 20%, transparent);
352
+ }
353
+ }
354
+
355
+ @keyframes slideInSuccess {
356
+ from {
357
+ opacity: 0;
358
+ transform: translateY(calc(-1 * var(--spaceM))) scaleY(0.8);
359
+ max-height: 0;
360
+ padding-top: 0;
361
+ padding-bottom: 0;
362
+ margin-top: 0;
363
+ margin-bottom: 0;
364
+ }
365
+ to {
366
+ opacity: 1;
367
+ transform: translateY(0) scaleY(1);
368
+ max-height: 200px;
369
+ padding-top: var(--spaceS);
370
+ padding-bottom: var(--spaceS);
371
+ margin-top: var(--spaceXS);
372
+ margin-bottom: var(--spaceS);
373
+ }
374
+ }
375
+
376
+ @keyframes successPulse {
377
+ 0%,
378
+ 100% {
379
+ opacity: 1;
380
+ box-shadow: 0 0 0 0 color-mix(in lab, var(--success) 40%, transparent);
381
+ }
382
+ 50% {
383
+ opacity: 0.7;
384
+ box-shadow: 0 0 0 4px color-mix(in lab, var(--success) 20%, transparent);
385
+ }
386
+ }
387
+
388
+ /* Reduce motion for accessibility */
389
+ @media (prefers-reduced-motion: reduce) {
390
+ .error,
391
+ .success {
392
+ animation: none;
393
+ }
394
+
395
+ .error::before,
396
+ .success::before {
397
+ animation: none;
398
+ }
399
+ }
400
+
401
+ /* Email Verification Description */
402
+ .verificationDescription {
403
+ font-size: var(--fontSizeBodyM);
404
+ color: var(--textBody);
405
+ text-align: center;
406
+ margin-bottom: var(--spaceL);
407
+ line-height: var(--lineHeightBody);
408
+ }
409
+
410
+ .verificationActions {
411
+ display: flex;
412
+ flex-direction: column;
413
+ gap: var(--spaceM);
414
+ width: 100%;
415
+ margin-bottom: var(--spaceL);
416
+ }
417
+
418
+ .secondaryButton {
419
+ background: transparent;
420
+ border: 1px solid var(--primary);
421
+ color: var(--primary);
422
+ padding: var(--spaceM) var(--spaceL);
423
+ border-radius: var(--spaceXS);
424
+ font-size: var(--fontSizeBodyS);
425
+ font-weight: var(--fontWeightMedium);
426
+ cursor: pointer;
427
+ transition: all var(--durationS) var(--bezierFastoutSlowin);
428
+ }
429
+
430
+ .secondaryButton:hover:not(:disabled) {
431
+ background: color-mix(in lab, var(--primary) 10%, transparent);
432
+ }
433
+
434
+ .secondaryButton:disabled {
435
+ opacity: 0.5;
436
+ cursor: not-allowed;
437
+ }
438
+
439
+ .secondaryButtonLink {
440
+ display: inline-block;
441
+ background: transparent;
442
+ border: 1px solid var(--primary);
443
+ color: var(--primary);
444
+ padding: var(--spaceM) var(--spaceL);
445
+ border-radius: var(--spaceXS);
446
+ font-size: var(--fontSizeBodyS);
447
+ font-weight: var(--fontWeightMedium);
448
+ cursor: pointer;
449
+ transition: all var(--durationS) var(--bezierFastoutSlowin);
450
+ text-decoration: none;
451
+ text-align: center;
452
+ width: 100%;
453
+ box-sizing: border-box;
454
+ }
455
+
456
+ .secondaryButtonLink:hover {
457
+ background: color-mix(in lab, var(--primary) 10%, transparent);
458
+ text-decoration: none;
459
+ color: var(--primary);
460
+ transform: translateY(-1px);
461
+ box-shadow: 0 2px 6px color-mix(in lab, currentColor 20%, transparent);
462
+ }
463
+
464
+ .verificationHints {
465
+ text-align: left;
466
+ background: color-mix(in lab, var(--backgroundLight) 50%, transparent);
467
+ padding: var(--spaceL);
468
+ border-radius: var(--spaceXS);
469
+ border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
470
+ }
471
+
472
+ .loginToStriaeButton {
473
+ width: 100%;
474
+ padding: var(--spaceL);
475
+ background-color: #1e7e34;
476
+ color: var(--white);
477
+ border: none;
478
+ border-radius: var(--spaceXS);
479
+ font-weight: var(--fontWeightMedium);
480
+ font-size: var(--fontSizeBodyM);
481
+ cursor: pointer;
482
+ transition: all var(--durationS) var(--bezierFastoutSlowin);
483
+ box-sizing: border-box;
484
+ }
485
+
486
+ .loginToStriaeButton:hover:not(:disabled) {
487
+ background-color: #155724;
488
+ }
489
+
490
+ .loginToStriaeButton:disabled {
491
+ background-color: color-mix(in lab, var(--background) 95%, transparent);
492
+ color: var(--textLight);
493
+ cursor: not-allowed;
494
+ }
495
+
496
+ .verificationHints .hint {
497
+ font-size: var(--fontSizeBodyS);
498
+ color: var(--textTitle);
499
+ margin: 0 0 var(--spaceS) 0;
500
+ font-weight: var(--fontWeightMedium);
501
+ }
502
+
503
+ .hintList {
504
+ list-style: none;
505
+ padding: 0;
506
+ margin: 0;
507
+ }
508
+
509
+ .hintList li {
510
+ font-size: var(--fontSizeBodyXS);
511
+ color: var(--textBody);
512
+ margin-bottom: var(--spaceXS);
513
+ padding-left: var(--spaceM);
514
+ position: relative;
515
+ }
516
+
517
+ .hintList li::before {
518
+ content: "•";
519
+ color: var(--primary);
520
+ position: absolute;
521
+ left: 0;
522
+ font-weight: bold;
523
+ }
@@ -580,7 +580,7 @@ export const Login = () => {
580
580
  <Link
581
581
  viewTransition
582
582
  prefetch="intent"
583
- to="https://striae.app"
583
+ to="/"
584
584
  className={styles.logoLink}>
585
585
  <div className={styles.logo} />
586
586
  </Link>
@@ -12,7 +12,8 @@
12
12
  background-color: color-mix(in lab, var(--backgroundLight) 95%, transparent);
13
13
  padding: var(--space2XL);
14
14
  border-radius: var(--spaceXS);
15
- box-shadow: 0 var(--spaceXS) var(--spaceM) color-mix(in lab, var(--black) 10%, transparent);
15
+ box-shadow: 0 var(--spaceXS) var(--spaceM)
16
+ color-mix(in lab, var(--black) 10%, transparent);
16
17
  width: 100%;
17
18
  max-width: var(--maxWidthS);
18
19
  }
@@ -29,7 +30,7 @@
29
30
  left: var(--spaceL);
30
31
  width: 150px;
31
32
  height: 150px;
32
- background-image: url('/logo-dark.png');
33
+ background-image: url("/logo-dark.png");
33
34
  background-size: contain;
34
35
  background-repeat: no-repeat;
35
36
  background-position: center;
@@ -114,7 +115,8 @@
114
115
  margin: var(--spaceXS) 0 var(--spaceS) 0;
115
116
  font-size: var(--fontSizeBodyXS);
116
117
  padding: var(--spaceS) var(--spaceM);
117
- background: linear-gradient(135deg,
118
+ background: linear-gradient(
119
+ 135deg,
118
120
  color-mix(in lab, var(--error) 12%, transparent),
119
121
  color-mix(in lab, var(--error) 8%, transparent)
120
122
  );
@@ -128,7 +130,7 @@
128
130
  }
129
131
 
130
132
  .error::before {
131
- content: '';
133
+ content: "";
132
134
  position: absolute;
133
135
  top: 0;
134
136
  left: 0;
@@ -144,7 +146,8 @@
144
146
  margin: var(--spaceXS) 0 var(--spaceS) 0;
145
147
  font-size: var(--fontSizeBodyXS);
146
148
  padding: var(--spaceS) var(--spaceM);
147
- background: linear-gradient(135deg,
149
+ background: linear-gradient(
150
+ 135deg,
148
151
  color-mix(in lab, var(--success) 12%, transparent),
149
152
  color-mix(in lab, var(--success) 8%, transparent)
150
153
  );
@@ -158,7 +161,7 @@
158
161
  }
159
162
 
160
163
  .success::before {
161
- content: '';
164
+ content: "";
162
165
  position: absolute;
163
166
  top: 0;
164
167
  left: 0;
@@ -176,6 +179,7 @@
176
179
  justify-content: center;
177
180
  align-items: center;
178
181
  z-index: var(--zIndex5);
182
+ cursor: default;
179
183
  }
180
184
 
181
185
  .modal {
@@ -183,9 +187,11 @@
183
187
  border-radius: var(--spaceXS);
184
188
  width: 90%;
185
189
  max-width: 500px;
186
- box-shadow: 0 var(--spaceXS) var(--spaceL) color-mix(in lab, var(--black) 10%, transparent);
190
+ box-shadow: 0 var(--spaceXS) var(--spaceL)
191
+ color-mix(in lab, var(--black) 10%, transparent);
187
192
  overflow: hidden;
188
193
  padding: var(--space2XL);
194
+ cursor: default;
189
195
  }
190
196
 
191
197
  .modalHeader {
@@ -220,7 +226,8 @@
220
226
  }
221
227
 
222
228
  @keyframes errorPulse {
223
- 0%, 100% {
229
+ 0%,
230
+ 100% {
224
231
  opacity: 1;
225
232
  box-shadow: 0 0 0 0 color-mix(in lab, var(--error) 40%, transparent);
226
233
  }
@@ -252,7 +259,8 @@
252
259
  }
253
260
 
254
261
  @keyframes successPulse {
255
- 0%, 100% {
262
+ 0%,
263
+ 100% {
256
264
  opacity: 1;
257
265
  box-shadow: 0 0 0 0 color-mix(in lab, var(--success) 40%, transparent);
258
266
  }
@@ -264,11 +272,13 @@
264
272
 
265
273
  /* Reduce motion for accessibility */
266
274
  @media (prefers-reduced-motion: reduce) {
267
- .error, .success {
275
+ .error,
276
+ .success {
268
277
  animation: none;
269
278
  }
270
-
271
- .error::before, .success::before {
279
+
280
+ .error::before,
281
+ .success::before {
272
282
  animation: none;
273
283
  }
274
- }
284
+ }
@@ -28,6 +28,8 @@ export const Striae = ({ user }: StriaePage) => {
28
28
  // User states
29
29
  const [userCompany, setUserCompany] = useState<string>('');
30
30
  const [userFirstName, setUserFirstName] = useState<string>('');
31
+ const [userLastName, setUserLastName] = useState<string>('');
32
+ const [userBadgeId, setUserBadgeId] = useState<string>('');
31
33
 
32
34
  // Case management states - All managed here
33
35
  const [currentCase, setCurrentCase] = useState<string>('');
@@ -80,9 +82,11 @@ export const Striae = ({ user }: StriaePage) => {
80
82
  });
81
83
 
82
84
  if (response.ok) {
83
- const userData = await response.json() as { company?: string; firstName?: string };
85
+ const userData = await response.json() as { company?: string; firstName?: string; lastName?: string; badgeId?: string };
84
86
  setUserCompany(userData.company || '');
85
87
  setUserFirstName(userData.firstName || '');
88
+ setUserLastName(userData.lastName || '');
89
+ setUserBadgeId(userData.badgeId || '');
86
90
  }
87
91
  } catch (err) {
88
92
  console.error('Failed to load user company:', err);
@@ -167,6 +171,8 @@ export const Striae = ({ user }: StriaePage) => {
167
171
  selectedFilename,
168
172
  userCompany,
169
173
  userFirstName,
174
+ userLastName,
175
+ userBadgeId,
170
176
  currentCase,
171
177
  annotationData,
172
178
  activeAnnotations,
@@ -389,6 +395,7 @@ export const Striae = ({ user }: StriaePage) => {
389
395
  imageUrl={selectedImage}
390
396
  filename={selectedFilename}
391
397
  company={userCompany}
398
+ badgeId={userBadgeId}
392
399
  firstName={userFirstName}
393
400
  error={error ?? ''}
394
401
  activeAnnotations={activeAnnotations}
package/app/routes.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { type RouteConfig, index, route } from "@react-router/dev/routes";
2
+
3
+ export default [
4
+ index("routes/_index.tsx"),
5
+ route("auth", "routes/auth/route.ts"),
6
+ route("auth/login", "routes/auth/route.ts", { id: "routes/auth/login-alias" }),
7
+ ] satisfies RouteConfig;
@@ -30,6 +30,7 @@ export const AUDIT_CSV_ENTRY_HEADERS = [
30
30
  'Profile Field',
31
31
  'Old Value',
32
32
  'New Value',
33
+ 'Badge/ID',
33
34
  'Total Confirmations In File',
34
35
  'Confirmations Successfully Imported',
35
36
  'Validation Steps Failed',
@@ -112,6 +113,7 @@ export const entryToCSVRow = (entry: ValidationAuditEntry): string => {
112
113
  formatForCSV(userProfileDetails?.profileField),
113
114
  formatForCSV(userProfileDetails?.oldValue),
114
115
  formatForCSV(userProfileDetails?.newValue),
116
+ formatForCSV(userProfileDetails?.badgeId),
115
117
  caseDetails?.totalAnnotations?.toString() || '',
116
118
  performanceMetrics?.validationStepsCompleted?.toString() || '',
117
119
  performanceMetrics?.validationStepsFailed?.toString() || '',