@striae-org/striae 4.2.1 → 4.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.
Files changed (66) hide show
  1. package/app/components/actions/case-import/confirmation-import.ts +20 -1
  2. package/app/components/actions/case-import/orchestrator.ts +3 -0
  3. package/app/components/actions/case-manage.ts +5 -1
  4. package/app/components/actions/confirm-export.ts +12 -3
  5. package/app/components/audit/viewer/audit-entries-list.tsx +20 -2
  6. package/app/components/audit/viewer/use-audit-viewer-export.ts +2 -2
  7. package/app/components/audit/viewer/use-audit-viewer-filters.ts +11 -1
  8. package/app/components/canvas/canvas.tsx +2 -1
  9. package/app/components/navbar/case-modals/archive-case-modal.module.css +0 -76
  10. package/app/components/navbar/case-modals/archive-case-modal.tsx +9 -8
  11. package/app/components/navbar/case-modals/case-modal-shared.module.css +94 -0
  12. package/app/components/navbar/case-modals/delete-case-modal.module.css +9 -0
  13. package/app/components/navbar/case-modals/delete-case-modal.tsx +79 -0
  14. package/app/components/navbar/case-modals/open-case-modal.module.css +2 -1
  15. package/app/components/navbar/case-modals/rename-case-modal.module.css +0 -72
  16. package/app/components/navbar/case-modals/rename-case-modal.tsx +9 -8
  17. package/app/components/navbar/navbar.module.css +11 -0
  18. package/app/components/navbar/navbar.tsx +38 -19
  19. package/app/components/sidebar/case-import/hooks/useImportExecution.ts +2 -0
  20. package/app/components/sidebar/cases/case-sidebar.tsx +27 -3
  21. package/app/components/sidebar/cases/cases-modal.module.css +312 -10
  22. package/app/components/sidebar/cases/cases-modal.tsx +690 -110
  23. package/app/components/sidebar/cases/cases.module.css +23 -0
  24. package/app/components/sidebar/files/delete-files-modal.module.css +26 -0
  25. package/app/components/sidebar/files/delete-files-modal.tsx +94 -0
  26. package/app/components/sidebar/files/files-modal.module.css +285 -44
  27. package/app/components/sidebar/files/files-modal.tsx +452 -145
  28. package/app/components/sidebar/notes/class-details-fields.tsx +146 -0
  29. package/app/components/sidebar/notes/class-details-modal.tsx +147 -0
  30. package/app/components/sidebar/notes/class-details-sections.tsx +561 -0
  31. package/app/components/sidebar/notes/class-details-shared.ts +239 -0
  32. package/app/components/sidebar/notes/notes-editor-form.tsx +43 -5
  33. package/app/components/sidebar/notes/notes.module.css +236 -4
  34. package/app/components/sidebar/notes/use-class-details-state.ts +371 -0
  35. package/app/components/sidebar/sidebar-container.tsx +2 -0
  36. package/app/components/sidebar/sidebar.tsx +8 -1
  37. package/app/hooks/useCaseListPreferences.ts +99 -0
  38. package/app/hooks/useFileListPreferences.ts +106 -0
  39. package/app/routes/striae/striae.tsx +45 -1
  40. package/app/services/audit/audit-export-csv.ts +4 -2
  41. package/app/services/audit/audit-export-report.ts +36 -4
  42. package/app/services/audit/audit.service.ts +2 -0
  43. package/app/services/audit/builders/audit-entry-builder.ts +1 -0
  44. package/app/services/audit/builders/audit-event-builders-workflow.ts +8 -2
  45. package/app/types/annotations.ts +48 -1
  46. package/app/types/audit.ts +1 -0
  47. package/app/utils/data/case-filters.ts +127 -0
  48. package/app/utils/data/confirmation-summary/summary-core.ts +18 -2
  49. package/app/utils/data/file-filters.ts +201 -0
  50. package/app/utils/forensics/confirmation-signature.ts +20 -5
  51. package/functions/api/image/[[path]].ts +4 -0
  52. package/package.json +3 -4
  53. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  54. package/workers/data-worker/src/signing-payload-utils.ts +5 -0
  55. package/workers/data-worker/wrangler.jsonc.example +1 -1
  56. package/workers/image-worker/wrangler.jsonc.example +1 -1
  57. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  58. package/workers/pdf-worker/src/formats/format-striae.ts +84 -118
  59. package/workers/pdf-worker/src/pdf-worker.example.ts +28 -10
  60. package/workers/pdf-worker/src/report-layout.ts +227 -0
  61. package/workers/pdf-worker/src/report-types.ts +20 -0
  62. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  63. package/workers/user-worker/wrangler.jsonc.example +1 -1
  64. package/wrangler.toml.example +1 -1
  65. package/workers/pdf-worker/src/assets/icon-256.png +0 -0
  66. /package/workers/pdf-worker/src/assets/{generated-assets.ts → generated-assets.example.ts} +0 -0
