@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
@@ -0,0 +1,258 @@
1
+ /**
2
+ * THE RESPONSIVE SHELL PRIMITIVES.
3
+ *
4
+ * The prototype is not responsive. Rendered at 360 and 768 its sidebar stays a
5
+ * fixed 252px, the content column is clipped, and the hamburger only hides the
6
+ * rail instead of overlaying it - so there is no reference to copy and this
7
+ * behaviour is designed rather than transcribed. It is built from the same
8
+ * tokens, so nothing here introduces a colour, a size or a radius of its own.
9
+ *
10
+ * Breakpoints, and why these:
11
+ * >= 1024 the layout the design draws: 252px rail beside the content.
12
+ * < 1024 the rail becomes an overlay drawer behind the hamburger. 252px
13
+ * out of 1024 is a quarter of the screen; below that the content
14
+ * column stops being usable before the rail stops fitting.
15
+ * < 768 one column. Filter rows stack, the breadcrumb truncates.
16
+ * < 600 modals go near-full-screen with a 16px inset.
17
+ *
18
+ * TWO KINDS OF RULE LIVE HERE, and the distinction is deliberate:
19
+ *
20
+ * 1. Token adjustments at a breakpoint. These change what `--content-pad-x`
21
+ * and friends MEAN on a small screen. They reach every surface that reads
22
+ * the tokens and touch nothing that does not, which is why the padding is
23
+ * done this way instead of as a rule on someone else's markup.
24
+ *
25
+ * 2. Named primitives (`.app-shell`, `.app-topbar`, `.filter-row`,
26
+ * `.table-scroll`, `.modal-body`). These are what Phase 2 onwards builds
27
+ * the shell out of. NOTHING in Phase 1 applies them to a module screen -
28
+ * the primitives exist and are reviewable; rolling them across the
29
+ * product is the next phase's work.
30
+ *
31
+ * The one thing here that DOES reach the existing product is the touch-target
32
+ * rule at the bottom, and that is on purpose - see the note above it.
33
+ */
34
+
35
+ /* ------------------------------------------------------------------ */
36
+ /* 1. What the tokens mean on a smaller screen. */
37
+ /* ------------------------------------------------------------------ */
38
+
39
+ @media (max-width: 1023.98px) {
40
+ :root {
41
+ --content-pad-x: 20px;
42
+ --content-pad-y: 18px;
43
+ }
44
+ }
45
+
46
+ @media (max-width: 767.98px) {
47
+ :root {
48
+ --content-pad-x: 16px;
49
+ --content-pad-y: 16px;
50
+ --content-pad-bottom: 28px;
51
+ /* The row keeps its vertical rhythm and gives back side padding, which is
52
+ what buys a table its columns before it has to scroll. */
53
+ --cellpad-x: 14px;
54
+ --grid-gap: 10px;
55
+ }
56
+ }
57
+
58
+ @media (max-width: 599.98px) {
59
+ :root {
60
+ --content-pad-x: 14px;
61
+ /* 22px on a 360px screen is a headline that wraps to three lines. */
62
+ --fs-h1: 19px;
63
+ --fs-kpi: 26px;
64
+ }
65
+ }
66
+
67
+ /* ------------------------------------------------------------------ */
68
+ /* 2. The primitives. */
69
+ /* ------------------------------------------------------------------ */
70
+
71
+ /**
72
+ * The shell. A flex row at desktop; at < 1024 the rail is out of flow (it is
73
+ * an overlay) and the content column takes the full width.
74
+ *
75
+ * `min-width: 0` on the content column is not decoration. A flex child's
76
+ * default `min-width: auto` refuses to shrink below its content, so ONE wide
77
+ * table pushes the whole column past the viewport and the page scrolls
78
+ * sideways with the sidebar attached. This is the line that stops that.
79
+ */
80
+ .app-shell {
81
+ display: flex;
82
+ min-height: 100vh;
83
+ background: var(--bg);
84
+ }
85
+
86
+ .app-shell__rail {
87
+ flex: 0 0 var(--sidebar-w);
88
+ width: var(--sidebar-w);
89
+ background: var(--sidebar);
90
+ border-right: 1px solid var(--border);
91
+ }
92
+
93
+ .app-shell__content {
94
+ flex: 1 1 auto;
95
+ min-width: 0;
96
+ display: flex;
97
+ flex-direction: column;
98
+ }
99
+
100
+ .app-content {
101
+ flex: 1 1 auto;
102
+ min-width: 0;
103
+ padding: var(--content-pad-y) var(--content-pad-x) var(--content-pad-bottom);
104
+ }
105
+
106
+ @media (max-width: 1023.98px) {
107
+ .app-shell__rail {
108
+ display: none;
109
+ }
110
+ }
111
+
112
+ /** The 58px top bar. Its two ends are fixed; the middle is what gives way. */
113
+ .app-topbar {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: 12px;
117
+ height: var(--topbar-h);
118
+ min-height: var(--topbar-h);
119
+ padding: 0 var(--content-pad-x);
120
+ background: var(--card);
121
+ border-bottom: 1px solid var(--border);
122
+ }
123
+
124
+ .app-topbar__crumb {
125
+ flex: 1 1 auto;
126
+ min-width: 0;
127
+ font-size: var(--fs-breadcrumb);
128
+ color: var(--muted);
129
+ /* Truncates rather than wrapping: a two-line breadcrumb would break the
130
+ 58px bar, and the site name is the half worth losing first. */
131
+ overflow: hidden;
132
+ white-space: nowrap;
133
+ text-overflow: ellipsis;
134
+ }
135
+
136
+ .app-topbar__actions {
137
+ flex: 0 0 auto;
138
+ display: flex;
139
+ align-items: center;
140
+ gap: 8px;
141
+ }
142
+
143
+ /** The hamburger only exists where the rail does not. */
144
+ .app-topbar__toggle {
145
+ display: none;
146
+ }
147
+
148
+ @media (max-width: 1023.98px) {
149
+ .app-topbar__toggle {
150
+ display: inline-flex;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * A filter row wraps before it stacks. Wrapping keeps two short selects side
156
+ * by side on a 768px tablet, where forcing them full-width would waste half
157
+ * the row; below 600 there is no room for two, so they stack.
158
+ */
159
+ .filter-row {
160
+ display: flex;
161
+ flex-wrap: wrap;
162
+ gap: var(--grid-gap);
163
+ align-items: center;
164
+ }
165
+
166
+ .filter-row > * {
167
+ flex: 1 1 200px;
168
+ min-width: 0;
169
+ }
170
+
171
+ @media (max-width: 599.98px) {
172
+ .filter-row {
173
+ flex-direction: column;
174
+ align-items: stretch;
175
+ }
176
+
177
+ .filter-row > * {
178
+ flex: 1 1 auto;
179
+ width: 100%;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * A table scrolls sideways inside its own card rather than reflowing into
185
+ * stacked key/value blocks. The design is a table card and its columns carry
186
+ * meaning by position; restacking them at 360 would be a different design, not
187
+ * a smaller one - and it is the kind of change rule 2 rules out. So the card
188
+ * edge stays put and the table moves within it.
189
+ */
190
+ .table-scroll {
191
+ overflow-x: auto;
192
+ -webkit-overflow-scrolling: touch;
193
+ overscroll-behavior-x: contain;
194
+ }
195
+
196
+ .table-scroll > table {
197
+ min-width: max-content;
198
+ }
199
+
200
+ /**
201
+ * Modal container. 520px as designed, but never wider or taller than the
202
+ * viewport it has to fit inside, and the BODY is what scrolls - a modal whose
203
+ * whole card scrolls takes its Cancel/Submit footer off screen with it.
204
+ */
205
+ .modal-card {
206
+ width: min(var(--modal-w), calc(100vw - 32px));
207
+ max-height: calc(100vh - 32px);
208
+ display: flex;
209
+ flex-direction: column;
210
+ border-radius: var(--r-modal);
211
+ background: var(--card);
212
+ box-shadow: var(--shadow-modal);
213
+ }
214
+
215
+ .modal-body {
216
+ flex: 1 1 auto;
217
+ min-height: 0;
218
+ overflow-y: auto;
219
+ }
220
+
221
+ @media (max-width: 599.98px) {
222
+ .modal-card {
223
+ width: calc(100vw - 32px);
224
+ max-height: calc(100dvh - 32px);
225
+ }
226
+ }
227
+
228
+ /* ------------------------------------------------------------------ */
229
+ /* 3. Touch targets. This one DOES reach the existing product. */
230
+ /* ------------------------------------------------------------------ */
231
+
232
+ /**
233
+ * A 36px icon button is comfortable with a mouse and a miss with a thumb.
234
+ * WCAG 2.2 asks for 44x44 on a touch pointer, and this is the one rule in
235
+ * Phase 1 that changes existing screens rather than waiting for a phase to
236
+ * adopt it - because it only applies on a COARSE pointer, so no desktop
237
+ * rendering moves by a pixel, and because leaving it to seven more phases
238
+ * means shipping the mobile-hostile version for all of them.
239
+ *
240
+ * `min-height`/`min-width` only, so nothing shrinks and no layout is forced.
241
+ */
242
+ @media (pointer: coarse) {
243
+ .v-btn--icon,
244
+ .v-btn--size-small,
245
+ .v-btn--size-x-small {
246
+ min-height: 44px;
247
+ min-width: 44px;
248
+ }
249
+
250
+ .v-list-item {
251
+ min-height: 44px;
252
+ }
253
+
254
+ .v-selection-control__wrapper {
255
+ min-height: 44px;
256
+ min-width: 44px;
257
+ }
258
+ }
@@ -0,0 +1,236 @@
1
+ /**
2
+ * PLATFORM REDESIGN - THE DESIGN TOKENS.
3
+ *
4
+ * Not one colour is written down in this file. Every colour token is an ALIAS
5
+ * onto the Vuetify theme variable that already holds it, and the values
6
+ * themselves live in `utils/theme.ts` (`PALETTE`), which `utils/theme.test.ts`
7
+ * measures. Two systems, one source of truth, flowing one way:
8
+ *
9
+ * PALETTE -> LIGHT_THEME/DARK_THEME -> --v-theme-* -> --bg, --card, --muted
10
+ *
11
+ * The aliases are declared on `[class*="v-theme--"]` rather than on `:root`
12
+ * ON PURPOSE. Vuetify puts that class on the application root AND on every
13
+ * `<v-theme-provider>` subtree, so `--card` read inside the `plain-dark`
14
+ * layout, or inside a dialog that forces a theme, resolves to THAT subtree's
15
+ * theme. Declaring the aliases at `:root` - or flipping a class on `<body>`,
16
+ * which is how the prototype does it - would have painted those subtrees with
17
+ * the page's theme instead.
18
+ *
19
+ * Values below that are NOT colours (opacity overlays, shadows, the type
20
+ * scale, the spacing scale) have nothing on the Vuetify side to alias, so they
21
+ * are stated here. The theme-dependent ones are stated twice, once per theme.
22
+ */
23
+
24
+ /* ------------------------------------------------------------------ */
25
+ /* Colour tokens - aliases only. */
26
+ /* ------------------------------------------------------------------ */
27
+
28
+ [class*="v-theme--"] {
29
+ /* Surfaces */
30
+ --bg: rgb(var(--v-theme-background));
31
+ --sidebar: rgb(var(--v-theme-sidebar));
32
+ --card: rgb(var(--v-theme-surface));
33
+ --border: rgb(var(--v-theme-border));
34
+
35
+ /* Text */
36
+ --text: rgb(var(--v-theme-text-primary));
37
+ --text2: rgb(var(--v-theme-text2));
38
+ --muted: rgb(var(--v-theme-muted));
39
+
40
+ /* Semantic status */
41
+ --ok: rgb(var(--v-theme-success));
42
+ /*
43
+ * The label on a SOLID `--ok` fill. Not `#fff`: the dark theme's `--ok` is
44
+ * `#41c795`, a light mint, and white on it measures 2.14:1 - worse than the
45
+ * 2.99:1 that got batch C's solid danger button reverted. `on-success` is
46
+ * the pair Vuetify already computes per theme from `PALETTE.onFill`
47
+ * (`#ffffff` light, `#131418` dark), so the fill keeps the colour the design
48
+ * asks for and only the label follows the theme.
49
+ */
50
+ --on-ok: rgb(var(--v-theme-on-success));
51
+ --warn: rgb(var(--v-theme-warning));
52
+ --err: rgb(var(--v-theme-error));
53
+ --info: rgb(var(--v-theme-info));
54
+
55
+ /*
56
+ * The brand accent, exactly as the design specifies it. `--accent-strong` is
57
+ * the same colour 18% darker and is the ONLY thing a white label may sit on:
58
+ * white on `--accent` measures 3.23:1 in both themes. Everything else - the
59
+ * links, the active nav item, the tab underline, the toggle's on state, the
60
+ * chart lines, `--accent-soft` - uses `--accent` unchanged.
61
+ */
62
+ --accent: rgb(var(--v-theme-accent));
63
+ --accent-strong: rgb(var(--v-theme-accent-strong));
64
+ --accent-text: rgb(var(--v-theme-accent-text));
65
+ --accent-soft: rgba(var(--v-theme-accent), 0.12);
66
+ --on-accent-strong: #ffffff;
67
+ }
68
+
69
+ /*
70
+ * Overlays and shadows. Not colours in the Vuetify sense - an overlay is a
71
+ * colour AND an alpha, and a shadow is a whole box-shadow - so these are the
72
+ * only values restated per theme. Equal specificity to the block above, so
73
+ * source order decides: light first, dark second.
74
+ */
75
+ [class*="v-theme--"] {
76
+ --hover: rgba(20, 22, 26, 0.05);
77
+ --thead: rgba(20, 22, 26, 0.02);
78
+ --shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
79
+ --shadow-modal: 0 24px 64px rgba(10, 12, 16, 0.3);
80
+ --shadow-menu: 0 12px 32px rgba(16, 24, 40, 0.14);
81
+ --scrim: rgba(12, 14, 18, 0.5);
82
+
83
+ /* Soft status backgrounds. Stated by the design literally rather than
84
+ derived from the foreground, so darkening a label for AA leaves its chip
85
+ the colour it was drawn. */
86
+ --ok-bg: rgba(15, 138, 98, 0.1);
87
+ --warn-bg: rgba(190, 140, 25, 0.13);
88
+ --err-bg: rgba(207, 75, 75, 0.1);
89
+ --info-bg: rgba(59, 111, 212, 0.1);
90
+
91
+ /* The edge of a TONAL error control - the check-out button. Alpha over the
92
+ live `--err` rather than a literal, exactly the way `--accent-soft` is
93
+ built, so it tracks the theme's error colour instead of restating it. */
94
+ --err-border: rgba(var(--v-theme-error), 0.24);
95
+ }
96
+
97
+ .v-theme--dark {
98
+ --hover: rgba(255, 255, 255, 0.06);
99
+ --thead: rgba(255, 255, 255, 0.02);
100
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
101
+
102
+ --ok-bg: rgba(65, 199, 149, 0.12);
103
+ --warn-bg: rgba(220, 175, 78, 0.13);
104
+ --err-bg: rgba(229, 115, 115, 0.12);
105
+ --info-bg: rgba(125, 166, 236, 0.12);
106
+
107
+ --err-border: rgba(var(--v-theme-error), 0.28);
108
+ }
109
+
110
+ /* ------------------------------------------------------------------ */
111
+ /* Type, shape and spacing - the same in both themes. */
112
+ /* ------------------------------------------------------------------ */
113
+
114
+ :root {
115
+ /*
116
+ * Manrope is loaded in `nuxt.config.ts` and this token names it, but NOTHING
117
+ * in Phase 1 sets `font-family` on the page. Switching eleven applications to
118
+ * a new typeface before a single screen has been rebuilt would leave the
119
+ * product looking half-migrated; each phase adopts the token as it rebuilds
120
+ * its surfaces.
121
+ */
122
+ --font-sans: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
123
+ "Helvetica Neue", Arial, sans-serif;
124
+
125
+ /* Type scale - size / weight / letter-spacing, straight from the contract. */
126
+ --fs-h1: 22px;
127
+ --fw-h1: 800;
128
+ --ls-h1: -0.01em;
129
+
130
+ --fs-card-title: 15.5px;
131
+ --fw-card-title: 800;
132
+
133
+ --fs-kpi: 30px;
134
+ --fw-kpi: 800;
135
+ --ls-kpi: -0.02em;
136
+
137
+ --fs-table-head: 11px;
138
+ --fw-table-head: 800;
139
+ --ls-table-head: 0.07em;
140
+
141
+ --fs-cell: 13.5px;
142
+ --fw-cell: 600;
143
+ --fs-cell-dense: 13px;
144
+
145
+ --fs-nav: 13px;
146
+
147
+ --fs-section-label: 10.5px;
148
+ --fw-section-label: 800;
149
+ --ls-section-label: 0.09em;
150
+
151
+ --fs-btn: 13.5px;
152
+ --fw-btn: 700;
153
+ --fw-btn-strong: 800;
154
+
155
+ --fs-chip: 11.5px;
156
+ --fs-chip-lg: 12px;
157
+ --fw-chip: 700;
158
+
159
+ --fs-breadcrumb: 12.5px;
160
+
161
+ /* Shape */
162
+ --r-card: 14px;
163
+ --r-inner: 10px;
164
+ --r-inner-sm: 9px;
165
+ --r-inner-lg: 12px;
166
+ --r-tag: 7px;
167
+ --r-pill: 999px;
168
+ --r-modal: 16px;
169
+ --r-menu: 14px;
170
+
171
+ /* Shell metrics */
172
+ --sidebar-w: 252px;
173
+ --topbar-h: 58px;
174
+ --content-pad-y: 22px;
175
+ --content-pad-x: 26px;
176
+ --content-pad-bottom: 40px;
177
+
178
+ /* Controls */
179
+ --input-h: 40px;
180
+ --input-h-compact: 34px;
181
+ --btn-h: 40px;
182
+ --icon-btn: 36px;
183
+ --avatar: 36px;
184
+ --toggle-w: 38px;
185
+ --toggle-h: 22px;
186
+ --grid-gap: 12px;
187
+ --modal-w: 520px;
188
+ --menu-w: 240px;
189
+
190
+ /*
191
+ * Table density - the design's own knob. `--cellpad` is the comfortable row
192
+ * padding; put `data-density="compact"` on a table card for the dense one.
193
+ * The accent is the other knob the handoff asks for, and it is a value in
194
+ * `PALETTE` (utils/theme.ts) rather than a runtime switch, because nothing
195
+ * changes it at runtime yet.
196
+ */
197
+ --cellpad: 15px;
198
+ --cellpad-x: 20px;
199
+
200
+ --motion: 150ms;
201
+ }
202
+
203
+ [data-density="compact"] {
204
+ --cellpad: 9px;
205
+ --fs-cell: var(--fs-cell-dense);
206
+ }
207
+
208
+ /* ------------------------------------------------------------------ */
209
+ /* Semantic status tones. */
210
+ /* ------------------------------------------------------------------ */
211
+
212
+ /*
213
+ * The mapping from a status word to a tone lives in `utils/status.ts`, so that
214
+ * one list serves the chips, the dots and anything else. These classes are
215
+ * what that tone paints with. A chip is `.tone-*` plus its own shape.
216
+ */
217
+ .tone-ok {
218
+ color: var(--ok);
219
+ --tone-bg: var(--ok-bg);
220
+ }
221
+ .tone-warn {
222
+ color: var(--warn);
223
+ --tone-bg: var(--warn-bg);
224
+ }
225
+ .tone-err {
226
+ color: var(--err);
227
+ --tone-bg: var(--err-bg);
228
+ }
229
+ .tone-info {
230
+ color: var(--info);
231
+ --tone-bg: var(--info-bg);
232
+ }
233
+ .tone-neutral {
234
+ color: var(--text2);
235
+ --tone-bg: var(--hover);
236
+ }
@@ -2,34 +2,33 @@
2
2
  <v-dialog
3
3
  :model-value="modelValue"
4
4
  @update:model-value="val => $emit('update:modelValue', val)"
5
- max-width="420"
5
+ :max-width="520"
6
6
  persistent
7
7
  >
8
- <v-card class="pa-4" style="border-radius:12px;">
9
- <v-card-title class="headline" style="font-size:1.3rem; font-weight:500;">
10
- {{ title }}
11
- </v-card-title>
12
- <v-card-text style="font-size:1.05rem; margin-bottom:24px;">
8
+ <v-card class="screen-modal">
9
+ <v-card-title>{{ title }}</v-card-title>
10
+ <v-card-text class="screen-modal__body">
13
11
  {{ message }}
14
12
  </v-card-text>
15
- <v-card-actions class="justify-end" style="gap:12px;">
13
+ <!-- The design's footer is ghost-then-primary, equal width. "No" is the
14
+ dismissing action and "Yes" the confirming one, so they take the
15
+ Cancel / Submit shapes in that order. -->
16
+ <div class="screen-modal__footer">
16
17
  <v-btn
17
- color="primary"
18
+ class="screen-btn-ghost"
18
19
  variant="outlined"
19
- style="min-width:70px; background:#e3f2fd; color:#1976d2; font-weight:500;"
20
- @click="$emit('confirm')"
20
+ @click="$emit('cancel')"
21
21
  >
22
- Yes
22
+ No
23
23
  </v-btn>
24
24
  <v-btn
25
- color="error"
26
- variant="outlined"
27
- style="min-width:70px; background:#ffebee; color:#d32f2f; font-weight:500;"
28
- @click="$emit('cancel')"
25
+ class="screen-btn-primary"
26
+ variant="flat"
27
+ @click="$emit('confirm')"
29
28
  >
30
- No
29
+ Yes
31
30
  </v-btn>
32
- </v-card-actions>
31
+ </div>
33
32
  </v-card>
34
33
  </v-dialog>
35
34
  </template>
@@ -41,4 +40,4 @@ defineProps<{
41
40
  message?: string
42
41
  }>();
43
42
  defineEmits(['confirm', 'cancel', 'update:modelValue']);
44
- </script>
43
+ </script>
@@ -1,24 +1,32 @@
1
1
  <template>
2
2
  <v-row no-gutters align="center" style="gap: 8px">
3
3
  <span class="text-caption text-medium-emphasis mr-1">Available:</span>
4
- <v-chip
5
- size="small"
6
- variant="tonal"
7
- color="primary"
8
- prepend-icon="mdi-card-account-details"
9
- :loading="loading"
10
- >
4
+ <!--
5
+ These were `v-chip variant="tonal"` on Vuetify's `primary` / `secondary`.
6
+ `secondary` is never defined in this layer's theme, so the QR chip was
7
+ painting itself Vuetify's BUILT-IN default `#48a9a6` - a colour the design
8
+ never chose, and one that measures 2.58:1 on the page background.
9
+
10
+ `StatusChip`'s `tone` prop is the documented route for a chip that is a
11
+ COUNT rather than a status word: the screen has already worked out the
12
+ meaning and only needs the design's tone.
13
+
14
+ Both take `neutral`, and that is the design system's own rule rather than
15
+ a preference - `utils/status.ts`: a value the design never named must NOT
16
+ be guessed into a colour. The handoff draws no "available cards" stat, so
17
+ neither count has a colour of its own to honour. They are told apart by
18
+ their icon and label. `info` was measured here too and reads 3.88:1 on
19
+ the page background, which would have ADDED a sub-AA pair to buy a
20
+ distinction the design never asked for.
21
+ -->
22
+ <StatusChip tone="neutral" pill :dot="false">
23
+ <v-icon icon="mdi-card-account-details" size="14" />
11
24
  NFC: <strong class="ml-1">{{ availablePhysical }}</strong>
12
- </v-chip>
13
- <v-chip
14
- size="small"
15
- variant="tonal"
16
- color="secondary"
17
- prepend-icon="mdi-qrcode"
18
- :loading="loading"
19
- >
25
+ </StatusChip>
26
+ <StatusChip tone="neutral" pill :dot="false">
27
+ <v-icon icon="mdi-qrcode" size="14" />
20
28
  QR Code: <strong class="ml-1">{{ availableNonPhysical }}</strong>
21
- </v-chip>
29
+ </StatusChip>
22
30
  </v-row>
23
31
  </template>
24
32