@7365admin1/layer-common 3.2.2-staging.120 → 3.2.2-staging.122

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.
@@ -1,53 +1,28 @@
1
+ <!--
2
+ HID USER ENROLLMENT - the people a reader will recognise.
3
+
4
+ Was a bespoke screen: an above-card toolbar, a raw `v-table` with six fixed
5
+ column widths, its own refresh strip, footer pager, empty state and three
6
+ private `status-*` chip palettes - about twenty-six literal hexes. Those
7
+ literals are why the screen had no dark theme; `#0a2638` on the dark card
8
+ measures 1.07:1.
9
+
10
+ It is the design's standard table screen, so it is drawn with the shared one:
11
+ `TableMain` supplies the page heading, the card, the in-card toolbar, the
12
+ uppercase header row, the row spec and the pager; the reader picker, search
13
+ and status filter move onto the card's `extension` row. Every column, action,
14
+ dialog and handler is unchanged - the same `users` still feed it and the
15
+ overflow menu still opens the same three dialogs.
16
+
17
+ The handoff draws no HID table, so it states no column proportions - the
18
+ table takes `TableMain`'s automatic layout, as panel 2 does, and the screen's
19
+ own fixed 24/20/12/20/16/8 % are dropped rather than carried over.
20
+ -->
1
21
  <template>
2
22
  <section class="hid-user-enrollment">
3
- <div class="toolbar">
4
- <v-btn
5
- rounded="xl"
6
- class="text-none enroll-button"
7
- variant="flat"
8
- prepend-icon="mdi-account-plus"
9
- @click="openEnroll"
10
- >
11
- Enroll
12
- </v-btn>
13
-
14
- <v-select
15
- v-if="readers.length"
16
- v-model="selectedReaderId"
17
- :items="readerOptions"
18
- item-title="title"
19
- item-value="value"
20
- variant="outlined"
21
- density="compact"
22
- hide-details
23
- class="reader-input"
24
- @update:model-value="loadUsers"
25
- />
26
-
27
- <div class="toolbar-spacer" />
28
-
29
- <v-text-field
30
- v-model="search"
31
- placeholder="Search"
32
- variant="outlined"
33
- density="compact"
34
- hide-details
35
- class="search-input"
36
- prepend-inner-icon="mdi-magnify"
37
- @keyup.enter="loadUsers"
38
- />
39
-
40
- <v-select
41
- v-model="status"
42
- :items="statusOptions"
43
- variant="outlined"
44
- density="compact"
45
- hide-details
46
- class="status-input"
47
- @update:model-value="loadUsers"
48
- />
49
- </div>
50
-
23
+ <!-- Staging's portal banner (1e6224e), kept whole: it names the portal the
24
+ selected reader is bound to and warns when it has none. It sat above
25
+ the old table card, so it stays above the shared one. -->
51
26
  <v-alert
52
27
  v-if="selectedReader"
53
28
  :type="selectedReader.portalId && selectedReader.portalName ? 'info' : 'warning'"
@@ -60,112 +35,106 @@
60
35
  {{ selectedReader.portalName || "Not configured" }}
61
36
  </v-alert>
62
37
 
