@7365admin1/layer-common 3.2.7 → 3.2.8-staging.201

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 (283) hide show
  1. package/assets/css/primitives.css +1113 -0
  2. package/assets/css/screens.css +1097 -0
  3. package/assets/css/shell.css +258 -0
  4. package/assets/css/tokens.css +236 -0
  5. package/components/AcceptDialog.vue +17 -18
  6. package/components/AccessCard/AvailableStats.vue +24 -16
  7. package/components/AccessCardAddForm.vue +42 -42
  8. package/components/AccessCardAssignToUnitForm.vue +13 -21
  9. package/components/AccessCardDeleteDialog.vue +41 -39
  10. package/components/AccessCardDetailsDialog.vue +125 -79
  11. package/components/AccessCardPreviewDialog.vue +58 -84
  12. package/components/AccessCardQrTagging.vue +60 -63
  13. package/components/AccessCardReplaceForm.vue +27 -23
  14. package/components/AccessManagement.vue +86 -85
  15. package/components/AddEqupmentForm.vue +18 -22
  16. package/components/AddPassKeyToVisitor.vue +95 -73
  17. package/components/AppButton.vue +62 -0
  18. package/components/AppCard.vue +20 -0
  19. package/components/AppDateField.vue +49 -0
  20. package/components/AppField.vue +76 -0
  21. package/components/AppKpiTile.vue +65 -0
  22. package/components/AppSegmented.vue +38 -0
  23. package/components/AppSelect.vue +247 -0
  24. package/components/AreaChecklistHistoryLogs.vue +51 -44
  25. package/components/AreaChecklistHistoryMain.vue +14 -7
  26. package/components/AreaFormDialog.vue +71 -59
  27. package/components/AreaMain.vue +73 -107
  28. package/components/AttachmentsViewer.vue +84 -22
  29. package/components/AttendanceCheckInOutDialog.vue +85 -88
  30. package/components/AttendanceDetailsDialog.vue +57 -63
  31. package/components/AttendanceMain.vue +4 -3
  32. package/components/AttendanceMapSearchDialog.vue +42 -44
  33. package/components/AttendanceSettingsDialog.vue +79 -63
  34. package/components/Avatar/Main.vue +54 -2
  35. package/components/BillingMain.vue +26 -30
  36. package/components/BtnUploadFile.vue +53 -11
  37. package/components/BuildingForm.vue +34 -27
  38. package/components/BuildingManagement/buildings.vue +39 -39
  39. package/components/BuildingManagement/units.vue +26 -42
  40. package/components/BuildingUnitFormAdd.vue +20 -23
  41. package/components/BuildingUnitFormEdit.vue +8 -4
  42. package/components/BulletinBoardForm.vue +19 -24
  43. package/components/BulletinBoardManagement.vue +49 -57
  44. package/components/BulletinBoardView.vue +9 -6
  45. package/components/BulletinExpirationChip.vue +13 -4
  46. package/components/Button/Close.vue +2 -2
  47. package/components/CameraForm.vue +21 -22
  48. package/components/CameraMain.vue +50 -49
  49. package/components/Card/DeleteConfirmation.vue +24 -22
  50. package/components/Card/MemberInfoSummary.vue +28 -3
  51. package/components/Card/Toggle.vue +15 -3
  52. package/components/CardToolbar.vue +37 -0
  53. package/components/Carousel.vue +56 -36
  54. package/components/Chat/Bubbles.vue +43 -14
  55. package/components/Chat/Information.vue +49 -32
  56. package/components/Chat/ListCard.vue +38 -9
  57. package/components/Chat/Message.vue +41 -15
  58. package/components/Chat/Navigation.vue +27 -15
  59. package/components/Chat/SkeletonLoader.vue +9 -2
  60. package/components/ChecklistItemRow.vue +25 -1
  61. package/components/CleaningScheduleMain.vue +44 -49
  62. package/components/ClientDetailForm.vue +81 -56
  63. package/components/ClientMain.vue +92 -82
  64. package/components/ConfirmDialog.vue +14 -8
  65. package/components/Container/Standard.vue +9 -7
  66. package/components/CreateSubsciptionPlan.vue +148 -89
  67. package/components/DashboardEmptyState.vue +48 -3
  68. package/components/DashboardMain.vue +924 -676
  69. package/components/DashboardPanel.vue +84 -21
  70. package/components/DeliveryCompany.vue +34 -20
  71. package/components/Dialog/DeleteConfirmation.vue +20 -21
  72. package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
  73. package/components/Dialog/ReusablePrompt.vue +30 -29
  74. package/components/Dialog/UpdateMoreAction.vue +62 -59
  75. package/components/DocumentForm.vue +14 -21
  76. package/components/DocumentManagement.vue +162 -108
  77. package/components/DocumentViewer.vue +29 -12
  78. package/components/DrawImage.vue +29 -21
  79. package/components/EntryPass/QrTemplatePreview.vue +27 -16
  80. package/components/EntryPassInformation.vue +40 -10
  81. package/components/EntryPassMain.vue +49 -27
  82. package/components/EquipmentItemMain.vue +84 -97
  83. package/components/EquipmentManagementMain.vue +81 -103
  84. package/components/Facility/BookingSetup.vue +43 -52
  85. package/components/Feedback/Form.vue +26 -12
  86. package/components/FeedbackMain.vue +35 -49
  87. package/components/FileInput.vue +75 -58
  88. package/components/FileInputWithList.vue +59 -12
  89. package/components/FormDialog.vue +60 -21
  90. package/components/HidAccessLogDashboard.vue +512 -490
  91. package/components/HidCameraCaptureDialog.vue +216 -0
  92. package/components/HidEnabledGate.vue +67 -9
  93. package/components/HidIntercomManagement.vue +1322 -653
  94. package/components/HidQrCodeConfiguration.vue +1030 -258
  95. package/components/HidReaderForm.vue +142 -59
  96. package/components/HidReaderManagement.vue +211 -323
  97. package/components/HidServiceSettingsPanel.vue +44 -13
  98. package/components/HidUserEnrollment.vue +1185 -412
  99. package/components/HygieneUpdateMoreAction.vue +44 -44
  100. package/components/IncidentReport/Authorities.vue +30 -18
  101. package/components/IncidentReport/IncidentInformation.vue +10 -10
  102. package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
  103. package/components/IncidentReport/affectedEntities.vue +41 -20
  104. package/components/Input/Date.vue +3 -0
  105. package/components/Input/DatePicker.vue +1 -2
  106. package/components/Input/DateTimePicker.vue +1 -1
  107. package/components/Input/File.vue +55 -10
  108. package/components/Input/FileV2.vue +39 -6
  109. package/components/Input/InputPhoneNumberV2.vue +2 -1
  110. package/components/Input/ListGroupSelection.vue +56 -8
  111. package/components/Input/NRICNumber.vue +1 -0
  112. package/components/Input/NewDate.vue +1 -0
  113. package/components/Input/Number.vue +1 -0
  114. package/components/Input/Password.vue +1 -0
  115. package/components/Input/PhoneNumber.vue +21 -1
  116. package/components/Input/VehicleNumber.vue +1 -0
  117. package/components/InputLabel.vue +2 -2
  118. package/components/InvitationClientForm.vue +21 -19
  119. package/components/InvitationForm.vue +15 -21
  120. package/components/InvitationMain.vue +46 -61
  121. package/components/Layout/Header.vue +263 -36
  122. package/components/Layout/NavigationDrawer.vue +300 -30
  123. package/components/ListItem.vue +33 -3
  124. package/components/LocalPagination.vue +27 -3
  125. package/components/ManageChecklistMain.vue +136 -86
  126. package/components/MemberInformation.vue +32 -16
  127. package/components/MemberMain.vue +113 -93
  128. package/components/MyAttendanceMain.vue +3 -5
  129. package/components/NavigationItem.vue +83 -3
  130. package/components/Nfc/NFCPatrolReportMain.vue +47 -33
  131. package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
  132. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  133. package/components/Nfc/NFCPatrolSettings.vue +14 -4
  134. package/components/Nfc/NFCTagForm.vue +22 -8
  135. package/components/Nfc/NFCTagMain.vue +41 -43
  136. package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
  137. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
  138. package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
  139. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
  140. package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
  141. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
  142. package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
  143. package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
  144. package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
  145. package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
  146. package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
  147. package/components/NotificationSettings.vue +373 -0
  148. package/components/NumberSettingField.vue +9 -2
  149. package/components/OnlineFormConfigurationForm.vue +725 -237
  150. package/components/OnlineFormFill.vue +213 -106
  151. package/components/OnlineFormRenderPreview.vue +25 -30
  152. package/components/OnlineFormsConfiguration.vue +113 -109
  153. package/components/OrgViewDialog.vue +34 -16
  154. package/components/OvernightParkingAvailability.vue +49 -26
  155. package/components/OvernightParkingManagement.vue +78 -18
  156. package/components/PageHeader.vue +43 -0
  157. package/components/PassInformation.vue +109 -53
  158. package/components/PasswordConfirmation.vue +44 -26
  159. package/components/PeopleForm.vue +41 -20
  160. package/components/PhotoUpload.vue +110 -48
  161. package/components/PlaceholderComponent.vue +35 -19
  162. package/components/PlanActionsMenu.vue +12 -2
  163. package/components/PlanStatusBadge.vue +7 -8
  164. package/components/PlateNumberDisplay.vue +9 -6
  165. package/components/QrTemplate/PrintDialog.vue +66 -43
  166. package/components/RolePermissionFormCreate.vue +104 -79
  167. package/components/RolePermissionFormPreviewUpdate.vue +111 -109
  168. package/components/RolePermissionMain.vue +183 -81
  169. package/components/ScanVisitorQRCode.vue +99 -35
  170. package/components/ScheduleAreaMain.vue +34 -59
  171. package/components/ScheduleTaskForm.vue +61 -42
  172. package/components/ScheduleTaskMain.vue +56 -69
  173. package/components/SearchVehicleNumberUser.vue +39 -37
  174. package/components/ServiceProviderFormCreate.vue +29 -20
  175. package/components/ServiceProviderInvitationPrompt.vue +155 -0
  176. package/components/ServiceProviderMain.vue +601 -432
  177. package/components/Signature.vue +40 -34
  178. package/components/SignaturePad.vue +5 -29
  179. package/components/SiteSettings.vue +41 -11
  180. package/components/SiteSettingsWorkOrder.vue +61 -30
  181. package/components/SlideCardGroup.vue +35 -11
  182. package/components/Snackbar.vue +36 -3
  183. package/components/SpecificAttr.vue +18 -8
  184. package/components/StatusChip.vue +156 -0
  185. package/components/StockCard.vue +17 -12
  186. package/components/SubscriptionPlanTable.vue +207 -140
  187. package/components/SwitchContext.vue +201 -9
  188. package/components/TableHygiene.vue +36 -46
  189. package/components/TableList.vue +10 -10
  190. package/components/TableListSecondary.vue +105 -54
  191. package/components/TableMain.vue +251 -78
  192. package/components/TableV3.vue +61 -70
  193. package/components/TiptapEditor.vue +33 -40
  194. package/components/TiptapEditorQr.vue +5 -2
  195. package/components/Tooltip/Info.vue +69 -24
  196. package/components/UnitMain.vue +40 -60
  197. package/components/UnitPersonCard.vue +42 -13
  198. package/components/VehicleAddSelection.vue +48 -32
  199. package/components/VehicleForm.vue +112 -129
  200. package/components/VehicleManagement.vue +26 -12
  201. package/components/VehicleUpdateMoreAction.vue +59 -46
  202. package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
  203. package/components/VisitorForm.vue +774 -210
  204. package/components/VisitorFormSelection.vue +40 -37
  205. package/components/VisitorManagement.vue +239 -82
  206. package/components/VisitorPassKeyQRScanner.vue +65 -18
  207. package/components/VisitorSearchResultSummary.vue +60 -16
  208. package/components/VisitorSocketPopUp.vue +22 -14
  209. package/components/VisitorsReportPreview.vue +44 -29
  210. package/components/WorkOrder/Create.vue +26 -17
  211. package/components/WorkOrder/ListView.vue +38 -7
  212. package/components/WorkOrder/Main.vue +62 -59
  213. package/composables/useFeedback.ts +3 -3
  214. package/composables/useHidAmico.ts +269 -0
  215. package/composables/useHidNavigation.ts +2 -9
  216. package/composables/useMember.ts +3 -3
  217. package/composables/useNFCPatrolDailyReport.ts +13 -0
  218. package/composables/useNFCPatrolMonthlyReport.ts +22 -56
  219. package/composables/useNFCPatrolReport.ts +13 -0
  220. package/composables/useNotificationPreference.ts +87 -0
  221. package/composables/useOnlineFormPages.ts +2 -0
  222. package/composables/useRole.ts +22 -0
  223. package/composables/useServiceProvider.ts +3 -3
  224. package/composables/useServiceProviderInvitation.ts +145 -0
  225. package/composables/useSipWebPhone.ts +336 -0
  226. package/composables/useSubscriptionPlan.ts +62 -0
  227. package/composables/useThemePreference.ts +63 -0
  228. package/composables/useVisitor.ts +6 -1
  229. package/composables/useWebUsb.ts +127 -37
  230. package/composables/useWorkOrder.ts +17 -3
  231. package/error.vue +30 -6
  232. package/nuxt.config.ts +36 -0
  233. package/package.json +25 -4
  234. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
  235. package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
  236. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  237. package/pages/notification-settings.vue +19 -0
  238. package/pages/payment-method-linked.vue +34 -10
  239. package/pages/require-customer.vue +48 -10
  240. package/pages/require-organization-membership.vue +32 -8
  241. package/pages/unauthorized.vue +29 -4
  242. package/plugins/secure-member.client.ts +21 -56
  243. package/plugins/vuetify.ts +69 -2
  244. package/types/nfc-patrol-report.ts +3 -2
  245. package/types/site.d.ts +71 -0
  246. package/types/subscription.ts +18 -11
  247. package/utils/breadcrumb.test.ts +108 -0
  248. package/utils/breadcrumb.ts +82 -0
  249. package/utils/dashboard.test.ts +118 -0
  250. package/utils/dashboard.ts +53 -0
  251. package/utils/data.test.ts +95 -0
  252. package/utils/data.ts +40 -2
  253. package/utils/hid-discover.test.ts +102 -0
  254. package/utils/list-page.test.ts +60 -0
  255. package/utils/list-page.ts +38 -0
  256. package/utils/materialSymbols.ts +363 -0
  257. package/utils/nav-sections.test.ts +190 -0
  258. package/utils/nav-sections.ts +151 -0
  259. package/utils/nfc-patrol-report.test.ts +130 -0
  260. package/utils/nfc-patrol-report.ts +101 -0
  261. package/utils/role.test.ts +69 -0
  262. package/utils/role.ts +10 -0
  263. package/utils/route-name.test.ts +85 -0
  264. package/utils/route-name.ts +24 -0
  265. package/utils/status.test.ts +110 -0
  266. package/utils/status.ts +135 -0
  267. package/utils/table-card.test.ts +84 -0
  268. package/utils/theme-aa-ledger.ts +148 -0
  269. package/utils/theme.test.ts +485 -7
  270. package/utils/theme.ts +295 -110
  271. package/.changeset/README.md +0 -8
  272. package/.changeset/config.json +0 -11
  273. package/.editorconfig +0 -12
  274. package/.github/workflows/main.yml +0 -17
  275. package/.github/workflows/publish-staging.yml +0 -47
  276. package/.github/workflows/publish.yml +0 -39
  277. package/PUBLISHING.md +0 -269
  278. package/components/HidIdentityMapping.vue +0 -975
  279. package/components/ScheduleTaskAreaFormDialog.vue +0 -144
  280. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
  281. package/components/ScheduleTastTicketMain.vue +0 -182
  282. package/components/TableWithButton.vue +0 -94
  283. package/test/visitor-socket.test.mjs +0 -36