@@ -384,6 +384,17 @@
384
384
  background: color-mix(in lab, #0d9488 20%, #ffffff);
385
385
  }
386
386
 
387
+ .caseMenuItemClearRO {
388
+ background: color-mix(in lab, #fd7e14 16%, #ffffff);
389
+ color: #7c3f00;
390
+ border-color: color-mix(in lab, #fd7e14 30%, transparent);
391
+ }
392
+
393
+ .caseMenuItemClearRO:hover {
394
+ background: color-mix(in lab, #fd7e14 22%, #ffffff);
395
+ border-color: color-mix(in lab, #fd7e14 36%, transparent);
396
+ }
397
+
387
398
  .caseMenuCaption {
388
399
  margin-top: 0.25rem;
389
400
  padding: 0.3rem 0.45rem 0.1rem;
@@ -26,6 +26,7 @@ interface NavbarProps {
26
26
  onOpenRenameCase?: () => void;
27
27
  onDeleteCase?: () => void;
28
28
  onArchiveCase?: () => void;
29
+ onClearROCase?: () => void;
29
30
  onOpenViewAllFiles?: () => void;
30
31
  onDeleteCurrentFile?: () => void;
31
32
  onOpenImageNotes?: () => void;
@@ -55,6 +56,7 @@ export const Navbar = ({
55
56
  onOpenRenameCase,
56
57
  onDeleteCase,
57
58
  onArchiveCase,
59
+ onClearROCase,
58
60
  onOpenViewAllFiles,
59
61
  onDeleteCurrentFile,
60
62
  onOpenImageNotes,
@@ -151,6 +153,8 @@ export const Navbar = ({
151
153
  type="button"
152
154
  role="menuitem"
153
155
  className={`${styles.caseMenuItem} ${styles.caseMenuItemOpen}`}
156
+ disabled={isReadOnly}
157
+ title={isReadOnly ? 'Clear the read-only case first to open or switch cases' : undefined}
154
158
  onClick={() => {
155
159
  onOpenCase?.();
156
160
  setIsCaseMenuOpen(false);
@@ -162,6 +166,8 @@ export const Navbar = ({
162
166
  type="button"
163
167
  role="menuitem"
164
168
  className={`${styles.caseMenuItem} ${styles.caseMenuItemList}`}
169
+ disabled={isReadOnly}
170
+ title={isReadOnly ? 'Clear the read-only case first to list all cases' : undefined}
165
171
  onClick={() => {
166
172
  onOpenListAllCases?.();
167
173
  setIsCaseMenuOpen(false);
@@ -202,8 +208,21 @@ export const Navbar = ({
202
208
  >
203
209
  Case Audit Trail
204
210
  </button>
205
- {(!isReadOnly || archiveDetails?.archived) && (
206
- <div className={styles.caseMenuSectionLabel}>Maintenance</div>
211
+ <div className={styles.caseMenuSectionLabel}>Maintenance</div>
212
+ {isReadOnly && (
213
+ <button
214
+ type="button"
215
+ role="menuitem"
216
+ className={`${styles.caseMenuItem} ${styles.caseMenuItemClearRO}`}
217
+ disabled={!hasLoadedCase}
218
+ title={!hasLoadedCase ? 'No read-only case is loaded' : undefined}
219
+ onClick={() => {
220
+ onClearROCase?.();
221
+ setIsCaseMenuOpen(false);
222
+ }}
223
+ >
224
+ Clear RO Case
225
+ </button>
207
226
  )}
208
227
  {!isReadOnly && (
209
228
  <button
@@ -226,27 +245,27 @@ export const Navbar = ({
226
245
  Rename Case
227
246
  </button>
228
247
  )}
229
- {(!isReadOnly || archiveDetails?.archived) && (
230
- <button
231
- type="button"
232
- role="menuitem"
233
- className={`${styles.caseMenuItem} ${styles.caseMenuItemDelete}`}
234
- disabled={!hasLoadedCase || disableLongRunningCaseActions}
235
- title={
236
- !hasLoadedCase
248
+ <button
249
+ type="button"
250
+ role="menuitem"
251
+ className={`${styles.caseMenuItem} ${styles.caseMenuItemDelete}`}
252
+ disabled={!hasLoadedCase || disableLongRunningCaseActions || isReadOnly}
253
+ title={
254
+ isReadOnly
255
+ ? 'Clear the read-only case first before deleting'
256
+ : !hasLoadedCase
237
257
  ? 'Load a case to delete it'
238
258
  : disableLongRunningCaseActions
239
259
  ? 'Delete is unavailable while files are uploading'
240
260
  : undefined
241
- }
242
- onClick={() => {
243
- onDeleteCase?.();
244
- setIsCaseMenuOpen(false);
245
- }}
246
- >
247
- Delete Case
248
- </button>
249
- )}
261
+ }
262
+ onClick={() => {
263
+ onDeleteCase?.();
264
+ setIsCaseMenuOpen(false);
265
+ }}
266
+ >
267
+ Delete Case
268
+ </button>
250
269
  {!isReadOnly && (
251
270
  <button
252
271
  type="button"
@@ -117,6 +117,8 @@ export const useImportExecution = ({
117
117
  }
118
118
 
119
119
  setSuccess(message);
120
+
121
+ onImportComplete?.(result);
120
122
 
121
123
  // No auto-close for confirmation imports - let user read the details and close manually
122
124
 
@@ -1,4 +1,5 @@
1
1
  import type { User } from 'firebase/auth';
2
+ import type React from 'react';
2
3
  import { useState, useEffect, useMemo, useCallback } from 'react';
3
4
  import styles from './cases.module.css';
4
5
  import { FilesModal } from '../files/files-modal';
@@ -25,12 +26,14 @@ interface CaseSidebarProps {
25
26
  setFiles: React.Dispatch<React.SetStateAction<FileData[]>>;
26
27
  currentCase: string | null;
27
28
  isReadOnly?: boolean;
29
+ isArchivedCase?: boolean;
28
30
  isConfirmed?: boolean;
29
31
  confirmationSaveVersion?: number;
30
32
  selectedFileId?: string;
31
33
  isUploading?: boolean;
32
34
  onUploadStatusChange?: (isUploading: boolean) => void;
33
35
  onUploadComplete?: (result: { successCount: number; failedFiles: string[] }) => void;
36
+ onOpenCaseExport?: () => void;
34
37
  }
35
38
 
36
39
  export const CaseSidebar = ({
@@ -44,12 +47,14 @@ export const CaseSidebar = ({
44
47
  setFiles,
45
48
  currentCase,
46
49
  isReadOnly = false,
50
+ isArchivedCase = false,
47
51
  isConfirmed = false,
48
52
  confirmationSaveVersion = 0,
49
53
  selectedFileId,
50
54
  isUploading = false,
51
55
  onUploadStatusChange,
52
- onUploadComplete
56
+ onUploadComplete,
57
+ onOpenCaseExport
53
58
  }: CaseSidebarProps) => {
54
59
 
55
60
  const [, setFileError] = useState('');
@@ -253,6 +258,14 @@ const handleImageSelect = (file: FileData) => {
253
258
  ? 'Select an image first'
254
259
  : undefined;
255
260
 
261
+ const showCaseExportButton = Boolean(currentCase && isReadOnly && !isArchivedCase);
262
+
263
+ const exportCaseTitle = isUploading
264
+ ? 'Cannot export while uploading'
265
+ : !currentCase
266
+ ? 'Load a case first'
267
+ : undefined;
268
+
256
269
  return (
257
270
  <>
258
271
  <div className={styles.caseSection}>
@@ -371,14 +384,25 @@ return (
371
384
  )}
372
385
  </div>
373
386
  <div className={styles.sidebarToggle}>
374
- <button
387
+ {showCaseExportButton ? (
388
+ <button
389
+ className={styles.confirmationExportButton}
390
+ onClick={onOpenCaseExport}
391
+ disabled={isUploading || !currentCase}
392
+ title={exportCaseTitle}
393
+ >
394
+ Export Confirmations
395
+ </button>
396
+ ) : (
397
+ <button
375
398
  onClick={onNotesClick}
376
399
  disabled={isImageNotesDisabled}
377
400
  title={imageNotesTitle}
378
401
  >
379
402
  Image Notes
380
403
  </button>
381
- </div>
404
+ )}
405
+ </div>
382
406
  </div>
383
407
  </>
384
408
  );
@@ -14,6 +14,7 @@
14
14
  position: relative;
15
15
  background: var(--backgroundLight);
16
16
  border-radius: var(--spaceXS);
17
+ overflow: hidden;
17
18
  width: 90%;
18
19
  max-width: var(--maxWidthL);
19
20
  max-height: 80vh;
@@ -41,6 +42,9 @@
41
42
  overflow-y: auto;
42
43
  flex: 1;
43
44
  min-height: 0;
45
+ display: flex;
46
+ flex-direction: column;
47
+ gap: var(--spaceM);
44
48
  }
45
49
 
46
50
  .closeButton {
@@ -57,46 +61,300 @@
57
61
  color: var(--text);
58
62
  }
59
63
 
64
+ .controlsSection {
65
+ display: grid;
66
+ grid-template-columns: repeat(4, minmax(0, 1fr));
67
+ gap: var(--spaceM);
68
+ align-items: end;
69
+ }
70
+
71
+ .controlGroup {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: var(--spaceXS);
75
+ }
76
+
77
+ .controlGroup label {
78
+ color: var(--textTitle);
79
+ font-size: var(--fontSizeBodyS);
80
+ font-weight: var(--fontWeightMedium);
81
+ }
82
+
83
+ .controlGroup select {
84
+ width: 100%;
85
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
86
+ border-radius: var(--spaceXS);
87
+ background: var(--backgroundLight);
88
+ color: var(--textBody);
89
+ padding: var(--spaceS) var(--spaceM);
90
+ font-size: var(--fontSizeBodyS);
91
+ }
92
+
93
+ .controlGroup select:focus {
94
+ outline: 2px solid color-mix(in lab, var(--primary) 30%, transparent);
95
+ outline-offset: 1px;
96
+ }
97
+
98
+ .archiveToggle {
99
+ display: inline-flex;
100
+ align-items: center;
101
+ gap: var(--spaceS);
102
+ color: var(--textBody);
103
+ font-size: var(--fontSizeBodyS);
104
+ }
105
+
106
+ .archiveToggle input {
107
+ width: var(--spaceM);
108
+ height: var(--spaceM);
109
+ accent-color: var(--primary);
110
+ }
111
+
112
+ .resetButton {
113
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
114
+ border-radius: var(--spaceXS);
115
+ background: var(--backgroundLight);
116
+ color: var(--textBody);
117
+ padding: var(--spaceS) var(--spaceM);
118
+ font-size: var(--fontSizeBodyS);
119
+ cursor: pointer;
120
+ }
121
+
122
+ .resetButton:disabled {
123
+ cursor: not-allowed;
124
+ opacity: 0.5;
125
+ }
126
+
127
+ .caseCount {
128
+ margin: 0;
129
+ font-size: var(--fontSizeBodyS);
130
+ color: var(--textLight);
131
+ }
132
+
133
+ .searchSection {
134
+ display: flex;
135
+ flex-direction: column;
136
+ gap: var(--spaceXS);
137
+ }
138
+
139
+ .searchSection label {
140
+ color: var(--textTitle);
141
+ font-size: var(--fontSizeBodyS);
142
+ font-weight: var(--fontWeightMedium);
143
+ }
144
+
145
+ .searchInput {
146
+ width: 100%;
147
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
148
+ border-radius: var(--spaceXS);
149
+ background: var(--backgroundLight);
150
+ color: var(--textBody);
151
+ padding: var(--spaceS) var(--spaceM);
152
+ font-size: var(--fontSizeBodyS);
153
+ }
154
+
155
+ .searchInput:focus {
156
+ outline: 2px solid color-mix(in lab, var(--primary) 30%, transparent);
157
+ outline-offset: 1px;
158
+ }
159
+
160
+ .actionNotice {
161
+ margin: 0;
162
+ border-radius: var(--spaceXS);
163
+ padding: var(--spaceS) var(--spaceM);
164
+ font-size: var(--fontSizeBodyS);
165
+ border: 1px solid transparent;
166
+ }
167
+
168
+ .actionNoticeSuccess {
169
+ background: color-mix(in lab, var(--success) 15%, var(--backgroundLight));
170
+ color: var(--textBody);
171
+ border-color: color-mix(in lab, var(--success) 35%, transparent);
172
+ }
173
+
174
+ .actionNoticeWarning {
175
+ background: color-mix(in lab, var(--warning) 18%, var(--backgroundLight));
176
+ color: var(--textBody);
177
+ border-color: color-mix(in lab, var(--warning) 40%, transparent);
178
+ }
179
+
180
+ .actionNoticeError {
181
+ background: color-mix(in lab, var(--error) 16%, var(--backgroundLight));
182
+ color: var(--error);
183
+ border-color: color-mix(in lab, var(--error) 30%, transparent);
184
+ }
185
+
60
186
  .casesList {
61
187
  list-style: none;
62
188
  padding: 0;
63
189
  margin: 0;
64
- display: grid;
65
- grid-template-columns: repeat(2, 1fr);
66
- gap: var(--spaceL);
190
+ display: flex;
191
+ flex-direction: column;
192
+ gap: var(--spaceM);
67
193
  }
68
194
 
69
195
  .caseItem {
70
196
  width: 100%;
71
- padding: var(--spaceM) var(--spaceL);
72
- border: 1px solid #e9ecef;
197
+ padding: var(--spaceM);
198
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
73
199
  border-radius: var(--spaceXS);
74
- text-align: left;
200
+ display: grid;
201
+ grid-template-columns: 1fr auto;
202
+ align-items: center;
203
+ gap: var(--spaceM);
75
204
  background: var(--backgroundLight);
76
205
  cursor: pointer;
77
206
  color: var(--textBody);
78
- transition: all var(--durationS) var(--bezierFastoutSlowin);
207
+ transition:
208
+ border-color var(--durationS) var(--bezierFastoutSlowin),
209
+ background-color var(--durationS) var(--bezierFastoutSlowin),
210
+ box-shadow var(--durationS) var(--bezierFastoutSlowin);
79
211
  }
80
212
 
81
213
  .caseItem:hover {
82
214
  background-color: color-mix(in lab, var(--background) 95%, transparent);
83
215
  }
84
216
 
217
+ .caseItem:focus {
218
+ outline: none;
219
+ border-color: color-mix(in lab, var(--primary) 60%, transparent);
220
+ box-shadow: 0 0 0 2px color-mix(in lab, var(--primary) 18%, transparent);
221
+ }
222
+
85
223
  .caseItem.active {
86
- background-color: color-mix(in lab, var(--background) 90%, transparent);
224
+ border-color: color-mix(in lab, var(--primary) 55%, transparent);
225
+ box-shadow:
226
+ inset 4px 0 0 color-mix(in lab, var(--primary) 55%, transparent),
227
+ 0 0 0 1px color-mix(in lab, var(--primary) 16%, transparent);
228
+ }
229
+
230
+ .caseDetails {
231
+ min-width: 0;
232
+ display: flex;
233
+ flex-direction: column;
234
+ gap: var(--spaceXS);
235
+ }
236
+
237
+ .caseNumberInput {
238
+ width: 100%;
239
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
240
+ border-radius: var(--spaceXS);
241
+ background: var(--backgroundLight);
242
+ color: var(--textBody);
243
+ font-size: var(--fontSizeBodyM);
244
+ padding: var(--spaceS) var(--spaceM);
245
+ }
246
+
247
+ .caseMetaText {
248
+ color: var(--textLight);
249
+ font-size: var(--fontSizeBodyS);
250
+ }
251
+
252
+ .confirmationBadge {
253
+ justify-self: end;
254
+ border-radius: var(--spaceXS);
255
+ padding: var(--spaceXS) var(--spaceS);
256
+ font-size: var(--fontSizeBodyS);
87
257
  font-weight: var(--fontWeightMedium);
258
+ color: var(--textBody);
259
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
260
+ white-space: nowrap;
88
261
  }
89
262
 
90
- .pagination {
263
+ .footerActions {
91
264
  display: flex;
92
265
  justify-content: space-between;
93
266
  align-items: center;
267
+ gap: var(--spaceM);
94
268
  padding: var(--spaceL);
95
269
  border-top: 1px solid color-mix(in lab, var(--text) 10%, transparent);
96
270
  background: var(--backgroundLight);
97
271
  flex-shrink: 0;
98
272
  }
99
273
 
274
+ .maintenanceActions {
275
+ display: flex;
276
+ flex-wrap: wrap;
277
+ gap: var(--spaceS);
278
+ }
279
+
280
+ .secondaryActionButton {
281
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
282
+ border-radius: var(--spaceXS);
283
+ background: var(--backgroundLight);
284
+ color: var(--textBody);
285
+ padding: var(--spaceS) var(--spaceM);
286
+ font-size: var(--fontSizeBodyS);
287
+ cursor: pointer;
288
+ transition: background-color var(--durationS) var(--bezierFastoutSlowin);
289
+ }
290
+
291
+ .secondaryActionButton:hover:not(:disabled) {
292
+ background: color-mix(in lab, var(--background) 94%, transparent);
293
+ }
294
+
295
+ .secondaryActionButton:disabled {
296
+ cursor: not-allowed;
297
+ opacity: 0.5;
298
+ }
299
+
300
+ .renameActionButton {
301
+ background: color-mix(in lab, #ffc107 16%, #ffffff);
302
+ color: #7a5a00;
303
+ border-color: color-mix(in lab, #ffc107 30%, transparent);
304
+ }
305
+
306
+ .renameActionButton:hover:not(:disabled) {
307
+ background: color-mix(in lab, #ffc107 22%, #ffffff);
308
+ border-color: color-mix(in lab, #ffc107 36%, transparent);
309
+ }
310
+
311
+ .archiveActionButton {
312
+ background: color-mix(in lab, #6c757d 15%, #ffffff);
313
+ color: #495057;
314
+ border-color: color-mix(in lab, #6c757d 28%, transparent);
315
+ }
316
+
317
+ .archiveActionButton:hover:not(:disabled) {
318
+ background: color-mix(in lab, #6c757d 21%, #ffffff);
319
+ }
320
+
321
+ .deleteActionButton {
322
+ border-color: color-mix(in lab, var(--error) 25%, transparent);
323
+ color: var(--error);
324
+ }
325
+
326
+ .deleteActionButton:hover:not(:disabled) {
327
+ background: color-mix(in lab, var(--error) 12%, var(--backgroundLight));
328
+ }
329
+
330
+ .openSelectedButton {
331
+ background: var(--primary);
332
+ color: var(--white);
333
+ border: none;
334
+ border-radius: var(--spaceXS);
335
+ padding: var(--spaceS) var(--spaceL);
336
+ font-size: var(--fontSizeBodyS);
337
+ cursor: pointer;
338
+ transition: background-color var(--durationS) var(--bezierFastoutSlowin);
339
+ }
340
+
341
+ .openSelectedButton:hover:not(:disabled) {
342
+ background: color-mix(in lab, var(--primary) 85%, var(--black));
343
+ }
344
+
345
+ .openSelectedButton:disabled {
346
+ background: color-mix(in lab, var(--text) 20%, transparent);
347
+ color: var(--textLight);
348
+ cursor: not-allowed;
349
+ }
350
+
351
+ .pagination {
352
+ display: flex;
353
+ justify-content: flex-end;
354
+ align-items: center;
355
+ gap: var(--spaceM);
356
+ }
357
+
100
358
  .pagination button {
101
359
  background: var(--primary);
102
360
  color: var(--white);
@@ -128,7 +386,7 @@
128
386
  .error,
129
387
  .emptyState {
130
388
  text-align: center;
131
- padding: var(--space2XL);
389
+ padding: var(--spaceXL);
132
390
  color: var(--textLight);
133
391
  }
134
392
 
@@ -145,6 +403,14 @@
145
403
  );
146
404
  }
147
405
 
406
+ .confirmationBadge.caseItemNotConfirmed {
407
+ background-color: color-mix(
408
+ in lab,
409
+ var(--warning) 22%,
410
+ var(--backgroundLight)
411
+ );
412
+ }
413
+
148
414
  .caseItemNotConfirmed:hover {
149
415
  background-color: color-mix(
150
416
  in lab,
@@ -177,6 +443,14 @@
177
443
  );
178
444
  }
179
445
 
446
+ .confirmationBadge.caseItemConfirmed {
447
+ background-color: color-mix(
448
+ in lab,
449
+ var(--success) 28%,
450
+ var(--backgroundLight)
451
+ );
452
+ }
453
+
180
454
  .caseItemConfirmed:hover {
181
455
  background-color: color-mix(
182
456
  in lab,
@@ -200,3 +474,31 @@
200
474
  var(--backgroundLight)
201
475
  );
202
476
  }
477
+
478
+ @media (max-width: 900px) {
479
+ .controlsSection {
480
+ grid-template-columns: repeat(2, minmax(0, 1fr));
481
+ }
482
+
483
+ .caseItem {
484
+ grid-template-columns: 1fr;
485
+ }
486
+
487
+ .confirmationBadge {
488
+ grid-column: 1 / -1;
489
+ justify-self: start;
490
+ }
491
+
492
+ .footerActions {
493
+ flex-direction: column;
494
+ align-items: stretch;
495
+ }
496
+
497
+ .maintenanceActions {
498
+ width: 100%;
499
+ }
500
+
501
+ .pagination {
502
+ justify-content: space-between;
503
+ }
504
+ }