63
- <v-card flat border class="table-card">
64
- <div class="table-refresh">
65
- <v-btn
66
- icon="mdi-refresh"
67
- variant="text"
68
- density="comfortable"
69
- :loading="loading"
70
- @click="loadUsers"
71
- />
72
- <div class="page-count">{{ pageRange }}</div>
73
- </div>
74
-
75
- <div class="table-body">
76
- <v-table>
77
- <thead>
78
- <tr>
79
- <th>Name</th>
80
- <th>Facial Data</th>
81
- <th>UID</th>
82
- <th>Status</th>
83
- <th class="text-right"></th>
84
- </tr>
85
- </thead>
86
- <tbody>
87
- <tr
88
- v-for="user in users"
89
- :key="user._rowKey || user._id || user.hidUserId"
90
- >
91
- <td>{{ getName(user) }}</td>
92
- <td>
93
- <v-avatar
94
- v-if="getUserImageSrc(user)"
95
- size="34"
96
- rounded="lg"
97
- class="hid-face-avatar"
98
- >
99
- <v-img :src="getUserImageSrc(user)" cover />
100
- </v-avatar>
101
- <span v-else>{{ getFacialData(user) }}</span>
102
- </td>
103
- <td>{{ formatHidUid(user.hidUserId) || "N/A" }}</td>
104
- <td>
105
- <v-chip
106
- size="small"
107
- variant="flat"
108
- class="status-chip"
109
- :class="getMappingStatusClass(user)"
110
- >
111
- {{ getMappingStatus(user) }}
112
- </v-chip>
113
- </td>
114
- <td class="text-right">
115
- <v-menu>
116
- <template #activator="{ props: menuProps }">
117
- <v-btn
118
- v-bind="menuProps"
119
- icon="mdi-dots-vertical"
120
- variant="text"
121
- density="comfortable"
122
- />
123
- </template>
124
-
125
- <v-list density="compact" min-width="150">
126
- <v-list-item title="View" @click="openView(user)" />
127
- <v-list-item title="Edit" @click="openEdit(user)" />
128
- <v-list-item
129
- title="Delete"
130
- class="text-error"
131
- @click="openDelete(user)"
132
- />
133
- </v-list>
134
- </v-menu>
135
- </td>
136
- </tr>
137
- </tbody>
138
- </v-table>
139
-
140
- <div v-if="!users.length" class="empty-state">
141
- No HID users enrolled yet.
38
+ <TableMain
39
+ title="HID Users"
40
+ :headers="userHeaders"
41
+ :items="users"
42
+ :loading="loading"
43
+ :items-per-page="-1"
44
+ :page="page"
45
+ :pages="pages"
46
+ :page-range="pageRange"
47
+ item-value="_rowKey"
48
+ show-header
49
+ @refresh="loadUsers"
50
+ @update:page="goToPage"
51
+ >
52
+ <template #actions>
53
+ <AppButton icon="mdi-account-plus" @click="openEnroll">Enroll</AppButton>
54
+ </template>
55
+
56
+ <!-- The design puts filters on their own full-width row under the
57
+ toolbar, which is what `extension` is. -->
58
+ <template #extension>
59
+ <div class="app-filter-row hid-user-filters">
60
+ <AppSelect
61
+ v-if="readers.length"
62
+ v-model="selectedReaderId"
63
+ class="reader-select"
64
+ :items="readerOptions"
65
+ @update:model-value="loadUsers"
66
+ />
67
+ <AppField
68
+ v-model="search"
69
+ search
70
+ placeholder="Search"
71
+ @keyup.enter="loadUsers"
72
+ />
73
+ <AppSelect
74
+ v-model="status"
75
+ :items="statusOptions"
76
+ @update:model-value="loadUsers"
77
+ />
78
+ </div>
79
+ </template>
80
+
81
+ <template #[`item.name`]="{ item }">
82
+ <span class="app-cell--strong">{{ getName(item) }}</span>
83
+ </template>
84
+
85
+ <!-- The face thumbnail, unchanged. It prints whatever `getUserImageSrc`
86
+ already returned; this screen does not read or export a template. -->
87
+ <template #[`item.facialData`]="{ item }">
88
+ <v-avatar
89
+ v-if="getUserImageSrc(item)"
90
+ size="34"
91
+ rounded="lg"
92
+ class="hid-face-avatar"
93
+ >
94
+ <v-img :src="getUserImageSrc(item)" cover />
95
+ </v-avatar>
96
+ <span v-else class="app-cell--muted">{{ getFacialData(item) }}</span>
97
+ </template>
98
+
99
+ <template #[`item.hidUserId`]="{ item }">
100
+ <span class="app-cell--num">{{ formatHidUid(item.hidUserId) || "N/A" }}</span>
101
+ </template>
102
+
103
+ <!-- The shared chip. `utils/status.ts` now carries Mapped/Unmapped with
104
+ the same tones this screen's private `getMappingStatusClass` gave
105
+ them, so the map is one list rather than a fourth private copy. -->
106
+ <template #[`item.status`]="{ item }">
107
+ <StatusChip :status="getMappingStatus(item)" />
108
+ </template>
109
+
110
+ <template #[`item.action-table`]="{ item }">
111
+ <v-menu>
112
+ <template #activator="{ props: menuProps }">
113
+ <AppButton v-bind="menuProps" variant="row" icon="mdi-dots-vertical" />
114
+ </template>
115
+
116
+ <v-list density="compact" min-width="150">
117
+ <v-list-item title="View" @click="openView(item)" />
118
+ <v-list-item title="Edit" @click="openEdit(item)" />
119
+ <v-list-item
120
+ title="Delete"
121
+ class="text-error"
122
+ @click="openDelete(item)"
123
+ />
124
+ </v-list>
125
+ </v-menu>
126
+ </template>
127
+
128
+ <!-- The screen's own sentence, kept: the shared empty state says "No data
129
+ available", which is less useful on a screen whose whole point is
130
+ that nobody has been enrolled yet. -->
131
+ <template #no-data>
132
+ <div class="table-card__empty">
133
+ <v-icon icon="mdi-account-off-outline" size="32" />
134
+ <span>No HID users enrolled yet.</span>
142
135
  </div>