@@ -1,8 +1,8 @@
1
1
  <template>
2
- <v-card width="100%">
3
- <v-toolbar>
2
+ <v-card width="100%" class="screen-modal">
3
+ <v-toolbar class="screen-dialog-bar">
4
4
  <v-row no-gutters class="fill-height px-6" align="center" justify="space-between">
5
- <span class="font-weight-bold text-h5 text-capitalize">
5
+ <span class="screen-card-title text-capitalize">
6
6
  {{ prop.mode }} Access Card
7
7
  </span>
8
8
  <v-btn-toggle
@@ -19,7 +19,10 @@
19
19
  </v-btn-toggle>
20
20
  </v-row>
21
21
  </v-toolbar>
22
- <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-0">
22
+ <!-- The height cap stays ON THE BODY. `.screen-modal` caps the CARD, which
23
+ only reaches the glass inside a dialog's flex column; mounted on its own
24
+ the footer would fall below the fold. -->
25
+ <v-card-text class="screen-modal__body pa-0" style="max-height: 100vh">
23
26
  <!-- Bulk Upload Mode -->
24
27
  <template v-if="uploadMode === 'bulk'">
25
28
  <v-row no-gutters class="px-6 pt-4 pb-2">
@@ -63,12 +66,12 @@
63
66
 
