@7365admin1/layer-common 3.2.2-staging.118 → 3.2.2-staging.121
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.
- package/assets/css/primitives.css +168 -0
- package/assets/css/screens.css +76 -1
- package/components/AccessCard/AvailableStats.vue +24 -16
- package/components/AddPassKeyToVisitor.vue +95 -73
- package/components/AppDateField.vue +15 -1
- package/components/AppField.vue +25 -0
- package/components/BuildingUnitFormEdit.vue +5 -1
- package/components/Card/DeleteConfirmation.vue +5 -2
- package/components/HidAccessLogDashboard.vue +288 -447
- package/components/HidIntercomManagement.vue +387 -521
- package/components/HidQrCodeConfiguration.vue +293 -369
- package/components/HidReaderManagement.vue +197 -320
- package/components/HidServiceSettingsPanel.vue +38 -26
- package/components/HidUserEnrollment.vue +239 -344
- package/components/IncidentReport/Authorities.vue +30 -18
- package/components/Nfc/NFCTagForm.vue +5 -1
- package/components/ScanVisitorQRCode.vue +64 -28
- package/components/SearchVehicleNumberUser.vue +39 -37
- package/components/TableMain.vue +50 -1
- package/components/VehicleAddSelection.vue +48 -32
- package/components/VehicleForm.vue +112 -129
- package/components/VehicleUpdateMoreAction.vue +59 -46
- package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
- package/components/VisitorFormSelection.vue +40 -37
- package/components/VisitorPassKeyQRScanner.vue +65 -18
- package/components/VisitorSocketPopUp.vue +21 -13
- package/components/VisitorsReportPreview.vue +44 -29
- package/package.json +1 -1
- package/tools/render-harness/baselines.json +95 -0
- package/tools/render-harness/harness-init.ps1 +4 -0
- package/utils/status.test.ts +33 -0
- package/utils/status.ts +36 -0
- package/utils/theme-aa-ledger.ts +142 -0
- package/utils/theme.test.ts +95 -34
- package/utils/theme.ts +55 -28
|
@@ -1,107 +1,105 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
HID READER MANAGEMENT - the readers on this site's doors.
|
|
3
|
+
|
|
4
|
+
Was a bespoke screen: an above-card toolbar, a raw `v-table` and about
|
|
5
|
+
twenty-five hardcoded hexes. Those hexes are why this screen had no dark
|
|
6
|
+
theme - `#111827` on the dark card measures **1.06:1**, and `#475467` 2.17:1.
|
|
7
|
+
|
|
8
|
+
It is the design's standard table screen, so it is drawn with the shared one:
|
|
9
|
+
`TableMain` supplies the page heading, the card, the in-card toolbar, the
|
|
10
|
+
uppercase header row, the 49px rows and the pager. Every column, action,
|
|
11
|
+
dialog and handler is the same - the table's own `filteredReaders` still
|
|
12
|
+
feeds it, the overflow menu still opens the same five dialogs.
|
|
13
|
+
|
|
14
|
+
The handoff draws no HID table, so it states no column proportions for it -
|
|
15
|
+
the table takes the shared automatic layout every other `TableMain` caller
|
|
16
|
+
uses, and scrolls inside the card at 360/768 the way the other wide tables
|
|
17
|
+
do. See `readerHeaders` for why the screen's own fixed percentages were
|
|
18
|
+
measured and dropped rather than carried over.
|
|
19
|
+
-->
|
|
1
20
|
<template>
|
|
2
21
|
<section class="hid-reader-management">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class="action-menu-button"
|
|
85
|
-
/>
|
|
86
|
-
</template>
|
|
87
|
-
|
|
88
|
-
<v-list density="compact" min-width="180">
|
|
89
|
-
<v-list-item title="View" @click="openView(reader)" />
|
|
90
|
-
<v-list-item title="Sync HID Reader" @click="openSyncConfirm(reader)" />
|
|
91
|
-
<v-list-item title="Test Connection" @click="openTestConfirm(reader)" />
|
|
92
|
-
<v-list-item title="Edit" @click="openEdit(reader)" />
|
|
93
|
-
<v-list-item title="Delete" class="text-error" @click="openDeleteConfirm(reader)" />
|
|
94
|
-
</v-list>
|
|
95
|
-
</v-menu>
|
|
96
|
-
</td>
|
|
97
|
-
</tr>
|
|
98
|
-
</tbody>
|
|
99
|
-
</v-table>
|
|
100
|
-
|
|
101
|
-
<div v-if="!filteredReaders.length" class="empty-state">
|
|
102
|
-
No HID devices added yet.
|
|
103
|
-
</div>
|
|
104
|
-
</v-card>
|
|
22
|
+
<TableMain
|
|
23
|
+
title="HID Readers"
|
|
24
|
+
:headers="readerHeaders"
|
|
25
|
+
:items="filteredReaders"
|
|
26
|
+
:loading="loading"
|
|
27
|
+
:items-per-page="-1"
|
|
28
|
+
:page-range="readerRange"
|
|
29
|
+
show-header
|
|
30
|
+
@refresh="loadReaders"
|
|
31
|
+
>
|
|
32
|
+
<template #actions>
|
|
33
|
+
<AppButton icon="mdi-plus" @click="openAdd">Add HID Reader</AppButton>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<!-- The design puts filters on their own full-width row under the
|
|
37
|
+
toolbar, which is what `extension` is. -->
|
|
38
|
+
<template #extension>
|
|
39
|
+
<div class="app-filter-row hid-reader-filters">
|
|
40
|
+
<AppField v-model="search" search placeholder="Search" />
|
|
41
|
+
<AppSelect v-model="status" :items="statusOptions" />
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<template #[`item.name`]="{ item }">
|
|
46
|
+
<span class="app-cell--strong">{{ item.name || "N/A" }}</span>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<template #[`item.location`]="{ item }">
|
|
50
|
+
<span class="app-cell--muted">{{ item.location || "N/A" }}</span>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<template #[`item.deviceId`]="{ item }">
|
|
54
|
+
<span class="app-cell--muted">{{ item.deviceId || "N/A" }}</span>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<!-- The shared chip, so "Active" is the same green here as on a work
|
|
58
|
+
order. `utils/status.ts` is the mapping; this screen's own
|
|
59
|
+
`getStatusClass` was a fourth private copy of it. -->
|
|
60
|
+
<template #[`item.status`]="{ item }">
|
|
61
|
+
<StatusChip
|
|
62
|
+
:status="resolveReaderStatus(item)"
|
|
63
|
+
:label="formatReaderStatus(resolveReaderStatus(item))"
|
|
64
|
+
/>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<template #[`item.lastSyncAt`]="{ item }">
|
|
68
|
+
<span class="app-cell--num">{{ formatDate(item.lastSyncAt) }}</span>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<template #[`item.lastSyncMessage`]="{ item }">
|
|
72
|
+
<span class="app-cell--muted" :title="item.lastSyncMessage || 'N/A'">
|
|
73
|
+
{{ item.lastSyncMessage || "N/A" }}
|
|
74
|
+
</span>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
77
|
+
<template #[`item.action-table`]="{ item }">
|
|
78
|
+
<v-menu>
|
|
79
|
+
<template #activator="{ props: menuProps }">
|
|
80
|
+
<AppButton v-bind="menuProps" variant="row" icon="mdi-dots-vertical" />
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<v-list density="compact" min-width="180">
|
|
84
|
+
<v-list-item title="View" @click="openView(item)" />
|
|
85
|
+
<v-list-item title="Sync HID Reader" @click="openSyncConfirm(item)" />
|
|
86
|
+
<v-list-item title="Test Connection" @click="openTestConfirm(item)" />
|
|
87
|
+
<v-list-item title="Edit" @click="openEdit(item)" />
|
|
88
|
+
<v-list-item title="Delete" class="text-error" @click="openDeleteConfirm(item)" />
|
|
89
|
+
</v-list>
|
|
90
|
+
</v-menu>
|
|
91
|
+
</template>
|
|
92
|
+
|
|
93
|
+
<!-- The screen's own sentence, kept: the shared empty state says "No data
|
|
94
|
+
available", which is less useful on a screen whose whole point is
|
|
95
|
+
that no device has been added yet. -->
|
|
96
|
+
<template #no-data>
|
|
97
|
+
<div class="table-card__empty">
|
|
98
|
+
<v-icon icon="mdi-card-account-details-outline" size="32" />
|
|
99
|
+
<span>No HID devices added yet.</span>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
102
|
+
</TableMain>
|
|
105
103
|
|
|
106
104
|
<HidReaderForm
|
|
107
105
|
v-model="formDialog"
|
|
@@ -113,7 +111,7 @@
|
|
|
113
111
|
|
|
114
112
|
<v-dialog v-model="viewDialog" max-width="390">
|
|
115
113
|
<v-card rounded="lg">
|
|
116
|
-
<v-card-title class="
|
|
114
|
+
<v-card-title class="screen-card-title">{{ selectedReader?.name || "HID Reader" }}</v-card-title>
|
|
117
115
|
<v-card-text>
|
|
118
116
|
<div class="detail-grid">
|
|
119
117
|
<span>HID reader name</span><strong>{{ selectedReader?.name || "N/A" }}</strong>
|
|
@@ -126,35 +124,35 @@
|
|
|
126
124
|
</div>
|
|
127
125
|
|
|
128
126
|
<div class="dialog-actions-inline">
|
|
129
|
-
<
|
|
130
|
-
<
|
|
127
|
+
<AppButton variant="ghost" @click="openTestConfirm(selectedReader)">Test Connection</AppButton>
|
|
128
|
+
<AppButton variant="ghost" @click="openSyncConfirm(selectedReader)">Sync Reader</AppButton>
|
|
131
129
|
</div>
|
|
132
130
|
</v-card-text>
|
|
133
131
|
<v-card-actions>
|
|
134
132
|
<v-spacer />
|
|
135
|
-
<
|
|
133
|
+
<AppButton variant="ghost" @click="viewDialog = false">Close</AppButton>
|
|
136
134
|
</v-card-actions>
|
|
137
135
|
</v-card>
|
|
138
136
|
</v-dialog>
|
|
139
137
|
|
|
140
138
|
<v-dialog v-model="confirmDialog" max-width="390" persistent>
|
|
141
139
|
<v-card rounded="lg" class="reader-action-dialog">
|
|
142
|
-
<v-card-title class="
|
|
140
|
+
<v-card-title class="screen-card-title">{{ confirmTitle }}</v-card-title>
|
|
143
141
|
|
|
144
142
|
<v-card-text v-if="pendingAction === 'test'" class="reader-action-content">
|
|
145
|
-
<v-icon icon="mdi-database-sync-outline"
|
|
143
|
+
<v-icon icon="mdi-database-sync-outline" class="dialog-mark" size="48" />
|
|
146
144
|
<h3>Test Device Connection</h3>
|
|
147
145
|
<p>You are about to test the connection for {{ selectedReader?.name || "this HID Reader" }}.</p>
|
|
148
146
|
</v-card-text>
|
|
149
147
|
|
|
150
148
|
<v-card-text v-else-if="pendingAction === 'sync' && actionStage === 'confirm'" class="reader-action-content">
|
|
151
|
-
<v-icon icon="mdi-database-sync-outline"
|
|
149
|
+
<v-icon icon="mdi-database-sync-outline" class="dialog-mark" size="48" />
|
|
152
150
|
<h3>Sync Data</h3>
|
|
153
151
|
<p>You're about to sync data with {{ selectedReader?.name || "this HID Reader" }}.</p>
|
|
154
152
|
</v-card-text>
|
|
155
153
|
|
|
156
154
|
<v-card-text v-else-if="pendingAction === 'sync' && actionStage === 'review'" class="reader-action-content reader-sync-review">
|
|
157
|
-
<v-icon icon="mdi-database-sync-outline"
|
|
155
|
+
<v-icon icon="mdi-database-sync-outline" class="dialog-mark" size="48" />
|
|
158
156
|
<h3>Ready to Synchronize</h3>
|
|
159
157
|
<div class="sync-summary-list">
|
|
160
158
|
<div>
|
|
@@ -182,7 +180,7 @@
|
|
|
182
180
|
</v-card-text>
|
|
183
181
|
|
|
184
182
|
<v-card-text v-else-if="pendingAction === 'sync' && actionStage === 'loading'" class="reader-action-content">
|
|
185
|
-
<v-progress-circular indeterminate
|
|
183
|
+
<v-progress-circular indeterminate class="dialog-spinner" size="42" width="5" />
|
|
186
184
|
<h3>Sync Data to {{ selectedReader?.name || "HID Reader" }}...</h3>
|
|
187
185
|
</v-card-text>
|
|
188
186
|
|
|
@@ -208,7 +206,7 @@
|
|
|
208
206
|
|
|
209
207
|
<v-dialog v-model="resultDialog" max-width="390">
|
|
210
208
|
<v-card rounded="lg">
|
|
211
|
-
<v-card-title v-if="resultReaderName" class="
|
|
209
|
+
<v-card-title v-if="resultReaderName" class="screen-card-title">{{ resultReaderName }}</v-card-title>
|
|
212
210
|
<v-card-text class="result-content" :class="{ 'result-with-stats': resultStats }">
|
|
213
211
|
<v-icon :icon="resultSuccess ? 'mdi-check-circle' : 'mdi-alert-circle'" :color="resultSuccess ? 'success' : 'warning'" size="42" />
|
|
214
212
|
<h3>{{ resultTitle }}</h3>
|
|
@@ -284,6 +282,37 @@ const resultStats = ref<typeof syncPreviewStats.value | null>(null);
|
|
|
284
282
|
|
|
285
283
|
const statusOptions = ["Status", "active", "inactive", "deleted", "offline"];
|
|
286
284
|
|
|
285
|
+
/**
|
|
286
|
+
* The columns. NO `weight`, on purpose.
|
|
287
|
+
*
|
|
288
|
+
* The old CSS pinned them at 19/14/15/12/15/20/5 % with `table-layout: fixed`,
|
|
289
|
+
* and `TableMain`'s `weight` would say exactly that - but it was measured and
|
|
290
|
+
* it is worse than the automatic layout, in two ways the design cares about:
|
|
291
|
+
*
|
|
292
|
+
* * at 360 the six percentages resolve against a ~330px card, so every cell
|
|
293
|
+
* truncates to "Mai...", the headers overlap and the status chip is cut in
|
|
294
|
+
* half. The old fixed grid only worked because it also carried
|
|
295
|
+
* `min-width: 920px`, which is the automatic layout's behaviour anyway.
|
|
296
|
+
* * a 5% action column is ~29px of content once the shared 20px cell padding
|
|
297
|
+
* is taken out, so the 30px row button overflows by half a pixel and the
|
|
298
|
+
* fixed-layout ellipsis rule paints a "..." beside every button.
|
|
299
|
+
*
|
|
300
|
+
* The handoff states `fr` proportions for the tables it draws (Feedbacks,
|
|
301
|
+
* Units, Pass & Key) and draws no HID table at all, so there is no design
|
|
302
|
+
* number to honour here. Automatic is what the other 39 `TableMain` callers
|
|
303
|
+
* use. `sortable: false` keeps the previous behaviour: the raw `v-table` never
|
|
304
|
+
* sorted.
|
|
305
|
+
*/
|
|
306
|
+
const readerHeaders = [
|
|
307
|
+
{ title: "Name", value: "name", sortable: false },
|
|
308
|
+
{ title: "Location", value: "location", sortable: false },
|
|
309
|
+
{ title: "Device ID", value: "deviceId", sortable: false },
|
|
310
|
+
{ title: "Status", value: "status", sortable: false },
|
|
311
|
+
{ title: "Last sync at", value: "lastSyncAt", sortable: false },
|
|
312
|
+
{ title: "Last sync message", value: "lastSyncMessage", sortable: false },
|
|
313
|
+
{ title: "", value: "action-table", sortable: false },
|
|
314
|
+
];
|
|
315
|
+
|
|
287
316
|
const filteredReaders = computed(() => {
|
|
288
317
|
const query = search.value.trim().toLowerCase();
|
|
289
318
|
return readers.value.filter((reader) => {
|
|
@@ -301,6 +330,15 @@ const filteredReaders = computed(() => {
|
|
|
301
330
|
});
|
|
302
331
|
});
|
|
303
332
|
|
|
333
|
+
/**
|
|
334
|
+
* The design's `1-6 of 6` count on the toolbar row. This screen loads every
|
|
335
|
+
* reader in one call and has never paged, so the range is simply what is on
|
|
336
|
+
* screen - stated rather than left as `TableMain`'s `-- - -- of --` placeholder.
|
|
337
|
+
*/
|
|
338
|
+
const readerRange = computed(() =>
|
|
339
|
+
filteredReaders.value.length ? `1 - ${filteredReaders.value.length} of ${filteredReaders.value.length}` : "0 of 0"
|
|
340
|
+
);
|
|
341
|
+
|
|
304
342
|
const confirmTitle = computed(() => {
|
|
305
343
|
if (pendingAction.value === "sync" && actionStage.value === "loading") return "Test Connection...";
|
|
306
344
|
if (pendingAction.value === "sync") return "Sync Data";
|
|
@@ -916,13 +954,6 @@ function formatDate(value?: string) {
|
|
|
916
954
|
return Number.isNaN(date.getTime()) ? "N/A" : date.toLocaleString();
|
|
917
955
|
}
|
|
918
956
|
|
|
919
|
-
function getStatusClass(value?: string) {
|
|
920
|
-
if (value === "active") return "status-success";
|
|
921
|
-
if (value === "inactive") return "status-warning";
|
|
922
|
-
if (value === "deleted") return "status-error";
|
|
923
|
-
return "status-muted";
|
|
924
|
-
}
|
|
925
|
-
|
|
926
957
|
function resolveReaderStatus(reader?: Record<string, any> | null) {
|
|
927
958
|
if (!reader) return "";
|
|
928
959
|
if (reader.lastSyncStatus === "failed" && reader.status === "active") return "offline";
|
|
@@ -943,208 +974,49 @@ function formatReaderStatus(value?: string) {
|
|
|
943
974
|
}
|
|
944
975
|
</script>
|
|
945
976
|
|
|
977
|
+
|
|
946
978
|
<style scoped>
|
|
979
|
+
/*
|
|
980
|
+
WHAT IS LEFT AFTER THE CARD AND TABLE MOVED TO `TableMain`.
|
|
981
|
+
|
|
982
|
+
The toolbar, the card, the `reader-table` grid, the seven column widths, the
|
|
983
|
+
header band, the row height, the hover, the empty state and the four
|
|
984
|
+
`status-*` chip palettes are all gone - they were a private copy of the shared
|
|
985
|
+
table, drawn in about twenty-five literal hexes. What remains is the two
|
|
986
|
+
dialogs, and every colour in them is now a token, so the dark theme reaches
|
|
987
|
+
them for the first time.
|
|
988
|
+
*/
|
|
947
989
|
.hid-reader-management {
|
|
948
990
|
padding: 24px 0;
|
|
949
991
|
width: 100%;
|
|
950
992
|
min-width: 0;
|
|
951
993
|
}
|
|
952
994
|
|
|
953
|
-
.
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
margin-bottom: 16px;
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
.toolbar-spacer {
|
|
962
|
-
flex: 1;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
.add-button {
|
|
966
|
-
min-width: 170px;
|
|
967
|
-
background: #e4e4e4;
|
|
968
|
-
color: #0a2638;
|
|
995
|
+
.hid-reader-filters {
|
|
996
|
+
/* The filter row sits INSIDE the card's extension, so it needs the card's
|
|
997
|
+
own side padding rather than the row's default bottom margin. */
|
|
998
|
+
margin: 0;
|
|
999
|
+
padding: 10px 14px;
|
|
969
1000
|
}
|
|
970
1001
|
|
|
971
|
-
.
|
|
1002
|
+
.hid-reader-filters :deep(.app-field) {
|
|
972
1003
|
max-width: 240px;
|
|
973
1004
|
}
|
|
974
1005
|
|
|
975
|
-
.
|
|
1006
|
+
.hid-reader-filters :deep(.app-select) {
|
|
976
1007
|
max-width: 160px;
|
|
977
1008
|
}
|
|
978
1009
|
|
|
979
|
-
.table-card {
|
|
980
|
-
min-height: 430px;
|
|
981
|
-
overflow-x: auto;
|
|
982
|
-
overflow-y: hidden;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
.reader-table :deep(table) {
|
|
986
|
-
table-layout: fixed;
|
|
987
|
-
width: 100%;
|
|
988
|
-
min-width: 920px;
|
|
989
|
-
border-collapse: separate;
|
|
990
|
-
border-spacing: 0;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
.reader-table :deep(th) {
|
|
994
|
-
height: 44px;
|
|
995
|
-
background: #f8fafc;
|
|
996
|
-
color: #344054;
|
|
997
|
-
font-size: 12px;
|
|
998
|
-
font-weight: 700;
|
|
999
|
-
letter-spacing: 0;
|
|
1000
|
-
text-transform: none;
|
|
1001
|
-
border-bottom: 1px solid #e7ebef;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
.reader-table :deep(td) {
|
|
1005
|
-
height: 64px;
|
|
1006
|
-
color: #1f2937;
|
|
1007
|
-
font-size: 13px;
|
|
1008
|
-
border-bottom: 1px solid #edf0f3;
|
|
1009
|
-
vertical-align: middle;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
.reader-table :deep(tbody tr) {
|
|
1013
|
-
transition: background-color 0.15s ease;
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
.reader-table :deep(tbody tr:hover) {
|
|
1017
|
-
background: #fbfcfe;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
.reader-table :deep(th),
|
|
1021
|
-
.reader-table :deep(td) {
|
|
1022
|
-
padding: 0 14px !important;
|
|
1023
|
-
white-space: nowrap;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
.name-column,
|
|
1027
|
-
.name-cell {
|
|
1028
|
-
width: 19%;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
.location-column,
|
|
1032
|
-
.location-cell {
|
|
1033
|
-
width: 14%;
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
.device-column,
|
|
1037
|
-
.device-cell {
|
|
1038
|
-
width: 15%;
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
.status-column,
|
|
1042
|
-
.status-cell {
|
|
1043
|
-
width: 12%;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
.sync-at-column,
|
|
1047
|
-
.sync-at-cell {
|
|
1048
|
-
width: 15%;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
.message-column,
|
|
1052
|
-
.message-cell {
|
|
1053
|
-
width: 20%;
|
|
1054
|
-
min-width: 0;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
.action-column,
|
|
1058
|
-
.action-cell {
|
|
1059
|
-
width: 5%;
|
|
1060
|
-
text-align: right;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
.cell-strong {
|
|
1064
|
-
display: block;
|
|
1065
|
-
overflow: hidden;
|
|
1066
|
-
color: #111827;
|
|
1067
|
-
font-weight: 600;
|
|
1068
|
-
text-overflow: ellipsis;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
.location-cell,
|
|
1072
|
-
.device-cell,
|
|
1073
|
-
.sync-at-cell {
|
|
1074
|
-
overflow: hidden;
|
|
1075
|
-
color: #475467;
|
|
1076
|
-
text-overflow: ellipsis;
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
.message-text {
|
|
1080
|
-
display: block;
|
|
1081
|
-
max-width: 100%;
|
|
1082
|
-
overflow: hidden;
|
|
1083
|
-
color: #475467;
|
|
1084
|
-
line-height: 1.35;
|
|
1085
|
-
text-overflow: ellipsis;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
.action-menu-button {
|
|
1089
|
-
color: #344054;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
.table-refresh {
|
|
1093
|
-
display: flex;
|
|
1094
|
-
justify-content: flex-start;
|
|
1095
|
-
padding: 10px 14px;
|
|
1096
|
-
border-bottom: 1px solid #e7ebef;
|
|
1097
|
-
background: #ffffff;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
.empty-state {
|
|
1101
|
-
min-height: 280px;
|
|
1102
|
-
display: grid;
|
|
1103
|
-
place-items: center;
|
|
1104
|
-
color: #687382;
|
|
1105
|
-
font-size: 13px;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
.status-chip {
|
|
1109
|
-
min-width: 86px;
|
|
1110
|
-
justify-content: center;
|
|
1111
|
-
font-weight: 600;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
.status-success {
|
|
1115
|
-
background: #dff7e7 !important;
|
|
1116
|
-
color: #1b7f3a !important;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
.status-warning {
|
|
1120
|
-
background: #fff0cc !important;
|
|
1121
|
-
color: #9a6700 !important;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
.status-error {
|
|
1125
|
-
background: #fde2e1 !important;
|
|
1126
|
-
color: #b42318 !important;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
.status-muted {
|
|
1130
|
-
background: #e9edf2 !important;
|
|
1131
|
-
color: #475467 !important;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
.small-title {
|
|
1135
|
-
font-size: 14px;
|
|
1136
|
-
font-weight: 700;
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
1010
|
.detail-grid {
|
|
1140
1011
|
display: grid;
|
|
1141
1012
|
grid-template-columns: 1fr 1fr;
|
|
1142
1013
|
gap: 8px 16px;
|
|
1143
1014
|
font-size: 13px;
|
|
1015
|
+
color: var(--text);
|
|
1144
1016
|
}
|
|
1145
1017
|
|
|
1146
1018
|
.detail-grid span {
|
|
1147
|
-
color:
|
|
1019
|
+
color: var(--muted);
|
|
1148
1020
|
}
|
|
1149
1021
|
|
|
1150
1022
|
.dialog-actions-inline {
|
|
@@ -1169,7 +1041,7 @@ function formatReaderStatus(value?: string) {
|
|
|
1169
1041
|
|
|
1170
1042
|
.reader-action-content h3 {
|
|
1171
1043
|
margin: 4px 0 0;
|
|
1172
|
-
color:
|
|
1044
|
+
color: var(--text);
|
|
1173
1045
|
font-size: 15px;
|
|
1174
1046
|
font-weight: 700;
|
|
1175
1047
|
line-height: 1.3;
|
|
@@ -1177,11 +1049,21 @@ function formatReaderStatus(value?: string) {
|
|
|
1177
1049
|
|
|
1178
1050
|
.reader-action-content p {
|
|
1179
1051
|
margin: 0;
|
|
1180
|
-
color:
|
|
1052
|
+
color: var(--muted);
|
|
1181
1053
|
font-size: 13px;
|
|
1182
1054
|
line-height: 1.45;
|
|
1183
1055
|
}
|
|
1184
1056
|
|
|
1057
|
+
/* Was `color="#1976d2"` and `color="#8a8f98"` on the elements themselves - the
|
|
1058
|
+
same blue and the same grey on both themes. */
|
|
1059
|
+
.dialog-mark {
|
|
1060
|
+
color: var(--accent);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.dialog-spinner {
|
|
1064
|
+
color: var(--muted);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1185
1067
|
.reader-sync-review {
|
|
1186
1068
|
justify-items: stretch;
|
|
1187
1069
|
text-align: left;
|
|
@@ -1205,24 +1087,32 @@ function formatReaderStatus(value?: string) {
|
|
|
1205
1087
|
align-items: center;
|
|
1206
1088
|
justify-content: space-between;
|
|
1207
1089
|
gap: 16px;
|
|
1208
|
-
color:
|
|
1090
|
+
color: var(--muted);
|
|
1209
1091
|
font-size: 13px;
|
|
1210
1092
|
}
|
|
1211
1093
|
|
|
1212
1094
|
.sync-summary-list strong {
|
|
1213
|
-
color:
|
|
1095
|
+
color: var(--text);
|
|
1214
1096
|
font-weight: 700;
|
|
1215
1097
|
}
|
|
1216
1098
|
|
|
1099
|
+
/* The split footer bar. The handoff does not draw one, so the SHAPE is the
|
|
1100
|
+
screen's own; only the colours change - `#062d42` was a fixed navy that sat
|
|
1101
|
+
on the dark card unchanged. */
|
|
1217
1102
|
.action-cancel,
|
|
1218
1103
|
.action-submit {
|
|
1219
1104
|
flex: 1;
|
|
1220
1105
|
border-radius: 0;
|
|
1221
1106
|
}
|
|
1222
1107
|
|
|
1108
|
+
.action-cancel {
|
|
1109
|
+
background: var(--card);
|
|
1110
|
+
color: var(--text2);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1223
1113
|
.action-submit {
|
|
1224
|
-
background:
|
|
1225
|
-
color:
|
|
1114
|
+
background: var(--accent-strong);
|
|
1115
|
+
color: var(--on-accent-strong);
|
|
1226
1116
|
}
|
|
1227
1117
|
|
|
1228
1118
|
.result-content {
|
|
@@ -1246,24 +1136,11 @@ function formatReaderStatus(value?: string) {
|
|
|
1246
1136
|
}
|
|
1247
1137
|
|
|
1248
1138
|
@media (max-width: 960px) {
|
|
1249
|
-
.
|
|
1250
|
-
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
.toolbar-spacer {
|
|
1254
|
-
display: none;
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
.add-button,
|
|
1258
|
-
.search-input,
|
|
1259
|
-
.status-input {
|
|
1139
|
+
.hid-reader-filters :deep(.app-field),
|
|
1140
|
+
.hid-reader-filters :deep(.app-select) {
|
|
1260
1141
|
max-width: none;
|
|
1261
1142
|
width: 100%;
|
|
1262
1143
|
}
|
|
1263
|
-
|
|
1264
|
-
.table-card {
|
|
1265
|
-
min-height: 360px;
|
|
1266
|
-
}
|
|
1267
1144
|
}
|
|
1268
1145
|
|
|
1269
1146
|
@media (max-width: 600px) {
|
|
@@ -1275,7 +1152,7 @@ function formatReaderStatus(value?: string) {
|
|
|
1275
1152
|
grid-template-columns: 1fr;
|
|
1276
1153
|
}
|
|
1277
1154
|
|
|
1278
|
-
.dialog-actions-inline .
|
|
1155
|
+
.dialog-actions-inline .app-btn {
|
|
1279
1156
|
width: 100%;
|
|
1280
1157
|
}
|
|
1281
1158
|
|