@striae-org/striae 4.2.0 → 4.3.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 (90) hide show
  1. package/LICENSE +1 -1
  2. package/app/components/actions/case-manage.ts +50 -17
  3. package/app/components/audit/viewer/audit-entries-list.tsx +5 -2
  4. package/app/components/audit/viewer/use-audit-viewer-data.ts +6 -3
  5. package/app/components/audit/viewer/use-audit-viewer-export.ts +1 -1
  6. package/app/components/canvas/confirmation/confirmation.tsx +6 -2
  7. package/app/components/colors/colors.module.css +4 -3
  8. package/app/components/navbar/case-modals/archive-case-modal.module.css +0 -76
  9. package/app/components/navbar/case-modals/archive-case-modal.tsx +9 -8
  10. package/app/components/navbar/case-modals/case-modal-shared.module.css +94 -0
  11. package/app/components/navbar/case-modals/delete-case-modal.module.css +9 -0
  12. package/app/components/navbar/case-modals/delete-case-modal.tsx +79 -0
  13. package/app/components/navbar/case-modals/open-case-modal.module.css +2 -1
  14. package/app/components/navbar/case-modals/rename-case-modal.module.css +0 -72
  15. package/app/components/navbar/case-modals/rename-case-modal.tsx +9 -8
  16. package/app/components/navbar/navbar.tsx +34 -9
  17. package/app/components/sidebar/cases/case-sidebar.tsx +93 -73
  18. package/app/components/sidebar/cases/cases-modal.module.css +312 -10
  19. package/app/components/sidebar/cases/cases-modal.tsx +737 -116
  20. package/app/components/sidebar/cases/cases.module.css +43 -0
  21. package/app/components/sidebar/files/delete-files-modal.module.css +26 -0
  22. package/app/components/sidebar/files/delete-files-modal.tsx +94 -0
  23. package/app/components/sidebar/files/files-modal.module.css +285 -44
  24. package/app/components/sidebar/files/files-modal.tsx +482 -177
  25. package/app/components/sidebar/notes/addl-notes-modal.tsx +82 -0
  26. package/app/components/sidebar/notes/class-details-fields.tsx +146 -0
  27. package/app/components/sidebar/notes/class-details-modal.tsx +147 -0
  28. package/app/components/sidebar/notes/class-details-sections.tsx +561 -0
  29. package/app/components/sidebar/notes/class-details-shared.ts +239 -0
  30. package/app/components/sidebar/notes/{notes-sidebar.tsx → notes-editor-form.tsx} +77 -76
  31. package/app/components/sidebar/notes/notes-editor-modal.tsx +5 -7
  32. package/app/components/sidebar/notes/notes.module.css +262 -14
  33. package/app/components/sidebar/notes/use-class-details-state.ts +371 -0
  34. package/app/components/sidebar/sidebar-container.tsx +2 -0
  35. package/app/components/sidebar/sidebar.tsx +15 -1
  36. package/app/{tailwind.css → global.css} +1 -3
  37. package/app/hooks/useCaseListPreferences.ts +99 -0
  38. package/app/hooks/useFileListPreferences.ts +106 -0
  39. package/app/hooks/useOverlayDismiss.ts +6 -4
  40. package/app/root.tsx +1 -1
  41. package/app/routes/striae/striae.tsx +7 -0
  42. package/app/services/audit/audit.service.ts +2 -2
  43. package/app/services/audit/builders/audit-event-builders-case-file.ts +1 -1
  44. package/app/types/annotations.ts +48 -1
  45. package/app/types/audit.ts +1 -0
  46. package/app/utils/data/case-filters.ts +127 -0
  47. package/app/utils/data/confirmation-summary/summary-core.ts +295 -0
  48. package/app/utils/data/data-operations.ts +17 -861
  49. package/app/utils/data/file-filters.ts +201 -0
  50. package/app/utils/data/index.ts +11 -1
  51. package/app/utils/data/operations/batch-operations.ts +113 -0
  52. package/app/utils/data/operations/case-operations.ts +168 -0
  53. package/app/utils/data/operations/confirmation-summary-operations.ts +301 -0
  54. package/app/utils/data/operations/file-annotation-operations.ts +196 -0
  55. package/app/utils/data/operations/index.ts +7 -0
  56. package/app/utils/data/operations/signing-operations.ts +225 -0
  57. package/app/utils/data/operations/types.ts +42 -0
  58. package/app/utils/data/operations/validation-operations.ts +48 -0
  59. package/app/utils/forensics/export-verification.ts +40 -111
  60. package/functions/api/_shared/firebase-auth.ts +2 -7
  61. package/functions/api/image/[[path]].ts +23 -22
  62. package/functions/api/pdf/[[path]].ts +27 -8
  63. package/package.json +7 -13
  64. package/scripts/deploy-primershear-emails.sh +1 -1
  65. package/worker-configuration.d.ts +2 -2
  66. package/workers/audit-worker/package.json +1 -1
  67. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  68. package/workers/data-worker/package.json +1 -1
  69. package/workers/data-worker/wrangler.jsonc.example +1 -1
  70. package/workers/image-worker/package.json +1 -1
  71. package/workers/image-worker/src/image-worker.example.ts +16 -5
  72. package/workers/image-worker/wrangler.jsonc.example +1 -1
  73. package/workers/keys-worker/package.json +1 -1
  74. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  75. package/workers/pdf-worker/package.json +1 -1
  76. package/workers/pdf-worker/src/formats/format-striae.ts +84 -124
  77. package/workers/pdf-worker/src/pdf-worker.example.ts +58 -61
  78. package/workers/pdf-worker/src/report-layout.ts +227 -0
  79. package/workers/pdf-worker/src/report-types.ts +23 -3
  80. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  81. package/workers/user-worker/package.json +1 -1
  82. package/workers/user-worker/src/user-worker.example.ts +17 -0
  83. package/workers/user-worker/wrangler.jsonc.example +1 -1
  84. package/wrangler.toml.example +1 -1
  85. package/NOTICE +0 -13
  86. package/app/components/sidebar/notes/notes-modal.tsx +0 -52
  87. package/postcss.config.js +0 -6
  88. package/tailwind.config.ts +0 -22
  89. package/workers/pdf-worker/src/assets/icon-256.png +0 -0
  90. /package/workers/pdf-worker/src/assets/{generated-assets.ts → generated-assets.example.ts} +0 -0