64
67
  <!-- Column Guide -->
65
68
  <div class="mt-3">
66
- <p class="text-caption font-weight-bold text-grey-darken-2 mb-1 text-uppercase">
69
+ <p class="text-caption font-weight-bold text-fontgray mb-1 text-uppercase">
67
70
  Template Column Guide
68
71
  </p>
69
72
  <v-table density="compact" class="text-caption rounded border">
70
73
  <thead>
71
- <tr class="bg-grey-lighten-4">
74
+ <tr class="template-guide-head">
72
75
  <th class="text-left text-caption font-weight-bold py-1 px-2">Column</th>
73
76
  <th class="text-left text-caption font-weight-bold py-1 px-2">Format / Accepted Values</th>
74
77
  <th class="text-left text-caption font-weight-bold py-1 px-2">Example</th>
@@ -77,8 +80,8 @@
77
80
  <tbody>
78
81
  <tr v-for="col in templateColumnGuide" :key="col.name">
79
82
  <td class="py-1 px-2 font-weight-medium" style="white-space: nowrap">{{ col.name }}</td>
80
- <td class="py-1 px-2 text-grey-darken-1">{{ col.format }}</td>
81
- <td class="py-1 px-2 text-grey-darken-2">{{ col.example }}</td>
83
+ <td class="py-1 px-2 text-fontgray">{{ col.format }}</td>
84
+ <td class="py-1 px-2 text-fontgray">{{ col.example }}</td>
82
85
  </tr>