143
- </div>
144
-
145
- <div class="table-footer">
146
- <span>{{ pageRange }}</span>
147
- <v-btn
148
- icon="mdi-chevron-left"
149
- variant="text"
150
- density="comfortable"
151
- :disabled="page <= 1"
152
- @click="
153
- page--;
154
- loadUsers();
155
- "
156
- />
157
- <v-btn
158
- icon="mdi-chevron-right"
159
- variant="text"
160
- density="comfortable"
161
- :disabled="page >= pages"
162
- @click="
163
- page++;
164
- loadUsers();
165
- "
166
- />
167
- </div>
168
- </v-card>
136
+ </template>
137
+ </TableMain>
169
138
 
170
139
  <v-dialog v-model="formDialog" max-width="420" persistent>
171
140
  <v-card rounded="lg">
@@ -188,7 +157,9 @@
188
157
  <v-icon icon="mdi-pencil" size="16" />
189
158
  </span>
190
159
  <template v-else>
191
- <v-icon icon="mdi-account-plus" size="40" color="#a4acb5" />
160
+ <!-- Was `color="#a4acb5"`, the same grey on both themes.
161
+ `.photo-button` already sets `--muted`, so it inherits. -->
162
+ <v-icon icon="mdi-account-plus" size="40" />
192
163
  <span>Add Photo</span>
193
164
  </template>
194
165
  </button>
@@ -217,55 +188,57 @@
217
188
  </div>
218
189
 
219
190
  <div class="field-label">Name <span>*</span></div>
220
- <v-text-field
191
+ <AppField
221
192
  v-model="form.name"
222
193
  aria-label="Name"
223
194
  placeholder="Enter name"
224
- density="compact"
225
- variant="outlined"
226
- hide-details="auto"
227
- class="mb-3"
195
+ klass="mb-3"
228
196
  />
229
197
 
230
198
  <div class="field-label">ID</div>
231
- <v-text-field
232
- v-model="form.hidUserId"
233
- aria-label="ID"
234
- density="compact"
235
- variant="outlined"
236
- hide-details="auto"
237
- readonly
238
- class="mb-3"
239
- />
199
+ <AppField v-model="form.hidUserId" aria-label="ID" readonly klass="mb-3" />
240
200
 
241
201
  <div class="field-label">Registration No. <span>*</span></div>
242
- <v-text-field
202
+ <AppField
243
203
  v-model="form.registration"
