@7365admin1/layer-common 3.2.6 → 3.2.8-staging.200

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 (284) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/assets/css/primitives.css +1113 -0
  3. package/assets/css/screens.css +1097 -0
  4. package/assets/css/shell.css +258 -0
  5. package/assets/css/tokens.css +236 -0
  6. package/components/AcceptDialog.vue +17 -18
  7. package/components/AccessCard/AvailableStats.vue +24 -16
  8. package/components/AccessCardAddForm.vue +42 -42
  9. package/components/AccessCardAssignToUnitForm.vue +13 -21
  10. package/components/AccessCardDeleteDialog.vue +41 -39
  11. package/components/AccessCardDetailsDialog.vue +125 -79
  12. package/components/AccessCardPreviewDialog.vue +58 -84
  13. package/components/AccessCardQrTagging.vue +60 -63
  14. package/components/AccessCardReplaceForm.vue +27 -23
  15. package/components/AccessManagement.vue +86 -85
  16. package/components/AddEqupmentForm.vue +18 -22
  17. package/components/AddPassKeyToVisitor.vue +95 -73
  18. package/components/AppButton.vue +62 -0
  19. package/components/AppCard.vue +20 -0
  20. package/components/AppDateField.vue +49 -0
  21. package/components/AppField.vue +76 -0
  22. package/components/AppKpiTile.vue +65 -0
  23. package/components/AppSegmented.vue +38 -0
  24. package/components/AppSelect.vue +247 -0
  25. package/components/AreaChecklistHistoryLogs.vue +51 -44
  26. package/components/AreaChecklistHistoryMain.vue +14 -7
  27. package/components/AreaFormDialog.vue +71 -59
  28. package/components/AreaMain.vue +73 -107
  29. package/components/AttachmentsViewer.vue +84 -22
  30. package/components/AttendanceCheckInOutDialog.vue +85 -88
  31. package/components/AttendanceDetailsDialog.vue +57 -63
  32. package/components/AttendanceMain.vue +4 -3
  33. package/components/AttendanceMapSearchDialog.vue +42 -44
  34. package/components/AttendanceSettingsDialog.vue +79 -63
  35. package/components/Avatar/Main.vue +54 -2
  36. package/components/BillingMain.vue +26 -30
  37. package/components/BtnUploadFile.vue +53 -11
  38. package/components/BuildingForm.vue +34 -27
  39. package/components/BuildingManagement/buildings.vue +39 -39
  40. package/components/BuildingManagement/units.vue +26 -42
  41. package/components/BuildingUnitFormAdd.vue +20 -23
  42. package/components/BuildingUnitFormEdit.vue +8 -4
  43. package/components/BulletinBoardForm.vue +19 -24
  44. package/components/BulletinBoardManagement.vue +49 -57
  45. package/components/BulletinBoardView.vue +9 -6
  46. package/components/BulletinExpirationChip.vue +13 -4
  47. package/components/Button/Close.vue +2 -2
  48. package/components/CameraForm.vue +21 -22
  49. package/components/CameraMain.vue +50 -49
  50. package/components/Card/DeleteConfirmation.vue +24 -22
  51. package/components/Card/MemberInfoSummary.vue +28 -3
  52. package/components/Card/Toggle.vue +15 -3
  53. package/components/CardToolbar.vue +37 -0
  54. package/components/Carousel.vue +56 -36
  55. package/components/Chat/Bubbles.vue +43 -14
  56. package/components/Chat/Information.vue +49 -32
  57. package/components/Chat/ListCard.vue +38 -9
  58. package/components/Chat/Message.vue +41 -15
  59. package/components/Chat/Navigation.vue +27 -15
  60. package/components/Chat/SkeletonLoader.vue +9 -2
  61. package/components/ChecklistItemRow.vue +25 -1
  62. package/components/CleaningScheduleMain.vue +44 -49
  63. package/components/ClientDetailForm.vue +81 -56
  64. package/components/ClientMain.vue +92 -82
  65. package/components/ConfirmDialog.vue +14 -8
  66. package/components/Container/Standard.vue +9 -7
  67. package/components/CreateSubsciptionPlan.vue +148 -89
  68. package/components/DashboardEmptyState.vue +48 -3
  69. package/components/DashboardMain.vue +924 -676
  70. package/components/DashboardPanel.vue +84 -21
  71. package/components/DeliveryCompany.vue +34 -20
  72. package/components/Dialog/DeleteConfirmation.vue +20 -21
  73. package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
  74. package/components/Dialog/ReusablePrompt.vue +30 -29
  75. package/components/Dialog/UpdateMoreAction.vue +62 -59
  76. package/components/DocumentForm.vue +14 -21
  77. package/components/DocumentManagement.vue +162 -108
  78. package/components/DocumentViewer.vue +29 -12
  79. package/components/DrawImage.vue +29 -21
  80. package/components/EntryPass/QrTemplatePreview.vue +27 -16
  81. package/components/EntryPassInformation.vue +40 -10
  82. package/components/EntryPassMain.vue +49 -27
  83. package/components/EquipmentItemMain.vue +84 -97
  84. package/components/EquipmentManagementMain.vue +81 -103
  85. package/components/Facility/BookingSetup.vue +43 -52
  86. package/components/Feedback/Form.vue +26 -12
  87. package/components/FeedbackMain.vue +35 -49
  88. package/components/FileInput.vue +75 -58
  89. package/components/FileInputWithList.vue +59 -12
  90. package/components/FormDialog.vue +60 -21
  91. package/components/HidAccessLogDashboard.vue +512 -490
  92. package/components/HidCameraCaptureDialog.vue +216 -0
  93. package/components/HidEnabledGate.vue +67 -9
  94. package/components/HidIntercomManagement.vue +1322 -653
  95. package/components/HidQrCodeConfiguration.vue +1030 -258
  96. package/components/HidReaderForm.vue +142 -59
  97. package/components/HidReaderManagement.vue +211 -323
  98. package/components/HidServiceSettingsPanel.vue +44 -13
  99. package/components/HidUserEnrollment.vue +1185 -412
  100. package/components/HygieneUpdateMoreAction.vue +44 -44
  101. package/components/IncidentReport/Authorities.vue +30 -18
  102. package/components/IncidentReport/IncidentInformation.vue +10 -10
  103. package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
  104. package/components/IncidentReport/affectedEntities.vue +41 -20
  105. package/components/Input/Date.vue +3 -0
  106. package/components/Input/DatePicker.vue +1 -2
  107. package/components/Input/DateTimePicker.vue +1 -1
  108. package/components/Input/File.vue +55 -10
  109. package/components/Input/FileV2.vue +39 -6
  110. package/components/Input/InputPhoneNumberV2.vue +2 -1
  111. package/components/Input/ListGroupSelection.vue +56 -8
  112. package/components/Input/NRICNumber.vue +1 -0
  113. package/components/Input/NewDate.vue +1 -0
  114. package/components/Input/Number.vue +1 -0
  115. package/components/Input/Password.vue +1 -0
  116. package/components/Input/PhoneNumber.vue +21 -1
  117. package/components/Input/VehicleNumber.vue +1 -0
  118. package/components/InputLabel.vue +2 -2
  119. package/components/InvitationClientForm.vue +21 -19
  120. package/components/InvitationForm.vue +15 -21
  121. package/components/InvitationMain.vue +46 -61
  122. package/components/Layout/Header.vue +263 -36
  123. package/components/Layout/NavigationDrawer.vue +300 -30
  124. package/components/ListItem.vue +33 -3
  125. package/components/LocalPagination.vue +27 -3
  126. package/components/ManageChecklistMain.vue +136 -86
  127. package/components/MemberInformation.vue +32 -16
  128. package/components/MemberMain.vue +113 -93
  129. package/components/MyAttendanceMain.vue +3 -5
  130. package/components/NavigationItem.vue +83 -3
  131. package/components/Nfc/NFCPatrolReportMain.vue +47 -33
  132. package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
  133. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  134. package/components/Nfc/NFCPatrolSettings.vue +14 -4
  135. package/components/Nfc/NFCTagForm.vue +22 -8
  136. package/components/Nfc/NFCTagMain.vue +41 -43
  137. package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
  138. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
  139. package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
  140. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
  141. package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
  142. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
  143. package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
  144. package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
  145. package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
  146. package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
  147. package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
  148. package/components/NotificationSettings.vue +373 -0
  149. package/components/NumberSettingField.vue +9 -2
  150. package/components/OnlineFormConfigurationForm.vue +725 -237
  151. package/components/OnlineFormFill.vue +213 -106
  152. package/components/OnlineFormRenderPreview.vue +25 -30
  153. package/components/OnlineFormsConfiguration.vue +113 -109
  154. package/components/OrgViewDialog.vue +34 -16
  155. package/components/OvernightParkingAvailability.vue +49 -26
  156. package/components/OvernightParkingManagement.vue +78 -18
  157. package/components/PageHeader.vue +43 -0
  158. package/components/PassInformation.vue +109 -53
  159. package/components/PasswordConfirmation.vue +44 -26
  160. package/components/PeopleForm.vue +41 -20
  161. package/components/PhotoUpload.vue +110 -48
  162. package/components/PlaceholderComponent.vue +35 -19
  163. package/components/PlanActionsMenu.vue +12 -2
  164. package/components/PlanStatusBadge.vue +7 -8
  165. package/components/PlateNumberDisplay.vue +9 -6
  166. package/components/QrTemplate/PrintDialog.vue +66 -43
  167. package/components/RolePermissionFormCreate.vue +104 -79
  168. package/components/RolePermissionFormPreviewUpdate.vue +111 -109
  169. package/components/RolePermissionMain.vue +183 -81
  170. package/components/ScanVisitorQRCode.vue +99 -35
  171. package/components/ScheduleAreaMain.vue +34 -59
  172. package/components/ScheduleTaskForm.vue +61 -42
  173. package/components/ScheduleTaskMain.vue +56 -69
  174. package/components/SearchVehicleNumberUser.vue +39 -37
  175. package/components/ServiceProviderFormCreate.vue +29 -20
  176. package/components/ServiceProviderInvitationPrompt.vue +155 -0
  177. package/components/ServiceProviderMain.vue +601 -432
  178. package/components/Signature.vue +40 -34
  179. package/components/SignaturePad.vue +5 -29
  180. package/components/SiteSettings.vue +41 -11
  181. package/components/SiteSettingsWorkOrder.vue +61 -30
  182. package/components/SlideCardGroup.vue +35 -11
  183. package/components/Snackbar.vue +36 -3
  184. package/components/SpecificAttr.vue +18 -8
  185. package/components/StatusChip.vue +156 -0
  186. package/components/StockCard.vue +17 -12
  187. package/components/SubscriptionPlanTable.vue +207 -140
  188. package/components/SwitchContext.vue +201 -9
  189. package/components/TableHygiene.vue +36 -46
  190. package/components/TableList.vue +10 -10
  191. package/components/TableListSecondary.vue +105 -54
  192. package/components/TableMain.vue +241 -79
  193. package/components/TableV3.vue +61 -70
  194. package/components/TiptapEditor.vue +33 -40
  195. package/components/TiptapEditorQr.vue +5 -2
  196. package/components/Tooltip/Info.vue +69 -24
  197. package/components/UnitMain.vue +40 -60
  198. package/components/UnitPersonCard.vue +42 -13
  199. package/components/VehicleAddSelection.vue +48 -32
  200. package/components/VehicleForm.vue +112 -129
  201. package/components/VehicleManagement.vue +26 -12
  202. package/components/VehicleUpdateMoreAction.vue +59 -46
  203. package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
  204. package/components/VisitorForm.vue +774 -210
  205. package/components/VisitorFormSelection.vue +40 -37
  206. package/components/VisitorManagement.vue +239 -82
  207. package/components/VisitorPassKeyQRScanner.vue +65 -18
  208. package/components/VisitorSearchResultSummary.vue +60 -16
  209. package/components/VisitorSocketPopUp.vue +22 -14
  210. package/components/VisitorsReportPreview.vue +44 -29
  211. package/components/WorkOrder/Create.vue +26 -17
  212. package/components/WorkOrder/ListView.vue +38 -7
  213. package/components/WorkOrder/Main.vue +62 -59
  214. package/composables/useFeedback.ts +3 -3
  215. package/composables/useHidAmico.ts +269 -0
  216. package/composables/useHidNavigation.ts +2 -9
  217. package/composables/useMember.ts +3 -3
  218. package/composables/useNFCPatrolDailyReport.ts +13 -0
  219. package/composables/useNFCPatrolMonthlyReport.ts +22 -56
  220. package/composables/useNFCPatrolReport.ts +13 -0
  221. package/composables/useNotificationPreference.ts +87 -0
  222. package/composables/useOnlineFormPages.ts +2 -0
  223. package/composables/useRole.ts +22 -0
  224. package/composables/useServiceProvider.ts +3 -3
  225. package/composables/useServiceProviderInvitation.ts +145 -0
  226. package/composables/useSipWebPhone.ts +336 -0
  227. package/composables/useSubscriptionPlan.ts +62 -0
  228. package/composables/useThemePreference.ts +63 -0
  229. package/composables/useVisitor.ts +6 -1
  230. package/composables/useWebUsb.ts +127 -37
  231. package/composables/useWorkOrder.ts +17 -3
  232. package/error.vue +30 -6
  233. package/nuxt.config.ts +36 -0
  234. package/package.json +25 -4
  235. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
  236. package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
  237. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  238. package/pages/notification-settings.vue +19 -0
  239. package/pages/payment-method-linked.vue +34 -10
  240. package/pages/require-customer.vue +48 -10
  241. package/pages/require-organization-membership.vue +32 -8
  242. package/pages/unauthorized.vue +29 -4
  243. package/plugins/secure-member.client.ts +21 -56
  244. package/plugins/vuetify.ts +69 -2
  245. package/types/nfc-patrol-report.ts +3 -2
  246. package/types/site.d.ts +71 -0
  247. package/types/subscription.ts +18 -11
  248. package/utils/breadcrumb.test.ts +108 -0
  249. package/utils/breadcrumb.ts +82 -0
  250. package/utils/dashboard.test.ts +118 -0
  251. package/utils/dashboard.ts +53 -0
  252. package/utils/data.test.ts +95 -0
  253. package/utils/data.ts +40 -2
  254. package/utils/hid-discover.test.ts +102 -0
  255. package/utils/list-page.test.ts +60 -0
  256. package/utils/list-page.ts +38 -0
  257. package/utils/materialSymbols.ts +363 -0
  258. package/utils/nav-sections.test.ts +190 -0
  259. package/utils/nav-sections.ts +151 -0
  260. package/utils/nfc-patrol-report.test.ts +130 -0
  261. package/utils/nfc-patrol-report.ts +101 -0
  262. package/utils/role.test.ts +69 -0
  263. package/utils/role.ts +10 -0
  264. package/utils/route-name.test.ts +85 -0
  265. package/utils/route-name.ts +24 -0
  266. package/utils/status.test.ts +110 -0
  267. package/utils/status.ts +135 -0
  268. package/utils/table-card.test.ts +84 -0
  269. package/utils/theme-aa-ledger.ts +148 -0
  270. package/utils/theme.test.ts +485 -7
  271. package/utils/theme.ts +295 -110
  272. package/.changeset/README.md +0 -8
  273. package/.changeset/config.json +0 -11
  274. package/.editorconfig +0 -12
  275. package/.github/workflows/main.yml +0 -17
  276. package/.github/workflows/publish-staging.yml +0 -47
  277. package/.github/workflows/publish.yml +0 -39
  278. package/PUBLISHING.md +0 -269
  279. package/components/HidIdentityMapping.vue +0 -975
  280. package/components/ScheduleTaskAreaFormDialog.vue +0 -144
  281. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
  282. package/components/ScheduleTastTicketMain.vue +0 -182
  283. package/components/TableWithButton.vue +0 -94
  284. package/test/visitor-socket.test.mjs +0 -36