@@ -204,6 +204,24 @@
204
204
  margin-top: 0.75rem;
205
205
  }
206
206
 
207
+ .openCaseButton {
208
+ width: 100%;
209
+ padding: 0.75rem 1rem;
210
+ background-color: var(--primary);
211
+ color: white;
212
+ border: none;
213
+ border-radius: 6px;
214
+ font-weight: 600;
215
+ font-size: 0.95rem;
216
+ cursor: pointer;
217
+ transition: all 0.2s;
218
+ box-sizing: border-box;
219
+ }
220
+
221
+ .openCaseButton:hover {
222
+ background-color: color-mix(in lab, var(--primary) 85%, var(--black));
223
+ }
224
+
207
225
  .fileListPlaceholder {
208
226
  display: flex;
209
227
  align-items: center;
@@ -471,6 +489,29 @@
471
489
  cursor: not-allowed;
472
490
  }
473
491
 
492
+ .confirmationExportButton {
493
+ width: 100%;
494
+ padding: 0.625rem 0.75rem;
495
+ background-color: #198754;
496
+ color: white;
497
+ border: none;
498
+ border-radius: 6px;
499
+ font-weight: 600;
500
+ font-size: 0.9rem;
501
+ cursor: pointer;
502
+ transition: all 0.2s;
503
+ }
504
+
505
+ .confirmationExportButton:hover:not(:disabled) {
506
+ background-color: #146c43;
507
+ }
508
+
509
+ .confirmationExportButton:disabled {
510
+ background-color: var(--backgroundLight);
511
+ color: var(--textLight);
512
+ cursor: not-allowed;
513
+ }
514
+
474
515
  /* Case Actions Section */