83
86
  </tbody>
84
87
  </v-table>
@@ -306,7 +309,7 @@
306
309
  ></span>
307
310
  <span class="d-flex align-center">
308
311
  Anti Pass Back
309
- <v-icon class="ml-1" color="grey" size="small"
312
+ <v-icon class="ml-1 text-fontgray" size="small"
310
313
  >mdi-help-circle</v-icon
311
314
  >
312
315
  <v-checkbox
@@ -382,39 +385,25 @@
382
385
  </v-row>
383
386
  </v-form>
384
387
  </v-card-text>
385
- <v-toolbar density="compact">
386
- <v-row no-gutters>
387
- <v-col cols="6">
388
- <v-btn
389
- tile
390
- block
391
- variant="text"
392
- class="text-none"
393
- size="48"
394
- @click="cancel"
395
- :disabled="disable"
396
- >
397
- Cancel
398
- </v-btn>
399
- </v-col>
400
-
401
- <v-col cols="6">
402
- <v-btn
403
- tile
404
- block
405
- variant="flat"
406
- color="black"
407
- class="text-none"
408
- size="48"
409
- :disabled="(uploadMode === 'single' ? !validForm : !bulkFile || !entryPassTemplate.id || templateLoading) || disable"
410
- @click="submit"
411
- :loading="disable"
412
- >
413
- Submit
414
- </v-btn>
415
- </v-col>
416
- </v-row>
417
- </v-toolbar>
388
+ <v-card-actions class="screen-modal__footer">
389
+ <v-btn
390
+ variant="flat"
391
+ class="text-none screen-btn-ghost"
392
+ :disabled="disable"
393
+ @click="cancel"
394
+ >
395
+ Cancel
396
+ </v-btn>
397
+ <v-btn
398
+ variant="flat"
399
+ class="text-none screen-btn-primary"
400
+ :disabled="(uploadMode === 'single' ? !validForm : !bulkFile || !entryPassTemplate.id || templateLoading) || disable"
401
+ :loading="disable"
402
+ @click="submit"
403
+ >
404
+ Submit
405
+ </v-btn>
406
+ </v-card-actions>
418
407
  </v-card>
419
408
  </template>
420
409
  <script setup lang="ts">
@@ -813,3 +802,14 @@ async function submit() {
813
802
  }
814
803
  }
815
804
  </script>
805
+
806
+ <style scoped>
807
+ /*
808
+ * The template guide's header row. `bg-grey-lighten-4` was a fixed near-white,
809
+ * i.e. a bright band across a dark card; `--thead` is the design's own table
810
+ * head wash and is defined per theme.
811
+ */
812
+ .template-guide-head {
813
+ background: var(--thead);
814
+ }
815
+ </style>
@@ -1,11 +1,8 @@
1
1
  <template>