@@ -0,0 +1,216 @@
1
+ <template>
2
+ <v-dialog
3
+ :model-value="modelValue"
4
+ max-width="520"
5
+ persistent
6
+ @update:model-value="emit('update:modelValue', $event)"
7
+ >
8
+ <!-- `rounded="lg"` is dropped, not replaced. Measured on the glass, the card
9
+ read 16px BEFORE and after: the `.rounded-lg` utility it compiled to was
10
+ already being overridden, so the prop stated a radius the card never
11
+ had. `.screen-modal` states the same 16px as `--r-modal`, on purpose. -->
12
+ <v-card class="camera-dialog screen-modal">
13
+ <v-card-title class="d-flex align-center justify-space-between">
14
+ <span>Take Facial Photo</span>
15
+ <v-btn
16
+ icon="mdi-close"
17
+ variant="text"
18
+ class="screen-modal__close"
19
+ aria-label="Close camera"
20
+ @click="close"
21
+ />
22
+ </v-card-title>
23
+
24
+ <v-card-text class="screen-modal__body">
25
+ <div class="camera-viewport">
26
+ <video
27
+ ref="videoElement"
28
+ class="camera-preview"
29
+ autoplay
30
+ muted
31
+ playsinline
32
+ />
33
+
34
+ <div v-if="starting" class="camera-state">
35
+ <v-progress-circular indeterminate color="primary" />
36
+ <span class="mt-3">Starting camera...</span>
37
+ </div>
38
+
39
+ <div v-else-if="cameraError" class="camera-state px-6 text-center">
40
+ <v-icon icon="mdi-camera-off-outline" size="42" color="error" />
41
+ <span class="mt-3 text-error">{{ cameraError }}</span>
42
+ </div>
43
+ </div>
44
+
45
+ <canvas ref="canvasElement" class="d-none" />
46
+
47
+ <div class="camera-dialog__hint text-center">
48
+ Keep your face centered and make sure the area is well lit.
49
+ </div>
50
+ </v-card-text>
51
+
52
+ <!-- `color="primary"` on a `v-btn` is the design's FOREGROUND token - the
53
+ link ink - not a fill, so the capture button was accent-on-accent.
54
+ `.screen-btn-primary` states the fill the design does draw. -->
55
+ <div class="screen-modal__footer">
56
+ <v-btn class="text-none screen-btn-ghost" variant="flat" @click="close">
57
+ Cancel
58
+ </v-btn>
59
+ <v-btn
60
+ class="text-none screen-btn-primary"
61
+ variant="flat"
62
+ prepend-icon="mdi-camera"
63
+ :disabled="starting || !!cameraError || !stream"
64
+ @click="capture"
65
+ >
66
+ Take Photo
67
+ </v-btn>
68
+ </div>
69
+ </v-card>
70
+ </v-dialog>
71
+ </template>
72
+
73
+ <script setup lang="ts">
74
+ const props = defineProps<{ modelValue: boolean }>();
75
+ const emit = defineEmits<{
76
+ "update:modelValue": [value: boolean];
77
+ capture: [dataUrl: string];
78
+ }>();
79
+
80
+ const videoElement = ref<HTMLVideoElement | null>(null);
81
+ const canvasElement = ref<HTMLCanvasElement | null>(null);
82
+ const stream = shallowRef<MediaStream | null>(null);
83
+ const starting = ref(false);
84
+ const cameraError = ref("");
85
+
86
+ function stopCamera() {
87
+ stream.value?.getTracks().forEach((track) => track.stop());
88
+ stream.value = null;
89
+ if (videoElement.value) videoElement.value.srcObject = null;
90
+ }
91
+
92
+ function getCameraErrorMessage(caught: unknown) {
93
+ if (caught instanceof DOMException) {
94
+ if (caught.name === "NotAllowedError") {
95
+ return "Camera permission was denied. Allow camera access and try again.";
96
+ }
97
+ if (caught.name === "NotFoundError") {
98
+ return "No camera was found on this device.";
99
+ }
100
+ if (caught.name === "NotReadableError") {
101
+ return "The camera is currently being used by another application.";
102
+ }
103
+ }
104
+ return "Unable to open the camera. Use HTTPS or check the browser camera permission.";
105
+ }
106
+
107
+ async function startCamera() {
108
+ stopCamera();
109
+ cameraError.value = "";
110
+ starting.value = true;
111
+
112
+ try {
113
+ if (!navigator.mediaDevices?.getUserMedia) {
114
+ throw new Error("Camera API is unavailable");
115
+ }
116
+
117
+ stream.value = await navigator.mediaDevices.getUserMedia({
118
+ audio: false,
119
+ video: {
120
+ facingMode: "user",
121
+ width: { ideal: 1280 },
122
+ height: { ideal: 960 },
123
+ },
124
+ });
125
+
126
+ await nextTick();
127
+ if (!videoElement.value) throw new Error("Camera preview is unavailable");
128
+ videoElement.value.srcObject = stream.value;
129
+ await videoElement.value.play();
130
+ } catch (caught: unknown) {
131
+ stopCamera();
132
+ cameraError.value = getCameraErrorMessage(caught);
133
+ } finally {
134
+ starting.value = false;
135
+ }
136
+ }
137
+
138
+ function capture() {
139
+ const video = videoElement.value;
140
+ const canvas = canvasElement.value;
141
+ if (!video || !canvas || !video.videoWidth || !video.videoHeight) return;
142
+
143
+ canvas.width = video.videoWidth;
144
+ canvas.height = video.videoHeight;
145
+ const context = canvas.getContext("2d");
146
+ if (!context) return;
147
+
148
+ context.translate(canvas.width, 0);
149
+ context.scale(-1, 1);
150
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
151
+
152
+ emit("capture", canvas.toDataURL("image/jpeg", 0.9));
153
+ close();
154
+ }
155
+
156
+ function close() {
157
+ stopCamera();
158
+ emit("update:modelValue", false);
159
+ }
160
+
161
+ watch(
162
+ () => props.modelValue,
163
+ (isOpen) => {
164
+ if (isOpen) void startCamera();
165
+ else stopCamera();
166
+ }
167
+ );
168
+
169
+ onBeforeUnmount(stopCamera);
170
+ </script>
171
+
172
+ <style scoped>
173
+ .camera-dialog {
174
+ overflow: hidden;
175
+ }
176
+
177
+ .camera-viewport {
178
+ position: relative;
179
+ width: 100%;
180
+ overflow: hidden;
181
+ aspect-ratio: 3 / 4;
182
+ border-radius: var(--r-inner);
183
+ /* Deliberately NOT a theme surface: this is the stage a video sits on, and it
184
+ stays dark in both themes so the preview does not flash white on light.
185
+ Its own `#ffffff` text below is paired with it, not with the theme. */
186
+ background: #111722;
187
+ }
188
+
189
+ /* The design's small helper line, not Vuetify's `text-caption` /
190
+ `text-medium-emphasis` pair. */
191
+ .camera-dialog__hint {
192
+ margin-top: 12px;
193
+ font-family: var(--font-sans);
194
+ font-size: var(--fs-cell);
195
+ font-weight: 500;
196
+ color: var(--muted);
197
+ }
198
+
199
+ .camera-preview {
200
+ width: 100%;
201
+ height: 100%;
202
+ object-fit: cover;
203
+ transform: scaleX(-1);
204
+ }
205
+
206
+ .camera-state {
207
+ position: absolute;
208
+ inset: 0;
209
+ display: flex;
210
+ flex-direction: column;
211
+ align-items: center;
212
+ justify-content: center;
213
+ background: #111722;
214
+ color: #ffffff;
215
+ }
216
+ </style>
@@ -1,14 +1,31 @@
1
+ <!--
2
+ The gate that stands in for a whole screen when HID is not enabled for a site.
3
+
4
+ What it was: a raw `v-alert type="info" variant="tonal"` — Vuetify's own tonal
5
+ slab, at Vuetify's 4px radius, in Vuetify's font, on a Vuetify blue the theme
6
+ does not control. It is a full-screen state, not an inline warning.
7
+
8
+ DESIGN GAP, STATED: the handoff draws no "service not enabled" page. So this
9
+ is assembled from the established primitive rather than invented, exactly as
10
+ `PlaceholderComponent` was — `.screen-empty` (`assets/css/screens.css:149`) is
11
+ the design's centred, muted, 52px-padded block for "there is nothing to show
12
+ here", and the heading takes the `--fs-card-title` / `--fw-card-title` type
13
+ the design uses for every panel heading.
14
+
15
+ `.tone-info` sets `--tone-bg`; the icon's soft disc is the only thing that
16
+ paints with it, so the block keeps the informational colour without becoming
17
+ a coloured slab. Icons are translated centrally — no hand-mapping here.
18
+ -->
1
19
  <template>
