@7365admin1/layer-common 3.2.7 → 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 (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 +241 -79
  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,88 +1,80 @@
1
1
  <template>
2
2
  <v-row no-gutters>
3
3
  <v-col cols="12">
4
- <v-row no-gutters class="mb-4" align="center">
5
- <v-col cols="12" md="6">
6
- <v-btn
7
- rounded="pill"
8
- variant="outlined"
9
-
10
- size="large"
11
- class="text-none px-8"
4
+ <!-- Title row; nav calls this screen "Routes". The Add pill moves onto it
5
+ and the search onto the card's toolbar, where the design draws them. -->
6
+ <PageHeader title="Routes">
7
+ <template #actions>
8
+ <AppButton
9
+ v-if="props.canCreateNfcPatrol"
10
+ icon="mdi-plus"
12
11
  @click="dialogAdd = true"
13
12
  >
14
13
  Add Route
15
- </v-btn>
16
- </v-col>
17
- <v-col cols="12" md="6" class="d-flex justify-end">
18
- <v-text-field
14
+ </AppButton>
15
+ </template>
16
+ </PageHeader>
17
+
18
+ <!--
19
+ Was `v-card` + `v-data-table` + a `grey-lighten-4` `v-toolbar` BELOW the
20
+ table holding the page range and pager (a white slab that stayed white in
21
+ dark mode). `TableMain` draws the same card with that row on top, which
22
+ is where the design puts it. Same headers, items, page size, row click
23
+ and item slots; the refresh button is wired to the refresh this screen
24
+ already calls after add/edit.
25
+ -->
26
+ <TableMain
27
+ :headers="headers"
28
+ :items="items"
29
+ :page="page"
30
+ :pages="pages"
31
+ :pageRange="pageRange"
32
+ show-header
33
+ @refresh="getRoutesRefresh()"
34
+ @update:page="page = $event"
35
+ @row-click="(data: any) => handleRowClick(null, data)"
36
+ >
37
+ <template #prepend-additional>
38
+ <AppField
19
39
  v-model="searchInput"
20
- density="comfortable"
40
+ search
41
+ compact
21
42
  placeholder="Search"
22
- clearable
23
- append-inner-icon="mdi-magnify"
24
- hide-details
25
- variant="outlined"
26
- rounded="lg"
27
- style="max-width: 400px"
28
- bg-color="white"
43
+ klass="app-toolbar__search"
29
44
  />
30
- </v-col>
31
- </v-row>
32
-
33
- <v-card width="100%" variant="outlined" border="thin" rounded="lg">
34
-
35
- <v-card-text class="pa-0">
36
- <v-data-table
37
- :headers="headers"
38
- :items="items"
39
- :items-per-page="20"
40
- fixed-header
41
- hide-default-footer
42
- @click:row="handleRowClick"
43
- style="max-height: calc(100vh - 300px)"
44
- class="nfc-route-table"
45
- hover
46
- >
47
- <template #item.numberOfCheckpoints="{ value }">
48
- <div class="text-center">{{ value || "--" }}</div>
49
- </template>
50
-
51
- <template #item.tolerance="{ item }">
52
- <div class="text-center">
53
- <div>{{ item.tolerance ? `${item.tolerance} mins` : "--" }}</div>
54
- <div class="text-grey">{{ item.totalTravelTime ? `${item.totalTravelTime} mins` : "--" }}</div>
55
- </div>
56
- </template>
57
-
58
- <template #item.schedules="{ value }">
59
- <span v-if="Array.isArray(value) && value.length > 0">
60
- {{ value.map(s => `${s.start}-${s.end}`).join(", ") }}
61
- </span>
62
- <span v-else>--</span>
63
- </template>
64
-
65
- <template #item.repeatDays="{ value }">
66
- <span>{{ value || "--" }} </span>
67
- </template>
68
- </v-data-table>
69
- </v-card-text>
70
-
71
- <v-toolbar density="compact" color="grey-lighten-4">
72
- <template #append>
73
- <v-row no-gutters justify="end" align="center">
74
- <span class="mr-2 text-caption text-fontgray">
75
- {{ pageRange }}
76
- </span>
77
- <local-pagination v-model="page" :length="pages" />
78
- </v-row>
79
- </template>
80
- </v-toolbar>
81
- </v-card>
45
+ </template>
46
+
47
+ <template #item.numberOfCheckpoints="{ value }">
48
+ <div class="text-center">{{ value || "--" }}</div>
49
+ </template>
50
+
51
+ <template #item.tolerance="{ item }">
52
+ <div class="text-center">
53
+ <div>{{ item.tolerance ? `${item.tolerance} mins` : "--" }}</div>
54
+ <div class="text-grey">{{ item.totalTravelTime ? `${item.totalTravelTime} mins` : "--" }}</div>
55
+ </div>
56
+ </template>
57
+
58
+ <template #item.schedules="{ value }">
59
+ <span v-if="Array.isArray(value) && value.length > 0">
60
+ {{ value.map((s: any) => `${s.start}-${s.end}`).join(", ") }}
61
+ </span>
62
+ <span v-else>--</span>
63
+ </template>
64
+
65
+ <template #item.repeatDays="{ value }">
66
+ <span>{{ value || "--" }} </span>
67
+ </template>
68
+ </TableMain>
82
69
  </v-col>
83
70
 
84
71
  <!-- Add Dialog -->
85
- <v-dialog v-model="dialogAdd" persistent max-width="900">
72
+ <v-dialog
73
+ v-if="props.canCreateNfcPatrol"
74
+ v-model="dialogAdd"
75
+ persistent
76
+ max-width="900"
77
+ >
86
78
  <NFCPatrolRouteForm
87
79
  title="Add Route"
88
80
  :site="props.site"
@@ -94,7 +86,12 @@
94
86
  </v-dialog>
95
87
 
96
88
  <!-- Edit Dialog -->
97
- <v-dialog v-model="dialogEdit" persistent max-width="900">
89
+ <v-dialog
90
+ v-if="props.canUpdateNfcPatrol"
91
+ v-model="dialogEdit"
92
+ persistent
93
+ max-width="900"
94
+ >
98
95
  <NFCPatrolRouteForm
99
96
  title="Edit Route"
100
97
  :site="props.site"
@@ -190,7 +187,10 @@
190
187
 
191
188
  <v-toolbar class="pa-0" density="compact">
192
189
  <v-row no-gutters>
193
- <v-col cols="6" class="pa-0">
190
+ <v-col
191
+ :cols="props.canUpdateNfcPatrol || props.canDeleteNfcPatrol ? 6 : 12"
192
+ class="pa-0"
193
+ >
194
194
  <v-btn
195
195
  block
196
196
  variant="text"
@@ -203,7 +203,11 @@
203
203
  </v-btn>
204
204
  </v-col>
205
205
 
206
- <v-col cols="6" class="pa-0">
206
+ <v-col
207
+ v-if="props.canUpdateNfcPatrol || props.canDeleteNfcPatrol"
208
+ cols="6"
209
+ class="pa-0"
210
+ >
207
211
  <v-menu>
208
212
  <template #activator="{ props }">
209
213
  <v-btn
@@ -220,13 +224,17 @@
220
224
  </template>
221
225
 
222
226
  <v-list class="pa-0">
223
- <v-list-item @click="openDialogEdit()">
227
+ <v-list-item v-if="props.canUpdateNfcPatrol" @click="openDialogEdit()">
224
228
  <v-list-item-title class="text-subtitle-2">
225
229
  Edit Route
226
230
  </v-list-item-title>
227
231
  </v-list-item>
228
232
 
229
- <v-list-item @click="openDialogDelete()" class="text-red">
233
+ <v-list-item
234
+ v-if="props.canDeleteNfcPatrol"
235
+ @click="openDialogDelete()"
236
+ class="text-red"
237
+ >
230
238
  <v-list-item-title class="text-subtitle-2">
231
239
  Delete Route
232
240
  </v-list-item-title>
@@ -240,7 +248,12 @@
240
248
  </v-dialog>
241
249
 
242
250
  <!-- Delete Dialog -->
243
- <v-dialog v-model="dialogDelete" persistent width="540">
251
+ <v-dialog
252
+ v-if="props.canDeleteNfcPatrol"
253
+ v-model="dialogDelete"
254
+ persistent
255
+ width="540"
256
+ >
244
257
  <v-card width="100%">
245
258
  <v-card-text
246
259
  style="max-height: 100vh; overflow-y: auto"
@@ -304,6 +317,18 @@ const props = defineProps({
304
317
  type: String,
305
318
  required: true,
306
319
  },
320
+ canCreateNfcPatrol: {
321
+ type: Boolean,
322
+ default: false,
323
+ },
324
+ canUpdateNfcPatrol: {
325
+ type: Boolean,
326
+ default: false,
327
+ },
328
+ canDeleteNfcPatrol: {
329
+ type: Boolean,
330
+ default: false,
331
+ },
307
332
  headers: {
308
333
  type: Array as PropType<Array<any>>,
309
334
  default: () => [
@@ -406,6 +431,8 @@ const messageSnackbar = ref(false);
406
431
  const messageColor = ref("");
407
432
 
408
433
  async function openDialogEdit() {
434
+ if (!props.canUpdateNfcPatrol) return;
435
+
409
436
  try {
410
437
  // Fetch the full route details from the API
411
438
  const response = await getRouteById(selectedRoute.value._id);
@@ -431,6 +458,8 @@ async function openDialogEdit() {
431
458
  }
432
459
 
433
460
  function openDialogDelete() {
461
+ if (!props.canDeleteNfcPatrol) return;
462
+
434
463
  dialogDelete.value = true;
435
464
 
436
465
  if (dialogPreview.value) dialogPreview.value = false;
@@ -502,6 +531,8 @@ function handleRowClick(_: any, data: any) {
502
531
  }
503
532
 
504
533
  async function submitDelete() {
534
+ if (!props.canDeleteNfcPatrol) return;
535
+
505
536
  try {
506
537
  await deleteRouteById(selectedRoute.value._id ?? "");
507
538
  await getRoutesRefresh();
@@ -513,27 +544,9 @@ async function submitDelete() {
513
544
  }
514
545
  }
515
546
  </script>
516
-
517
- <style scoped>
518
- .nfc-route-table :deep(.v-data-table__th) {
519
- background-color: rgb(250, 250, 250);
520
- font-weight: 500;
521
- white-space: pre-line;
522
- padding: 16px 12px !important;
523
- border-bottom: 1px solid rgba(0, 0, 0, 0.12);
524
- }
525
-
526
- .nfc-route-table :deep(.v-data-table__td) {
527
- padding: 16px 12px !important;
528
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
529
- }
530
-
531
- .nfc-route-table :deep(tbody tr:hover) {
532
- background-color: rgba(0, 0, 0, 0.02) !important;
533
- cursor: pointer;
534
- }
535
-
536
- .nfc-route-table :deep(.v-data-table__tr) {
537
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
538
- }
539
- </style>
547
+ <!--
548
+ The screen's own table CSS is gone with the `v-data-table` it styled: it
549
+ hardcoded an `rgb(250,250,250)` header fill and black-alpha borders, which
550
+ stay light in dark mode. `TableMain` draws the design's header row and
551
+ borders from the theme tokens instead.
552
+ -->
@@ -1,7 +1,12 @@
1
1
  <template>
2
2
  <v-row no-gutters>
3
3
  <v-col cols="12">
4
- <v-card width="100%" variant="outlined" border="thin" rounded="lg">
4
+ <!--
5
+ `AppCard` for the outlined card. This mounts inside PM `settings.vue`'s
6
+ "NFC Patrol Settings" expansion panel, which already draws that
7
+ heading - so NO `PageHeader`, or the panel would name itself twice.
8
+ -->
9
+ <AppCard>
5
10
  <v-row no-gutters class="pa-6" align="center">
6
11
  <v-col cols="12" md="7" class="pr-md-8">
7
12
  <div class="text-subtitle-1 font-weight-medium">
@@ -36,10 +41,15 @@
36
41
 
37
42
  <v-row no-gutters class="pa-6 pt-0">
38
43
  <v-col cols="12" class="d-flex justify-end">
44
+ <!--
45
+ Stays a `v-btn`: `AppButton` has no `loading`, and dropping the
46
+ spinner would lose the only signal this save is in flight. The
47
+ design's fill comes from `.screen-btn-primary` instead of
48
+ Vuetify's `color="primary"` - same click, same `loading` gate.
49
+ -->
39
50
  <v-btn
40
51
  text="Save"
41
- color="primary"
42
- class="text-none"
52
+ class="screen-btn-primary"
43
53
  size="large"
44
54
  variant="flat"
45
55
  @click="submit"
@@ -47,7 +57,7 @@
47
57
  />
48
58
  </v-col>
49
59
  </v-row>
50
- </v-card>
60
+ </AppCard>
51
61
  </v-col>
52
62
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
53
63
  </v-row>
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <v-card width="100%">
3
- <v-toolbar>
3
+ <v-toolbar class="screen-dialog-bar">
4
4
  <v-row no-gutters class="fill-height px-6" align="center">
5
- <span class="font-weight-bold text-h5 text-capitalize">
5
+ <span class="screen-card-title text-capitalize">
6
6
  {{ title }}
7
7
  </span>
8
8
  </v-row>
@@ -15,8 +15,8 @@
15
15
  <v-form ref="formRef" v-model="valid" @submit.prevent="submit">
16
16
  <v-row no-gutters>
17
17
  <v-col cols="12" class="mb-4">
18
- <label class="text-subtitle-2 font-weight-medium mb-2">
19
- Name <span class="text-red">*</span>
18
+ <label class="nfc-tag__label mb-2">
19
+ Name <span class="text-error">*</span>
20
20
  </label>
21
21
  <v-text-field
22
22
  v-model="form.name"
@@ -29,8 +29,8 @@
29
29
  </v-col>
30
30
 
31
31
  <v-col cols="12" class="mb-4">
32
- <label class="text-subtitle-2 font-weight-medium mb-2">
33
- Tag ID <span class="text-red">*</span>
32
+ <label class="nfc-tag__label mb-2">
33
+ Tag ID <span class="text-error">*</span>
34
34
  </label>
35
35
  <v-text-field
36
36
  v-model="form.tagID"
@@ -47,7 +47,7 @@
47
47
  </v-form>
48
48
  </v-card-text>
49
49
 
50
- <v-toolbar class="pa-0" density="compact">
50
+ <v-toolbar class="pa-0 screen-dialog-bar" density="compact">
51
51
  <v-row no-gutters>
52
52
  <v-col cols="6" class="pa-0">
53
53
  <v-btn
@@ -64,6 +64,10 @@
64
64
  </v-col>
65
65
 
66
66
  <v-col cols="6" class="pa-0">
67
+ <!-- Was `color="black"`: a fixed slab that stayed black on the dark
68
+ card. `primary-button` is the theme's FILL token, and its pair
69
+ `on-primary-button` supplies the white label - unlike `primary`,
70
+ which is a FOREGROUND here (links, icons, text buttons). -->
67
71
  <v-btn
68
72
  block
69
73
  tile
@@ -71,7 +75,7 @@
71
75
  class="text-none"
72
76
  size="large"
73
77
  height="48"
74
- color="black"
78
+ color="primary-button"
75
79
  @click="submit"
76
80
  :loading="loading"
77
81
  :disabled="!valid || loading || !hasChanges"
@@ -234,3 +238,13 @@ async function submit() {
234
238
  }
235
239
  }
236
240
  </script>
241
+
242
+ <style scoped>
243
+ /* Was `text-subtitle-2`, Vuetify's 14px. The required asterisk beside it
244
+ keeps `text-error`; only the label's size and ink move. */
245
+ .nfc-tag__label {
246
+ font-size: var(--fs-cell);
247
+ font-weight: 500;
248
+ color: var(--text);
249
+ }
250
+ </style>
@@ -1,53 +1,51 @@
1
1
  <template>
2
2
  <v-row no-gutters>
3
3
  <v-col cols="12">
4
- <v-card width="100%" variant="outlined" border="thin" rounded="lg">
5
- <v-toolbar density="compact" color="grey-lighten-4 pl-2 pr-4">
6
- <template #prepend>
4
+ <!--
5
+ Two `grey-lighten-4` `v-toolbar`s - one above the table for refresh and
6
+ Add, one below it for the page range and the pager - become the ONE
7
+ `CardToolbar` the design draws. The range is the toolbar's own count
8
+ rather than a `text-font gray` caption; the pager keeps its model and
9
+ its `@update:value` emit, and Add keeps opening the same dialog.
10
+
11
+ This component mounts inside PM `settings.vue`'s "NFC Management"
12
+ expansion panel, which already draws that heading - so NO `PageHeader`
13
+ here, or the panel would read its name twice.
14
+ -->
15
+ <AppCard class="table-card">
16
+ <CardToolbar :count="pageRange">
17
+ <template #left>
7
18
  <v-btn fab icon density="comfortable" @click="getNFCTagRefresh()">
8
19
  <v-icon>mdi-refresh</v-icon>
9
20
  </v-btn>
10
21
  </template>
11
22
 
12
- <template #append>
13
- <v-btn
14
- variant="flat"
15
- color="primary"
16
- class="text-none"
17
- @click="dialogAdd = true"
18
- >
19
- Add
20
- </v-btn>
23
+ <template #right>
24
+ <local-pagination
25
+ v-model="page"
26
+ :length="pages"
27
+ @update:value="$emit('update:value', $event)"
28
+ />
29
+ <AppButton @click="dialogAdd = true">Add</AppButton>
21
30
  </template>
22
- </v-toolbar>
23
-
24
- <v-card-text class="pa-0">
25
- <v-data-table
26
- :headers="headers"
27
- :items="items"
28
- fixed-header
29
- hide-default-footer
30
- @click:row="handleRowClick"
31
- style="max-height: 300px"
32
- >
33
- </v-data-table>
34
- </v-card-text>
35
-
36
- <v-toolbar density="compact" color="grey-lighten-4">
37
- <template #append>
38
- <v-row no-gutters justify="end" align="center">
39
- <span class="mr-2 text-caption text-font gray">
40
- {{ pageRange }}
41
- </span>
42
- <local-pagination
43
- v-model="page"
44
- :length="pages"
45
- @update:value="$emit('update:value', $event)"
46
- />
47
- </v-row>
31
+ </CardToolbar>
32
+
33
+ <v-data-table
34
+ :headers="headers"
35
+ :items="items"
36
+ fixed-header
37
+ hide-default-footer
38
+ @click:row="handleRowClick"
39
+ style="max-height: 300px"
40
+ >
41
+ <!-- The status column printed the raw word as plain text. `StatusChip`
42
+ prints the SAME word - it is given the item's own value - and
43
+ only adds the design's tone from the shared map. -->
44
+ <template #item.status="{ item }">
45
+ <StatusChip :status="(item as any).status" />
48
46
  </template>
49
- </v-toolbar>
50
- </v-card>
47
+ </v-data-table>
48
+ </AppCard>
51
49
  </v-col>
52
50
 
53
51
  <!-- Add Dialog -->
@@ -79,7 +77,7 @@
79
77
  <!-- Preview Dialog -->
80
78
  <v-dialog v-model="dialogPreview" persistent width="540">
81
79
  <v-card width="100%">
82
- <v-toolbar>
80
+ <v-toolbar class="screen-dialog-bar">
83
81
  <v-row no-gutters class="fill-height px-6" align="center">
84
82
  <span class="font-weight-bold text-h5 text-capitalize">
85
83
  NFC Tag Details
@@ -110,7 +108,7 @@
110
108
  </v-row>
111
109
  </v-card-text>
112
110
 
113
- <v-toolbar class="pa-0" density="compact">
111
+ <v-toolbar class="pa-0 screen-dialog-bar" density="compact">
114
112
  <v-row no-gutters>
115
113
  <v-col cols="6" class="pa-0">
116
114
  <v-btn
@@ -171,7 +169,7 @@
171
169
  Are you sure you want to delete this NFC Tag?
172
170
  </v-card-text>
173
171
 
174
- <v-toolbar class="pa-0" density="compact">
172
+ <v-toolbar class="pa-0 screen-dialog-bar" density="compact">
175
173
  <v-row no-gutters>
176
174
  <v-col cols="6" class="pa-0">
177
175
  <v-btn
@@ -17,6 +17,13 @@
17
17
  </template>
18
18
 
19
19
 
20
+ <!-- A failed request is not an empty result. Saying "no patrol log found"
21
+ for a 500 states something about the data the server never said. -->
22
+ <ReportEmptyState
23
+ v-else-if="!loading && failed"
24
+ title="Report unavailable"
25
+ message="The patrol logs could not be loaded. Please try again."
26
+ />
20
27
  <ReportEmptyState
21
28
  v-else-if="!loading && notFound"
22
29
  title="No Daily Report"
@@ -51,7 +58,7 @@ defineEmits<{
51
58
  export: [type: "pdf" | "csv"];
52
59
  }>();
53
60
 
54
- const { report, loading, notFound, fetchReport } = useNFCPatrolDailyReport(
61
+ const { report, loading, notFound, failed, fetchReport } = useNFCPatrolDailyReport(
55
62
  props.filters,
56
63
  props.site,
57
64
  );
@@ -21,6 +21,13 @@
21
21
  />
22
22
  </template>
23
23
 
24
+ <!-- A failed request is not an empty result. Saying "no patrol log found"
25
+ for a 500 states something about the data the server never said. -->
26
+ <ReportEmptyState
27
+ v-else-if="!loading && failed"
28
+ title="Report unavailable"
29
+ message="The patrol logs could not be loaded. Please try again."
30
+ />
24
31
  <ReportEmptyState
25
32
  v-else-if="!loading"
26
33
  title="No Monthly Report"
@@ -55,7 +62,7 @@ defineEmits<{
55
62
  export: [type: "pdf" | "csv"];
56
63
  }>();
57
64
 
58
- const { report, loading, notFound, fetchReport } =
65
+ const { report, loading, notFound, failed, fetchReport } =
59
66
  useNFCPatrolMonthlyReport(props.filters, props.site);
60
67
 
61
68
  watch(
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="mt-6">
3
- <div class="text-h6 font-weight-bold mb-4">
3
+ <div class="screen-card-title mb-4">
4
4
  {{ title }}
5
5
  </div>
6
6
 
@@ -26,8 +26,10 @@
26
26
  {{ row.checked }}
27
27
  </td>
28
28
 
29
+ <!-- A month with no checkpoint due has no rate. "0%" there reads as
30
+ total failure, so it says nothing instead. -->
29
31
  <td class="text-center">
30
- {{ row.checkedPercentage }}%
32
+ {{ row.checkedPercentage === null ? "—" : `${row.checkedPercentage}%` }}
31
33
  </td>
32
34
 
33
35
  <td class="text-center">
@@ -35,7 +37,7 @@
35
37
  </td>
36
38
 
37
39
  <td class="text-center">
38
- {{ row.missedPercentage }}%
40
+ {{ row.missedPercentage === null ? "—" : `${row.missedPercentage}%` }}
39
41
  </td>
40
42
  </tr>
41
43
  </tbody>
@@ -1,9 +1,24 @@
1
+ <!--
2
+ The checkpoint-by-checkpoint rows of a patrol report.
3
+
4
+ Same change as the two tables beside it: off `variant="flat"` (no border on
5
+ either theme) and `border-thin` (a hairline the theme does not own) onto
6
+ `.table-card`, which is the design's table surface and styles the raw
7
+ `v-table`'s header row and cells.
8
+
9
+ `statusColor()` keeps returning `success`/`error`: those are the design
10
+ tokens by way of `utils/theme.ts`, and `utils/status.ts` does not name
11
+ "Skipped", so routing this through `StatusChip` would turn a red skipped
12
+ checkpoint neutral. That is a design decision, not a conversion.
13
+ -->
1
14
  <template>
2
- <v-card variant="flat" rounded="lg">
3
- <v-card-title>Patrol Activity</v-card-title>
15
+ <div class="table-card table-card__shrink">
16
+ <div class="activity-table__head">
17
+ <span class="screen-card-title">Patrol Activity</span>
18
+ </div>
4
19
 
5
- <v-card-text class="pa-0">
6
- <v-table class="border-thin">
20
+ <div>
21
+ <v-table>
7
22
  <thead>
8
23
  <tr>
9
24
  <th>Checkpoints</th>
@@ -58,8 +73,8 @@
58
73
  </tr>
59
74
  </tbody>
60
75
  </v-table>
61
- </v-card-text>
62
- </v-card>
76
+ </div>
77
+ </div>
63
78
  </template>
64
79
 
65
80
  <script setup lang="ts">
@@ -90,6 +105,11 @@ function handleActivityClick(activity: NFCPatrolActivity) {
90
105
  </script>
91
106
 
92
107
  <style scoped>
108
+ .activity-table__head {
109
+ padding: 14px var(--cellpad-x);
110
+ border-bottom: 1px solid var(--border);
111
+ }
112
+
93
113
  .cursor-pointer {
94
114
  cursor: pointer;
95
115
  }