2
- <v-card width="100%">
3
- <v-toolbar>
4
- <v-row no-gutters class="fill-height px-6" align="center">
5
- <span class="font-weight-bold text-h5">Assign Access Card to Unit</span>
6
- </v-row>
7
- </v-toolbar>
8
- <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-0">
2
+ <v-card width="100%" class="screen-modal">
3
+ <v-card-title>Assign Access Card to Unit</v-card-title>
4
+
5
+ <v-card-text class="screen-modal__body pa-0">
9
6
  <v-form v-model="validForm" :disabled="loading">
10
7
  <v-row no-gutters class="px-6 pt-4 pb-2">
11
8
  <!-- Building -->
@@ -147,10 +144,10 @@
147
144
  />
148
145
  <div class="text-caption mt-1">
149
146
  <template v-if="availableLoading">
150
- <span class="text-grey">Loading available count...</span>
147
+ <span class="screen-hint">Loading available count...</span>
151
148
  </template>
152
149
  <template v-else-if="availableCount !== null">
153
- <span :class="availableCount === 0 ? 'text-error' : 'text-grey'">
150
+ <span :class="availableCount === 0 ? 'text-error' : 'screen-hint'">
154
151
  Available: {{ availableCount }}
155
152
  </span>
156
153
  </template>
@@ -160,29 +157,24 @@
160
157
  </v-form>
161
158
  </v-card-text>
162
159
 
163
- <v-toolbar density="compact">
160
+ <v-card-actions class="screen-modal__footer">
164
161
  <v-row no-gutters>
165
- <v-col cols="6">
162
+ <v-col cols="6" class="pr-2">
166
163
  <v-btn
167
- tile
168
164
  block
169
- variant="text"
170
- class="text-none"
171
- size="48"
165
+ variant="outlined"
166
+ class="text-none screen-btn-ghost"
172
167
  @click="cancel"
173
168
  :disabled="loading"
174
169
  >
175
170
  Cancel
176
171
  </v-btn>
177
172
  </v-col>
178
- <v-col cols="6">
173
+ <v-col cols="6" class="pl-2">
179
174
  <v-btn
180
- tile
181
175
  block
182
176
  variant="flat"
183
- color="black"
184
- class="text-none"
185
- size="48"
177
+ class="text-none screen-btn-primary"
186
178
  :disabled="!validForm || loading || availableCount === 0 || (availableCount !== null && form.quantity > availableCount)"
187
179
  :loading="loading"
188
180
  @click="submit"
@@ -191,7 +183,7 @@
191
183
  </v-btn>
192
184
  </v-col>
193
185
  </v-row>
194
- </v-toolbar>
186
+ </v-card-actions>
195
187
  </v-card>
196
188
  </template>
197
189
  <script setup lang="ts">
@@ -1,20 +1,28 @@
1
1
  <template>
2
- <v-dialog :model-value="modelValue" width="450" persistent>
3
- <v-card width="100%">
4
- <v-toolbar density="compact" class="pl-4">
5
- <span class="font-weight-medium text-h5">Delete Card</span>
6
- </v-toolbar>
2
+ <!-- 520px is the handoff's `--modal-w`; the card wears `.screen-modal` for
3
+ the 16px corner, the `--card` surface and the modal shadow. The two
4
+ `v-toolbar`s it used for the title row and the footer were grey slabs
5
+ that stay grey on the dark theme - a `v-card-title` and the shared
6
+ `.screen-modal__footer` are the same rows on the card's own surface. -->
7
+ <v-dialog :model-value="modelValue" width="520" persistent>
8
+ <v-card class="screen-modal" width="100%">
9
+ <v-card-title>Delete Card</v-card-title>
7
10
 
8
- <v-card-text>
9
- <p class="text-subtitle-2 text-center mb-4">
11
+ <v-card-text class="screen-modal__body">
12
+ <p class="text-center mb-4">
10
13
  Are you sure you want to delete this card? This action cannot be
11
14
  undone.
12
15
  </p>
13
16
 
14
17
  <v-form v-model="validForm" :disabled="loading">
15
18
  <InputLabel class="text-capitalize font-weight-bold" title="Remarks" required />
19
+ <!-- Still a `v-textarea`, not an `AppField`: Delete Card is gated on
20
+ `validForm`, which only exists because this `:rules` runs.
21
+ `.filter-field` gives it the design's 10px corner and 13.5px
22
+ type without taking the validation away. -->
16
23
  <v-textarea
17
24
  v-model="remarks"
25
+ class="filter-field"
18
26
  placeholder="Enter remarks..."
19
27
  persistent-placeholder
20
28
  rows="3"
@@ -29,38 +37,32 @@
29
37
  </v-row>
30
38
  </v-card-text>
31
39
 
32
- <v-toolbar density="compact">
33
- <v-row no-gutters>
34
- <v-col cols="6">
35
- <v-btn
36
- tile
37
- block
38
- size="48"
39
- variant="text"
40
- class="text-none"
41
- :disabled="loading"
42
- @click="handleClose"
43
- >
44
- Close
45
- </v-btn>
46
- </v-col>
47
- <v-col cols="6">
48
- <v-btn
49
- tile
50
- block
51
- size="48"
52
- color="black"
53
- variant="flat"
54
- class="text-none"
55
- :loading="loading"
56
- :disabled="!validForm || loading"
57
- @click="handleConfirm"
58
- >
59
- Delete Card
60
- </v-btn>
61
- </v-col>
62
- </v-row>
63
- </v-toolbar>
40
+ <!-- The design's footer: two equal-width buttons, ghost then primary, on
41
+ a bordered strip. The confirming button was a hardcoded color="black"
42
+ fill - the same black on both themes. The handoff draws no
43
+ destructive button, so it takes the primary shape like every other
44
+ modal's confirm; the destructive-variant gap stays a design
45
+ decision. `loading` keeps it a v-btn: AppButton has no in-flight
46
+ state, and this is the only signal the delete is running. -->
47
+ <div class="screen-modal__footer">
48
+ <v-btn
49
+ variant="outlined"
50
+ class="screen-btn-ghost"
51
+ :disabled="loading"
52
+ @click="handleClose"
53
+ >
54
+ Close
55
+ </v-btn>
56
+ <v-btn
57
+ variant="flat"
58
+ class="screen-btn-primary"
59
+ :loading="loading"
60
+ :disabled="!validForm || loading"
61
+ @click="handleConfirm"
62
+ >
63
+ Delete Card
64
+ </v-btn>
65
+ </div>
64
66
  </v-card>