2
20
  <slot v-if="hidEnabled" />
3
21
 
4
- <v-alert
5
- v-else
6
- type="info"
7
- variant="tonal"
8
- class="mt-6"
9
- title="HID service is not enabled"
10
- :text="message"
11
- />
22
+ <div v-else class="screen-empty hid-gate">
23
+ <span class="hid-gate__icon tone-info">
24
+ <v-icon icon="mdi-information-outline" size="24" />
25
+ </span>
26
+ <h2 class="hid-gate__title">HID service is not enabled</h2>
27
+ <p class="hid-gate__body">{{ message }}</p>
28
+ </div>
12
29
  </template>
13
30
 
14
31
  <script setup lang="ts">
@@ -28,7 +45,11 @@ const props = withDefaults(
28
45
  const { getSiteById } = useSiteSettings();
29
46
  const { getHidServiceEnabled } = useOptionalServices();
30
47
 
31
- const { data: siteData } = await useLazyAsyncData(
48
+ // Do not await this request during route setup. Every HID page uses this gate;
49
+ // awaiting it makes Nuxt Suspense keep rendering the previous page until the
50
+ // site request finishes, so the URL changes while the visible module appears
51
+ // frozen. The lazy request updates `siteData` reactively after navigation.
52
+ const { data: siteData } = useLazyAsyncData(
32
53
  "hid-enabled-gate-site-" + props.site,
33
54
  () => getSiteById(props.site),
34
55
  {
@@ -49,3 +70,40 @@ watchEffect(() => {
49
70
  );
50
71
  });
51
72
  </script>
73
+
74
+ <style scoped>
75
+ /* It replaces a screen's whole body, so it holds the viewport rather than
76
+ collapsing to the height of three lines. Same reason as PlaceholderComponent. */
77
+ .hid-gate {
78
+ min-height: 40vh;
79
+ }
80
+
81
+ .hid-gate__icon {
82
+ display: inline-flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ width: 44px;
86
+ height: 44px;
87
+ border-radius: 50%;
88
+ background: var(--tone-bg);
89
+ }
90
+
91
+ /* `.screen-empty .v-icon` paints `--muted`; inside the tone scope the icon is
92
+ the informational colour, so it has to win that one-class rule. */
93
+ .hid-gate .hid-gate__icon .v-icon {
94
+ color: var(--info);
95
+ }
96
+
97
+ .hid-gate__title {
98
+ margin: 0;
99
+ font-size: var(--fs-card-title);
100
+ font-weight: var(--fw-card-title);
101
+ color: var(--text);
102
+ }
103
+
104
+ .hid-gate__body {
105
+ max-width: 46ch;
106
+ margin: 0;
107
+ font-weight: var(--fw-cell);
108
+ }
109
+ </style>