475
516
  .caseActionsSection {
476
517
  margin-top: 1.5rem;
@@ -678,6 +719,7 @@
678
719
  /* Case Header Container */
679
720
  .caseHeader {
680
721
  /* Normal case header styling (no background) */
722
+ margin-top: 0.75rem;
681
723
  }
682
724
 
683
725
  .readOnlyContainer {
@@ -685,6 +727,7 @@
685
727
  border: 1px solid #ffeaa7;
686
728
  border-radius: 4px;
687
729
  padding: 0.625rem 0.75rem;
730
+ margin-top: 0.75rem;
688
731
  margin-bottom: 0;
689
732
  }
690
733
 
@@ -0,0 +1,26 @@
1
+ .modal {
2
+ width: min(620px, calc(100vw - 2rem));
3
+ }
4
+
5
+ .filePreviewList {
6
+ margin: 0.6rem 0 0;
7
+ padding-left: 1.1rem;
8
+ color: #3f2a2e;
9
+ font-size: 0.86rem;
10
+ }
11
+
12
+ .filePreviewList li + li {
13
+ margin-top: 0.3rem;
14
+ }
15
+
16
+ .remainingNote {
17
+ margin-top: 0.5rem;
18
+ font-size: 0.84rem;
19
+ color: #6b2f35;
20
+ }
21
+
22
+ .confirmButton {
23
+ background: #dc3545;
24
+ color: #ffffff;
25
+ border-color: #c82333;
26
+ }
@@ -0,0 +1,94 @@
1
+ import { useMemo } from 'react';
2
+ import { useOverlayDismiss } from '~/hooks/useOverlayDismiss';
3
+ import { type FileData } from '~/types';
4
+ import sharedStyles from '~/components/navbar/case-modals/case-modal-shared.module.css';
5
+ import styles from './delete-files-modal.module.css';
6
+
7
+ interface DeleteFilesModalProps {
8
+ isOpen: boolean;
9
+ isSubmitting?: boolean;
10
+ files: FileData[];
11
+ selectedFileIds: Set<string>;
12
+ onClose: () => void;
13
+ onSubmit: () => Promise<void>;
14
+ }
15
+
16
+ export const DeleteFilesModal = ({
17
+ isOpen,
18
+ isSubmitting = false,
19
+ files,
20
+ selectedFileIds,
21
+ onClose,
22
+ onSubmit,
23
+ }: DeleteFilesModalProps) => {
24
+ const selectedFiles = useMemo(
25
+ () => files.filter((file) => selectedFileIds.has(file.id)),
26
+ [files, selectedFileIds]
27
+ );
28
+
29
+ const previewFiles = selectedFiles.slice(0, 5);
30
+ const remainingCount = Math.max(0, selectedFiles.length - previewFiles.length);
31
+
32
+ const {
33
+ requestClose,
34
+ overlayProps,
35
+ getCloseButtonProps,
36
+ } = useOverlayDismiss({
37
+ isOpen,
38
+ onClose,
39
+ canDismiss: !isSubmitting,
40
+ });
41
+
42
+ if (!isOpen) {
43
+ return null;
44
+ }
45
+
46
+ return (
47
+ <div className={sharedStyles.overlay} aria-label="Close delete files dialog" {...overlayProps}>
48
+ <div className={`${sharedStyles.modal} ${styles.modal}`} role="dialog" aria-modal="true" aria-label="Delete Selected Files">
49
+ <button {...getCloseButtonProps({ ariaLabel: 'Close delete files dialog' })}>×</button>
50
+
51
+ <h3 className={sharedStyles.title}>Delete Selected Files</h3>
52
+ <p className={sharedStyles.subtitle}>
53
+ {selectedFiles.length} file{selectedFiles.length === 1 ? '' : 's'} selected
54
+ </p>
55
+
56
+ <div className={sharedStyles.warningPanel}>
57
+ <p>This action permanently deletes the selected files and their annotation data.</p>
58
+ <p>This operation cannot be undone.</p>
59
+ {previewFiles.length > 0 && (
60
+ <ul className={styles.filePreviewList}>
61
+ {previewFiles.map((file) => (
62
+ <li key={file.id}>{file.originalFilename}</li>
63
+ ))}
64
+ </ul>
65
+ )}
66
+ {remainingCount > 0 && (
67
+ <p className={styles.remainingNote}>and {remainingCount} more...</p>
68
+ )}
69
+ </div>
70
+
71
+ <div className={sharedStyles.actions}>
72
+ <button
73
+ type="button"
74
+ className={sharedStyles.cancelButton}
75
+ onClick={requestClose}
76
+ disabled={isSubmitting}
77
+ >
78
+ Cancel
79
+ </button>
80
+ <button
81
+ type="button"
82
+ className={`${sharedStyles.confirmButton} ${styles.confirmButton}`}
83
+ onClick={() => {
84
+ void onSubmit();
85
+ }}
86
+ disabled={isSubmitting || selectedFiles.length === 0}
87
+ >
88
+ {isSubmitting ? 'Deleting...' : `Delete ${selectedFiles.length} File${selectedFiles.length === 1 ? '' : 's'}`}
89
+ </button>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ );
94
+ };
@@ -14,9 +14,10 @@
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
- max-height: 80vh;
20
+ max-height: 84vh;
20
21
  box-shadow: 0 var(--spaceXS) var(--spaceL)
21
22
  color-mix(in lab, var(--black) 10%, transparent);
22
23
  cursor: default;
@@ -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,6 +61,114 @@
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
+ .resetButton {
99
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
100
+ border-radius: var(--spaceXS);
101
+ background: var(--backgroundLight);
102
+ color: var(--textBody);
103
+ padding: var(--spaceS) var(--spaceM);
104
+ font-size: var(--fontSizeBodyS);
105
+ cursor: pointer;
106
+ }
107
+
108
+ .resetButton:disabled {
109
+ cursor: not-allowed;
110
+ opacity: 0.5;
111
+ }
112
+
113
+ .searchSection {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: var(--spaceXS);
117
+ }
118
+
119
+ .searchSection label {
120
+ color: var(--textTitle);
121
+ font-size: var(--fontSizeBodyS);
122
+ font-weight: var(--fontWeightMedium);
123
+ }
124
+
125
+ .searchInput {
126
+ width: 100%;
127
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
128
+ border-radius: var(--spaceXS);
129
+ background: var(--backgroundLight);
130
+ color: var(--textBody);
131
+ padding: var(--spaceS) var(--spaceM);
132
+ font-size: var(--fontSizeBodyS);
133
+ }
134
+
135
+ .searchInput:focus {
136
+ outline: 2px solid color-mix(in lab, var(--primary) 30%, transparent);
137
+ outline-offset: 1px;
138
+ }
139
+
140
+ .fileCount {
141
+ margin: 0;
142
+ font-size: var(--fontSizeBodyS);
143
+ color: var(--textLight);
144
+ }
145
+
146
+ .actionNotice {
147
+ margin: 0;
148
+ border-radius: var(--spaceXS);
149
+ padding: var(--spaceS) var(--spaceM);
150
+ font-size: var(--fontSizeBodyS);
151
+ border: 1px solid transparent;
152
+ }
153
+
154
+ .actionNoticeSuccess {
155
+ background: color-mix(in lab, var(--success) 15%, var(--backgroundLight));
156
+ color: var(--textBody);
157
+ border-color: color-mix(in lab, var(--success) 35%, transparent);
158
+ }
159
+
160
+ .actionNoticeWarning {
161
+ background: color-mix(in lab, var(--warning) 18%, var(--backgroundLight));
162
+ color: var(--textBody);
163
+ border-color: color-mix(in lab, var(--warning) 40%, transparent);
164
+ }
165
+
166
+ .actionNoticeError {
167
+ background: color-mix(in lab, var(--error) 16%, var(--backgroundLight));
168
+ color: var(--error);
169
+ border-color: color-mix(in lab, var(--error) 30%, transparent);
170
+ }
171
+
60
172
  .filesList {
61
173
  list-style: none;
62
174
  padding: 0;
@@ -67,36 +179,70 @@
67
179
  }
68
180
 
69
181
  .fileItem {
70
- display: flex;
71
- justify-content: space-between;
72
- align-items: center;
73
- padding: var(--spaceM) var(--spaceL);
74
- border: 1px solid #e9ecef;
182
+ width: 100%;
183
+ padding: var(--spaceM);
184
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
75
185
  border-radius: var(--spaceXS);
186
+ display: grid;
187
+ grid-template-columns: auto 1fr auto;
188
+ align-items: center;
189
+ gap: var(--spaceM);
76
190
  background: var(--backgroundLight);
77
191
  cursor: pointer;
78
- transition: all var(--durationS) var(--bezierFastoutSlowin);
192
+ color: var(--textBody);
193
+ transition:
194
+ border-color var(--durationS) var(--bezierFastoutSlowin),
195
+ background-color var(--durationS) var(--bezierFastoutSlowin),
196
+ box-shadow var(--durationS) var(--bezierFastoutSlowin);
79
197
  }
80
198
 
81
199
  .fileItem:hover {
82
200
  background-color: color-mix(in lab, var(--background) 95%, transparent);
83
201
  }
84
202
 
203
+ .fileItem:focus {
204
+ outline: none;
205
+ border-color: color-mix(in lab, var(--primary) 60%, transparent);
206
+ box-shadow: 0 0 0 2px color-mix(in lab, var(--primary) 18%, transparent);
207
+ }
208
+
85
209
  .fileItem.active {
86
- background-color: color-mix(in lab, var(--background) 90%, transparent);
87
- font-weight: var(--fontWeightMedium);
210
+ border-color: color-mix(in lab, var(--primary) 55%, transparent);
211
+ box-shadow:
212
+ inset 4px 0 0 color-mix(in lab, var(--primary) 55%, transparent),
213
+ 0 0 0 1px color-mix(in lab, var(--primary) 16%, transparent);
214
+ }
215
+
216
+ .deleteSelector {
217
+ margin: 0;
218
+ width: var(--spaceL);
219
+ height: var(--spaceL);
220
+ cursor: pointer;
221
+ }
222
+
223
+ .deleteSelector {
224
+ accent-color: var(--error);
88
225
  }
89
226
 
90
227
  .fileInfo {
91
- flex: 1;
92
- min-width: 0; /* Allow text truncation */
228
+ min-width: 0;
229
+ display: flex;
230
+ flex-direction: column;
231
+ gap: var(--spaceXS);
93
232
  }
94
233
 
95
234
  .fileName {
96
235
  font-weight: var(--fontWeightMedium);
97
236
  color: var(--textBody);
98
- margin-bottom: var(--spaceXS);
99
- word-break: break-word;
237
+ overflow: hidden;
238
+ text-overflow: ellipsis;
239
+ white-space: nowrap;
240
+ }
241
+
242
+ .fileMetaRow {
243
+ display: flex;
244
+ gap: var(--spaceM);
245
+ flex-wrap: wrap;
100
246
  }
101
247
 
102
248
  .fileDate {
@@ -104,53 +250,98 @@
104
250
  color: var(--textLight);
105
251
  }
106
252
 
107
- .deleteButton {
108
- background: none;
109
- border: none;
110
- color: #dc3545;
111
- font-size: 1.2rem;
112
- cursor: pointer;
113
- padding: 0.5rem;
253
+ .classTypeBadge {
114
254
  border-radius: var(--spaceXS);
115
- margin-left: var(--spaceM);
116
- transition: all var(--durationS) var(--bezierFastoutSlowin);
117
- flex-shrink: 0;
255
+ border: 1px solid color-mix(in lab, var(--text) 14%, transparent);
256
+ padding: 0.1rem 0.45rem;
257
+ font-size: var(--fontSizeBodyS);
258
+ color: var(--textBody);
259
+ background: color-mix(in lab, var(--background) 94%, transparent);
260
+ }
261
+
262
+ .confirmationBadge {
263
+ justify-self: end;
264
+ border-radius: var(--spaceXS);
265
+ padding: var(--spaceXS) var(--spaceS);
266
+ font-size: var(--fontSizeBodyS);
267
+ font-weight: var(--fontWeightMedium);
268
+ color: var(--textBody);
269
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
270
+ white-space: nowrap;
271
+ }
272
+
273
+ .footerActions {
118
274
  display: flex;
275
+ justify-content: space-between;
119
276
  align-items: center;
120
- justify-content: center;
121
- min-width: 32px;
122
- height: 32px;
277
+ gap: var(--spaceM);
278
+ padding: var(--spaceL);
279
+ border-top: 1px solid color-mix(in lab, var(--text) 10%, transparent);
280
+ background: var(--backgroundLight);
281
+ flex-shrink: 0;
123
282
  }
124
283
 
125
- .deleteButton:hover:not(:disabled) {
126
- color: #bd2130;
127
- background-color: rgba(220, 53, 69, 0.1);
284
+ .maintenanceActions {
285
+ display: flex;
286
+ flex-wrap: wrap;
287
+ gap: var(--spaceS);
128
288
  }
129
289
 
130
- .deleteButton:disabled {
131
- opacity: 0.5;
132
- cursor: not-allowed;
290
+ .secondaryActionButton {
291
+ border: 1px solid color-mix(in lab, var(--text) 15%, transparent);
292
+ border-radius: var(--spaceXS);
293
+ background: var(--backgroundLight);
294
+ color: var(--textBody);
295
+ padding: var(--spaceS) var(--spaceM);
296
+ font-size: var(--fontSizeBodyS);
297
+ cursor: pointer;
298
+ transition: background-color var(--durationS) var(--bezierFastoutSlowin);
133
299
  }
134
300
 
135
- .errorState,
136
- .emptyState {
137
- text-align: center;
138
- padding: var(--space2XL);
139
- color: var(--textLight);
301
+ .secondaryActionButton:hover:not(:disabled) {
302
+ background: color-mix(in lab, var(--background) 94%, transparent);
140
303
  }
141
304
 
142
- .errorState {
305
+ .secondaryActionButton:disabled {
306
+ cursor: not-allowed;
307
+ opacity: 0.5;
308
+ }
309
+
310
+ .deleteActionButton {
311
+ border-color: color-mix(in lab, var(--error) 25%, transparent);
143
312
  color: var(--error);
144
313
  }
145
314
 
315
+ .deleteActionButton:hover:not(:disabled) {
316
+ background: color-mix(in lab, var(--error) 12%, var(--backgroundLight));
317
+ }
318
+
319
+ .openSelectedButton {
320
+ background: var(--primary);
321
+ color: var(--white);
322
+ border: none;
323
+ border-radius: var(--spaceXS);
324
+ padding: var(--spaceS) var(--spaceL);
325
+ font-size: var(--fontSizeBodyS);
326
+ cursor: pointer;
327
+ transition: background-color var(--durationS) var(--bezierFastoutSlowin);
328
+ }
329
+
330
+ .openSelectedButton:hover:not(:disabled) {
331
+ background: color-mix(in lab, var(--primary) 85%, var(--black));
332
+ }
333
+
334
+ .openSelectedButton:disabled {
335
+ background: color-mix(in lab, var(--text) 20%, transparent);
336
+ color: var(--textLight);
337
+ cursor: not-allowed;
338
+ }
339
+
146
340
  .pagination {
147
341
  display: flex;
148
- justify-content: space-between;
342
+ justify-content: flex-end;
149
343
  align-items: center;
150
- padding: var(--spaceL);
151
- border-top: 1px solid color-mix(in lab, var(--text) 10%, transparent);
152
- background: var(--backgroundLight);
153
- flex-shrink: 0;
344
+ gap: var(--spaceM);
154
345
  }
155
346
 
156
347
  .pagination button {
@@ -179,7 +370,13 @@
179
370
  color: var(--textBody);
180
371
  font-weight: var(--fontWeightMedium);
181
372
  }
182
- /* Confirmation Status Indicators */
373
+
374
+ .emptyState {
375
+ text-align: center;
376
+ padding: var(--spaceXL);
377
+ color: var(--textLight);
378
+ }
379
+
183
380
  .fileItemNotConfirmed {
184
381
  background-color: color-mix(
185
382
  in lab,
@@ -212,6 +409,14 @@
212
409
  );
213
410
  }
214
411
 
412
+ .confirmationBadge.fileItemNotConfirmed {
413
+ background-color: color-mix(
414
+ in lab,
415
+ var(--warning) 22%,
416
+ var(--backgroundLight)
417
+ );
418
+ }
419
+
215
420
  .fileItemConfirmed {
216
421
  background-color: color-mix(
217
422
  in lab,
@@ -243,3 +448,39 @@
243
448
  var(--backgroundLight)
244
449
  );
245
450
  }
451
+
452
+ .confirmationBadge.fileItemConfirmed {
453
+ background-color: color-mix(
454
+ in lab,
455
+ var(--success) 28%,
456
+ var(--backgroundLight)
457
+ );
458
+ }
459
+
460
+ @media (max-width: 980px) {
461
+ .controlsSection {
462
+ grid-template-columns: repeat(2, minmax(0, 1fr));
463
+ }
464
+
465
+ .fileItem {
466
+ grid-template-columns: auto 1fr;
467
+ }
468
+
469
+ .confirmationBadge {
470
+ grid-column: 1 / -1;
471
+ justify-self: start;
472
+ }
473
+
474
+ .footerActions {
475
+ flex-direction: column;
476
+ align-items: stretch;
477
+ }
478
+
479
+ .maintenanceActions {
480
+ width: 100%;
481
+ }
482
+
483
+ .pagination {
484
+ justify-content: space-between;
485
+ }
486
+ }