65
67
  </v-dialog>
66
68
  </template>
@@ -1,117 +1,109 @@
1
1
  <template>
2
2
  <v-dialog :model-value="modelValue" width="480" persistent>
3
- <v-card width="100%">
4
- <v-toolbar density="compact" color="black">
5
- <v-toolbar-title class="text-subtitle-1 font-weight-medium">
3
+ <v-card width="100%" class="screen-modal card-details">
4
+ <div class="card-details__head">
5
+ <v-card-title>
6
6
  Card Details — {{ card?.cardNo ?? "N/A" }}
7
- </v-toolbar-title>
8
- <v-btn icon @click="emit('update:modelValue', false)">
9
- <v-icon>mdi-close</v-icon>
10
- </v-btn>
11
- </v-toolbar>
7
+ </v-card-title>
8
+ <AppButton variant="icon" icon="mdi-close" aria-label="Close"
9
+ @click="emit('update:modelValue', false)" />
10
+ </div>
12
11
 
13
- <v-card-text style="max-height: 70vh; overflow-y: auto" class="pa-4">
12
+ <div class="screen-modal__body card-details__body">
14
13
  <div v-if="pending" class="d-flex justify-center align-center py-8">
15
- <v-progress-circular indeterminate color="black" />
14
+ <v-progress-circular indeterminate color="accent" />
16
15
  </div>
17
16
 
18
- <div v-else-if="!details" class="text-center text-grey py-8">
17
+ <div v-else-if="!details" class="card-details__empty">
19
18
  No details available.
20
19
  </div>
21
20
 
22
21
  <div v-else class="d-flex flex-column ga-3">
23
22
  <div>
24
- <div class="text-caption text-grey">Card No</div>
25
- <div class="text-body-2 font-weight-medium">{{ details.cardNo ?? "N/A" }}</div>
23
+ <div class="card-details__label">Card No</div>
24
+ <div class="card-details__value">{{ details.cardNo ?? "N/A" }}</div>
26
25
  </div>
27
26
 
28
27
  <div>
29
- <div class="text-caption text-grey">Type</div>
30
- <div class="text-body-2 font-weight-medium">{{ details.type ?? "N/A" }}</div>
28
+ <div class="card-details__label">Type</div>
29
+ <div class="card-details__value">{{ details.type ?? "N/A" }}</div>
31
30
  </div>
32
31
 
33
32
  <div v-if="details.status">
34
- <div class="text-caption text-grey">Status</div>
35
- <v-chip
36
- :color="statusColor(details.status)"
37
- variant="flat"
38
- size="x-small"
39
- class="text-capitalize font-weight-medium mt-1"
40
- >
41
- {{ details.status ?? "N/A" }}
42
- </v-chip>
33
+ <div class="card-details__label">Status</div>
34
+ <StatusChip class="mt-1 text-capitalize" :status="details.status" />
43
35
  </div>
44
36
 
45
37
  <div>
46
- <div class="text-caption text-grey">Activated</div>
47
- <div class="text-body-2 font-weight-medium">{{ details.isActivated ? "Yes" : "No" }}</div>
38
+ <div class="card-details__label">Activated</div>
39
+ <div class="card-details__value">{{ details.isActivated ? "Yes" : "No" }}</div>
48
40
  </div>
49
41
 
50
42
  <div v-if="details.site?.name">
51
- <div class="text-caption text-grey">Site</div>
52
- <div class="text-body-2 font-weight-medium">{{ details.site?.name ?? "N/A" }}</div>
43
+ <div class="card-details__label">Site</div>
44
+ <div class="card-details__value">{{ details.site?.name ?? "N/A" }}</div>
53
45
  </div>
54
46
 
55
47
  <div>
56
- <div class="text-caption text-grey">User</div>
57
- <div class="text-body-2 font-weight-medium">
48
+ <div class="card-details__label">User</div>
49
+ <div class="card-details__value">
58
50
  {{ details.userCred?.name ?? details.user?.name ?? "N/A" }}
59
51
  <template v-if="details.user?.email">({{ details.user.email }})</template>
60
52
  </div>
61
53
  </div>
62
54
 
63
55
  <div v-if="details.userType">
64
- <div class="text-caption text-grey">User Type</div>
65
- <div class="text-body-2 font-weight-medium">{{ details.userType }}</div>
56
+ <div class="card-details__label">User Type</div>
57
+ <div class="card-details__value">{{ details.userType }}</div>
66
58
  </div>
67
59
 
68
60
  <!-- Visitor / Resident Credentials -->
69
61
  <template v-if="details.userCred">
70
62
  <v-divider />