244
204
  aria-label="Registration No."
245
205
  placeholder="Enter registration no."
246
- density="compact"
247
- variant="outlined"
248
- hide-details="auto"
249
206
  :readonly="!selectedUser"
250
- class="mb-3"
207
+ klass="mb-3"
251
208
  />
252
209
 
253
210
  <div class="field-label">Access PIN (optional)</div>
254
- <v-text-field
211
+ <!--
212
+ The handoff draws no password field anywhere (the sign-in screens
213
+ are the owner's own), so the reveal toggle is this product's, not
214
+ the design's. It goes in `AppField`'s `trailing` slot so the eye
215
+ sits INSIDE the 40px box as it did inside the Vuetify field, rather
216
+ than becoming a second control beside it.
217
+ -->
218
+ <AppField
255
219
  v-model="form.accessPin"
256
220
  aria-label="Access PIN"
257
221
  placeholder="Enter numeric access PIN"
258
- density="compact"
259
- variant="outlined"
260
222
  inputmode="numeric"
261
223
  autocomplete="new-password"
262
224
  :type="showPin ? 'text' : 'password'"
263
- :append-inner-icon="showPin ? 'mdi-eye-off' : 'mdi-eye'"
264
- :hint="selectedUser && pinEnrolled ? 'Leave blank to keep the current PIN.' : 'PIN works as an alternative to face or QR.'"
265
- persistent-hint
266
- @click:append-inner="showPin = !showPin"
267
- class="mb-3"
268
- />
225
+ >
226
+ <template #trailing>
227
+ <button
228
+ type="button"
229
+ class="app-field__trailing"
230
+ :aria-label="showPin ? 'Hide access PIN' : 'Show access PIN'"
231
+ @click="showPin = !showPin"
232
+ >
233
+ <v-icon :icon="showPin ? 'mdi-eye-off' : 'mdi-eye'" size="18" />
234
+ </button>
235
+ </template>
236
+ </AppField>
237
+ <!-- `persistent-hint` was Vuetify's; the design's settings module
238
+ draws an explanatory line as 12-13px/600 in `--muted`. -->
239
+ <p class="field-hint">
240
+ {{ selectedUser && pinEnrolled ? "Leave blank to keep the current PIN." : "PIN works as an alternative to face or QR." }}
241
+ </p>
269
242
  <v-checkbox
270
243
  v-if="selectedUser && pinEnrolled"
271
244
  v-model="removePinRequested"
@@ -275,6 +248,9 @@
275
248
  />
276
249
  </v-card-text>
277
250
 
251
+ <!-- The split footer bar, as panel 2 keeps it: `v-btn` because it needs
252
+ `:loading` and a full-bleed shape the handoff does not draw. Only
253
+ the colours change - they were fixed hexes on both themes. -->
278
254
  <v-card-actions class="pa-0">
279
255
  <v-btn
280
256
  class="text-none action-cancel"
@@ -324,9 +300,7 @@
324
300
  </v-card-text>
325
301
  <v-card-actions>
326
302
  <v-spacer />
327
- <v-btn class="text-none" variant="text" @click="viewDialog = false"
328
- >Close</v-btn
329
- >
303
+ <AppButton variant="ghost" @click="viewDialog = false">Close</AppButton>
330
304
  </v-card-actions>
331
305
  </v-card>
332
306
  </v-dialog>
@@ -439,6 +413,30 @@ const form = reactive({
439
413
 
440
414
  const statusOptions = ["Status", "Mapped", "Unmapped"];
441
415
 
416
+ /**
417
+ * `sortable: false` keeps the previous behaviour - the raw `v-table` never
418
+ * sorted - and no `weight` is set, so the table uses `TableMain`'s automatic
419
+ * layout. The handoff draws no HID table, so the screen's own fixed
420
+ * 24/20/12/20/16/8 % were a guess with no design number behind them.
421
+ */
422
+ const userHeaders = [
423
+ { title: "Name", value: "name", sortable: false },
424
+ { title: "Facial Data", value: "facialData", sortable: false },
425
+ { title: "UID", value: "hidUserId", sortable: false },
426
+ { title: "Status", value: "status", sortable: false },
427
+ { title: "", value: "action-table", sortable: false },
428
+ ];
429
+
430
+ /**
431
+ * The list is paged on the SERVER, so the shared pager only moves the cursor -
432
+ * the rows still come from `loadUsers`. This is the same pair of statements
433
+ * the old footer's two chevrons ran.
434
+ */
435
+ function goToPage(next: number) {
436
+ page.value = next;
437
+ loadUsers();
438
+ }
439
+
442
440
  const readerOptions = computed(() =>
443
441
  readers.value.map((reader) => ({
444
442
  title: `${reader.name || reader.deviceId || reader._id} — ${reader.portalName || "Portal not configured"}`,
@@ -1316,10 +1314,6 @@ function getMappingStatus(user: Record<string, any>) {
1316
1314
  return hasMappedRecord(user) ? "Mapped" : "Unmapped";
1317
1315
  }
1318
1316
 
1319
- function getMappingStatusClass(user: Record<string, any>) {
1320
- return hasMappedRecord(user) ? "status-success" : "status-warning";
1321
- }
1322
-
1323
1317
  function hasMappedRecord(user: Record<string, any>) {
1324
1318
  return Boolean(user?.person || user?.user || user?.member || user?.visitor);
1325
1319
  }
@@ -1368,154 +1362,64 @@ function getHidErrorMessage(error: any) {
1368
1362
  </script>
1369
1363
 
1370
1364
  <style scoped>
1365
+ /*
1366
+ WHAT IS LEFT AFTER THE CARD AND TABLE MOVED TO `TableMain`.
1367
+
1368
+ The toolbar, the card, the six fixed column widths, the refresh strip, the
1369
+ footer pager, the empty state and the three `status-*` chip palettes are all
1370
+ gone - they were a private copy of the shared table drawn in literal hexes.
1371
+ What remains is the three dialogs, and every colour in them is now a token,
1372
+ so the dark theme reaches them for the first time.
1373
+ */
1371
1374
  .hid-user-enrollment {
1372
1375
  padding: 24px 0;
1373
1376
  width: 100%;
1374
1377
  min-width: 0;
1375
1378
  }
1376
1379
 
1377
- .toolbar {
1378
- display: flex;
1379
- flex-wrap: wrap;
1380
- gap: 12px;
1381
- align-items: center;
1382
- margin-bottom: 16px;
1380
+ .hid-user-filters {
1381
+ /* The filter row sits INSIDE the card's extension, so it needs the card's
1382
+ own side padding rather than the row's default bottom margin. */
1383
+ margin: 0;
1384
+ padding: 10px 14px;
1383
1385
  }
1384
1386
 
1385
- .toolbar-spacer {
1386
- flex: 1;
1387
- }
1388
-
1389
- .enroll-button {
1390
- min-width: 170px;
1391
- background: #e4e4e4;
1392
- color: #0a2638;
1393
- }
1394
-
1395
- .reader-input,
1396
- .search-input {
1387
+ .hid-user-filters :deep(.app-field) {
1397
1388
  max-width: 240px;
1398
1389
  }
1399
1390
 
1400
- .status-input {
1401
- max-width: 160px;
1402
- }
1403
-
1404
- .table-card {
1405
- display: flex;
1406
- flex-direction: column;
1407
- min-height: 430px;
1408
- overflow: hidden;
1409
- }
1410
-
1411
- .table-card :deep(table) {
1412
- table-layout: fixed;
1413
- width: 100%;
1414
- min-width: 820px;
1415
- }
1416
-
1417
- .table-card :deep(th),
1418
- .table-card :deep(td) {
1419
- white-space: nowrap;
1420
- }
1421
-
1422
- .table-card :deep(th:nth-child(1)),
1423
- .table-card :deep(td:nth-child(1)) {
1424
- width: 28%;
1425
- }
1426
-
1427
- .table-card :deep(th:nth-child(2)),
1428
- .table-card :deep(td:nth-child(2)) {
1429
- width: 22%;
1430
- }
1431
-
1432
- .table-card :deep(th:nth-child(3)),
1433
- .table-card :deep(td:nth-child(3)) {
1434
- width: 25%;
1435
- }
1436
-
1437
- .table-card :deep(th:nth-child(4)),
1438
- .table-card :deep(td:nth-child(4)) {
1439
- width: 17%;
1440
- }
1441
-
1442
- .table-card :deep(th:nth-child(5)),
1443
- .table-card :deep(td:nth-child(5)) {
1444
- width: 8%;
1445
- }
1446
-
1447
- .table-body {
1448
- flex: 1;
1449
- overflow-x: auto;
1450
- }
1451
-
1452
- .table-refresh,
1453
- .table-footer {
1454
- display: flex;
1455
- align-items: center;
1456
- gap: 8px;
1457
- padding: 8px 10px;
1458
- border-bottom: 1px solid #eeeeee;
1459
- }
1460
-
1461
- .table-footer {
1462
- justify-content: flex-end;
1463
- border-top: 1px solid #eeeeee;
1464
- border-bottom: 0;
1465
- background: #f6f6f6;
1466
- min-height: 44px;
1467
- padding: 4px 10px;
1468
- }
1469
-
1470
- .page-count {
1471
- margin-left: auto;
1472
- color: #475467;
1473
- font-size: 12px;
1474
- }
1475
-
1476
- .empty-state {
1477
- min-height: 280px;
1478
- display: grid;
1479
- place-items: center;
1480
- color: #687382;
1481
- font-size: 13px;
1482
- }
1483
-
1484
- .status-chip {
1485
- min-width: 86px;
1486
- justify-content: center;
1487
- font-weight: 600;
1488
- }
1489
-
1490
- .status-success {
1491
- background: #dff7e7 !important;
1492
- color: #1b7f3a !important;
1493
- }
1494
-
1495
- .status-warning {
1496
- background: #fff0cc !important;
1497
- color: #9a6700 !important;
1498
- }
1499
-
1500
- .status-error {
1501
- background: #fde2e1 !important;
1502
- color: #b42318 !important;
1391
+ /* The reader option now reads "Reader - Portal", so it needs more room than
1392
+ the other filters. `AppSelect`'s root class IS `app-field`, so this has to
1393
+ be more specific than the rule above rather than a `.app-select` one. */
1394
+ .hid-user-filters :deep(.app-field.reader-select) {
1395
+ max-width: 320px;
1503
1396
  }
1504
1397
 
1505
1398
  .small-title {
1506
1399
  font-size: 14px;
1507
1400
  font-weight: 700;
1401
+ color: var(--text);
1508
1402
  }
1509
1403
 
1404
+ /* Was `#4f5a66` / `#e53935` - the same two greys on both themes. */
1510
1405
  .field-label {
1511
- color: #4f5a66;
1406
+ color: var(--text2);
1512
1407
  font-size: 12px;
1513
1408
  font-weight: 600;
1514
1409
  margin-bottom: 6px;
1515
1410
  }
1516
1411
 
1517
1412
  .field-label span {
1518
- color: #e53935;
1413
+ color: var(--err);
1414
+ }
1415
+
1416
+ /* Vuetify's `persistent-hint` line, redrawn as the design's explanatory line. */
1417
+ .field-hint {
1418
+ margin: 6px 0 0;
1419
+ color: var(--muted);
1420
+ font-size: 12px;
1421
+ font-weight: 600;
1422
+ line-height: 1.45;
1519
1423
  }
1520
1424
 
1521
1425
  .photo-wrap {
@@ -1527,12 +1431,12 @@ function getHidErrorMessage(error: any) {
1527
1431
  .photo-button {
1528
1432
  width: 120px;
1529
1433
  height: 120px;
1530
- border: 1px solid #d8dde4;
1531
- border-radius: 8px;
1532
- background: #ffffff;
1434
+ border: 1px solid var(--border);
1435
+ border-radius: var(--r-inner);
1436
+ background: var(--card);
1533
1437
  display: grid;
1534
1438
  place-items: center;
1535
- color: #98a2b3;
1439
+ color: var(--muted);
1536
1440
  font-size: 12px;
1537
1441
  overflow: hidden;
1538
1442
  position: relative;
@@ -1550,16 +1454,16 @@ function getHidErrorMessage(error: any) {
1550
1454
  width: 28px;
1551
1455
  height: 28px;
1552
1456
  border-radius: 50%;
1553
- background: #ffffff;
1554
- color: #00324a;
1457
+ background: var(--card);
1458
+ color: var(--text);
1555
1459
  display: grid;
1556
1460
  place-items: center;
1557
- box-shadow: 0 2px 8px rgba(15, 23, 42, 0.18);
1461
+ box-shadow: var(--shadow-menu);
1558
1462
  }
1559
1463
 
1560
1464
  .hid-face-avatar {
1561
- border: 1px solid #d8dde4;
1562
- background: #f2f4f7;
1465
+ border: 1px solid var(--border);
1466
+ background: var(--hover);
1563
1467
  }
1564
1468
 
1565
1469
  .hidden-input {
@@ -1571,43 +1475,38 @@ function getHidErrorMessage(error: any) {
1571
1475
  grid-template-columns: 1fr 1fr;
1572
1476
  gap: 8px 16px;
1573
1477
  font-size: 13px;
1478
+ color: var(--text);
1574
1479
  }
1575
1480
 
1576
1481
  .detail-grid span {
1577
- color: #667085;
1482
+ color: var(--muted);
1578
1483
  }
1579
1484
 
1485
+ /* The split footer bar. The handoff does not draw one, so the SHAPE is the
1486
+ screen's own; only the colours change - `#062d42` was a fixed navy that sat
1487
+ on the dark card unchanged. Same treatment as panel 2. */
1580
1488
  .action-cancel,
1581
1489
  .action-submit {
1582
1490
  flex: 1;
1583
1491
  border-radius: 0;
1584
1492
  }
1585
1493
 
1494
+ .action-cancel {
1495
+ background: var(--card);
1496
+ color: var(--text2);
1497
+ }
1498
+
1586
1499
  .action-submit {
1587
- background: #062d42;
1588
- color: #ffffff;
1500
+ background: var(--accent-strong);
1501
+ color: var(--on-accent-strong);
1589
1502
  }
1590
1503
 
1591
1504
  @media (max-width: 960px) {
1592
- .toolbar {
1593
- align-items: stretch;
1594
- }
1595
-
1596
- .toolbar-spacer {
1597
- display: none;
1598
- }
1599
-
1600
- .enroll-button,
1601
- .reader-input,
1602
- .search-input,
1603
- .status-input {
1505
+ .hid-user-filters :deep(.app-field),
1506
+ .hid-user-filters :deep(.app-select) {
1604
1507
  max-width: none;
1605
1508
  width: 100%;
1606
1509
  }
1607
-
1608
- .table-card {
1609
- min-height: 360px;
1610
- }
1611
1510
  }
1612
1511
 
1613
1512
  @media (max-width: 600px) {
@@ -1624,10 +1523,6 @@ function getHidErrorMessage(error: any) {
1624
1523
  height: 104px;
1625
1524
  }
1626
1525
 
1627
- .table-footer {
1628
- justify-content: center;
1629
- }
1630
-
1631
1526
  :global(.v-dialog > .v-overlay__content) {
1632
1527
  max-width: calc(100vw - 24px) !important;
1633
1528
  margin: 12px !important;