71
- <div class="text-caption font-weight-bold text-grey-darken-2 text-uppercase">
63
+ <div class="card-details__section">
72
64
  {{ details.userType ?? "Visitor Information" }}
73
65
  </div>
74
66
 
75
67
  <div v-if="details.userCred.email">
76
- <div class="text-caption text-grey">Email</div>
77
- <div class="text-body-2 font-weight-medium">{{ details.userCred.email }}</div>
68
+ <div class="card-details__label">Email</div>
69
+ <div class="card-details__value">{{ details.userCred.email }}</div>
78
70
  </div>
79
71
 
80
72
  <div v-if="details.userCred.nric">
81
- <div class="text-caption text-grey">NRIC</div>
82
- <div class="text-body-2 font-weight-medium">{{ details.userCred.nric }}</div>
73
+ <div class="card-details__label">NRIC</div>
74
+ <div class="card-details__value">{{ details.userCred.nric }}</div>
83
75
  </div>
84
76
 
85
77
  <div v-if="details.userCred.contact">
86
- <div class="text-caption text-grey">Contact</div>
87
- <div class="text-body-2 font-weight-medium">{{ details.userCred.contact }}</div>
78
+ <div class="card-details__label">Contact</div>
79
+ <div class="card-details__value">{{ details.userCred.contact }}</div>
88
80
  </div>
89
81
 
90
82
  <div v-if="details.userCred.company">
91
- <div class="text-caption text-grey">Company</div>
92
- <div class="text-body-2 font-weight-medium">{{ details.userCred.company }}</div>
83
+ <div class="card-details__label">Company</div>
84
+ <div class="card-details__value">{{ details.userCred.company }}</div>
93
85
  </div>
94
86
 
95
87
  <div v-if="details.userCred.plateNumber">
96
- <div class="text-caption text-grey">Plate Number</div>
97
- <div class="text-body-2 font-weight-medium">{{ details.userCred.plateNumber }}</div>
88
+ <div class="card-details__label">Plate Number</div>
89
+ <div class="card-details__value">{{ details.userCred.plateNumber }}</div>
98
90
  </div>
99
91
  <v-divider />
100
92
  </template>
101
93
 
102
94
  <div>
103
- <div class="text-caption text-grey">Created At</div>
104
- <div class="text-body-2 font-weight-medium">{{ formatDate(details.createdAt) }}</div>
95
+ <div class="card-details__label">Created At</div>
96
+ <div class="card-details__value">{{ formatDate(details.createdAt) }}</div>
105
97
  </div>
106
98
 
107
99
  <div>
108
- <div class="text-caption text-grey">Updated At</div>
109
- <div class="text-body-2 font-weight-medium">{{ formatDate(details.updatedAt) }}</div>
100
+ <div class="card-details__label">Updated At</div>
101
+ <div class="card-details__value">{{ formatDate(details.updatedAt) }}</div>
110
102
  </div>
111
103
 
112
104
  <div v-if="details.remarks">
113
- <div class="text-caption text-grey">Remarks</div>
114
- <div class="text-body-2 font-weight-medium">{{ details.remarks }}</div>
105
+ <div class="card-details__label">Remarks</div>
106
+ <div class="card-details__value">{{ details.remarks }}</div>
115
107
  </div>
116
108
 
117
109
  <!-- History -->
@@ -121,7 +113,7 @@
121
113
  class="d-flex align-center justify-space-between cursor-pointer"
122
114
  @click="historyOpen = !historyOpen"
123
115
  >
124
- <div class="text-caption font-weight-bold text-grey-darken-2 text-uppercase">
116
+ <div class="card-details__section">
125
117
  History ({{ details.history.length }})
126
118
  </div>
127
119
  <v-icon size="16" :icon="historyOpen ? 'mdi-chevron-up' : 'mdi-chevron-down'" />
@@ -137,37 +129,36 @@
137
129
  <div class="d-flex flex-column align-center" style="width: 20px; flex-shrink: 0;">
138
130
  <div
139
131
  class="timeline-dot rounded-circle mt-1"
140
- :class="(entry.type === 'assigned' || entry.visitorId) ? 'bg-blue' : 'bg-orange'"
132
+ :class="isAssignment(entry) ? 'timeline-dot--assigned' : 'timeline-dot--returned'"
141
133
  />
142
134
  <div
143
135
  v-if="idx < sortedHistory.length - 1"
144
- class="timeline-line bg-grey-lighten-2 mt-1"
136
+ class="timeline-line mt-1"
145
137
  />
146
138
  </div>
147
139
 
148
140
  <!-- Entry content -->
149
141
  <div class="d-flex flex-column ga-1 pb-4 flex-grow-1">
150
142
  <div class="d-flex align-center justify-space-between">
151
- <v-chip
152
- size="x-small"
153
- variant="tonal"
154
- :color="(entry.type === 'assigned' || entry.visitorId) ? 'blue' : 'orange'"
155
- :prepend-icon="(entry.type === 'assigned' || entry.visitorId) ? 'mdi-account-arrow-right' : 'mdi-arrow-u-left-top'"
156
- >
157
- {{ (entry.type === 'assigned' || entry.visitorId) ? "Assigned" : "Returned" }}
158
- </v-chip>
159
- <span class="text-caption text-grey">{{ formatDate(entry.createdAt) }}</span>
143
+ <StatusChip :status="isAssignment(entry) ? 'Assigned' : 'Returned'">
144
+ <v-icon
145
+ size="13"
146
+ class="mr-1"
147
+ :icon="isAssignment(entry) ? 'mdi-account-arrow-right' : 'mdi-arrow-u-left-top'"
148
+ />{{ isAssignment(entry) ? "Assigned" : "Returned" }}
149
+ </StatusChip>
150
+ <span class="card-details__label">{{ formatDate(entry.createdAt) }}</span>
160
151
  </div>
161
152
 
162
153
  <!-- Assignment entry -->
163
154
  <template v-if="entry.type === 'assigned' || entry.visitorId">
164
- <div class="text-body-2 font-weight-medium">{{ entry.name ?? "N/A" }}</div>
165
- <div class="text-caption text-grey-darken-1">
155
+ <div class="card-details__value">{{ entry.name ?? "N/A" }}</div>
156
+ <div class="card-details__sub">
166
157
  <span v-if="entry.nric">NRIC: {{ entry.nric }}</span>
167
158
  <span v-if="entry.contact" class="ml-2">· {{ entry.contact }}</span>
168
159
  <span v-if="entry.email && !entry.nric && !entry.contact">{{ entry.email }}</span>
169
160
  </div>
170
- <div class="text-caption text-grey-darken-1">
161
+ <div class="card-details__sub">
171
162
  <span v-if="entry.company">{{ entry.company }}</span>
172
163
  <span v-if="entry.plateNumber" class="ml-2">· {{ entry.plateNumber }}</span>
173
164
  </div>
@@ -176,12 +167,10 @@
176
167
  <!-- Return/checkout entry -->
177
168
  <template v-else>
178
169
  <div class="d-flex align-center ga-2">
179
- <v-chip size="x-small" variant="flat" :color="statusColor(entry.status?.toLowerCase())">
180
- {{ entry.status ?? "N/A" }}
181
- </v-chip>
182
- <span v-if="entry.damage" class="text-caption text-error">Damaged</span>
170
+ <StatusChip :status="entry.status" :label="entry.status ?? 'N/A'" />
171
+ <span v-if="entry.damage" class="card-details__damage">Damaged</span>
183
172
  </div>
184
- <div v-if="entry.remarks" class="text-caption text-grey-darken-1">
173
+ <div v-if="entry.remarks" class="card-details__sub">
185
174
  Remarks: {{ entry.remarks }}
186
175
  </div>
187
176
  </template>
@@ -190,7 +179,7 @@
190
179
  </template>
191
180
  </template>
192
181
  </div>
193
- </v-card-text>
182
+ </div>
194
183
  </v-card>
195
184
  </v-dialog>
196
185
  </template>
@@ -245,15 +234,12 @@ watch(
245
234
  }
246
235
  );
247
236
 
248
- function statusColor(status: string) {
249
- const map: Record<string, string> = {
250
- active: "success",
251
- assigned: "primary",
252
- replaced: "orange",
253
- deleted: "error",
254
- available: "grey",
255
- };
256
- return map[status] ?? "grey";
237
+ /**
238
+ * A history entry is an ASSIGNMENT if it says so or carries a visitor; the
239
+ * alternative is a return. Was written out three times inline.
240
+ */
241
+ function isAssignment(entry: Record<string, any>) {
242
+ return entry.type === "assigned" || Boolean(entry.visitorId);
257
243
  }
258
244
 
259
245
  function formatDate(date: string) {
@@ -270,14 +256,74 @@ function formatDate(date: string) {
270
256
  </script>
271
257
 
272
258
  <style scoped>
259
+ .card-details__head {
260
+ display: flex;
261
+ align-items: center;
262
+ justify-content: space-between;
263
+ padding-right: 12px;
264
+ }
265
+
266
+ .card-details__body {
267
+ padding: 0 20px 20px;
268
+ max-height: 70vh;
269
+ }
270
+
271
+ .card-details__label {
272
+ font-size: var(--fs-breadcrumb);
273
+ color: var(--text2);
274
+ }
275
+
276
+ .card-details__value {
277
+ font-size: var(--fs-cell);
278
+ font-weight: var(--fw-cell);
279
+ color: var(--text);
280
+ }
281
+
282
+ .card-details__section {
283
+ font-size: var(--fs-section-label);
284
+ font-weight: var(--fw-section-label);
285
+ letter-spacing: var(--ls-section-label);
286
+ text-transform: uppercase;
287
+ color: var(--text2);
288
+ }
289
+
290
+ .card-details__sub {
291
+ font-size: var(--fs-breadcrumb);
292
+ color: var(--text2);
293
+ }
294
+
295
+ .card-details__empty {
296
+ padding: 32px 0;
297
+ text-align: center;
298
+ font-size: var(--fs-cell);
299
+ color: var(--muted);
300
+ }
301
+
302
+ .card-details__damage {
303
+ font-size: var(--fs-breadcrumb);
304
+ color: var(--err);
305
+ }
306
+
273
307
  .timeline-dot {
274
308
  width: 10px;
275
309
  height: 10px;
276
310
  flex-shrink: 0;
277
311
  }
312
+
313
+ /* Were `bg-blue` / `bg-orange` / `bg-grey-lighten-2` - three fixed Vuetify hues
314
+ that do not follow the theme. Same meanings, as tokens. */
315
+ .timeline-dot--assigned {
316
+ background: var(--info);
317
+ }
318
+
319
+ .timeline-dot--returned {
320
+ background: var(--warn);
321
+ }
322
+
278
323
  .timeline-line {
279
324
  width: 2px;
280
325
  flex-grow: 1;
281
326
  min-height: 16px;
327
+ background: var(--border);
282
328
  }
283
329
  </style>