@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.
- package/assets/css/primitives.css +1113 -0
- package/assets/css/screens.css +1097 -0
- package/assets/css/shell.css +258 -0
- package/assets/css/tokens.css +236 -0
- package/components/AcceptDialog.vue +17 -18
- package/components/AccessCard/AvailableStats.vue +24 -16
- package/components/AccessCardAddForm.vue +42 -42
- package/components/AccessCardAssignToUnitForm.vue +13 -21
- package/components/AccessCardDeleteDialog.vue +41 -39
- package/components/AccessCardDetailsDialog.vue +125 -79
- package/components/AccessCardPreviewDialog.vue +58 -84
- package/components/AccessCardQrTagging.vue +60 -63
- package/components/AccessCardReplaceForm.vue +27 -23
- package/components/AccessManagement.vue +86 -85
- package/components/AddEqupmentForm.vue +18 -22
- package/components/AddPassKeyToVisitor.vue +95 -73
- package/components/AppButton.vue +62 -0
- package/components/AppCard.vue +20 -0
- package/components/AppDateField.vue +49 -0
- package/components/AppField.vue +76 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +247 -0
- package/components/AreaChecklistHistoryLogs.vue +51 -44
- package/components/AreaChecklistHistoryMain.vue +14 -7
- package/components/AreaFormDialog.vue +71 -59
- package/components/AreaMain.vue +73 -107
- package/components/AttachmentsViewer.vue +84 -22
- package/components/AttendanceCheckInOutDialog.vue +85 -88
- package/components/AttendanceDetailsDialog.vue +57 -63
- package/components/AttendanceMain.vue +4 -3
- package/components/AttendanceMapSearchDialog.vue +42 -44
- package/components/AttendanceSettingsDialog.vue +79 -63
- package/components/Avatar/Main.vue +54 -2
- package/components/BillingMain.vue +26 -30
- package/components/BtnUploadFile.vue +53 -11
- package/components/BuildingForm.vue +34 -27
- package/components/BuildingManagement/buildings.vue +39 -39
- package/components/BuildingManagement/units.vue +26 -42
- package/components/BuildingUnitFormAdd.vue +20 -23
- package/components/BuildingUnitFormEdit.vue +8 -4
- package/components/BulletinBoardForm.vue +19 -24
- package/components/BulletinBoardManagement.vue +49 -57
- package/components/BulletinBoardView.vue +9 -6
- package/components/BulletinExpirationChip.vue +13 -4
- package/components/Button/Close.vue +2 -2
- package/components/CameraForm.vue +21 -22
- package/components/CameraMain.vue +50 -49
- package/components/Card/DeleteConfirmation.vue +24 -22
- package/components/Card/MemberInfoSummary.vue +28 -3
- package/components/Card/Toggle.vue +15 -3
- package/components/CardToolbar.vue +37 -0
- package/components/Carousel.vue +56 -36
- package/components/Chat/Bubbles.vue +43 -14
- package/components/Chat/Information.vue +49 -32
- package/components/Chat/ListCard.vue +38 -9
- package/components/Chat/Message.vue +41 -15
- package/components/Chat/Navigation.vue +27 -15
- package/components/Chat/SkeletonLoader.vue +9 -2
- package/components/ChecklistItemRow.vue +25 -1
- package/components/CleaningScheduleMain.vue +44 -49
- package/components/ClientDetailForm.vue +81 -56
- package/components/ClientMain.vue +92 -82
- package/components/ConfirmDialog.vue +14 -8
- package/components/Container/Standard.vue +9 -7
- package/components/CreateSubsciptionPlan.vue +148 -89
- package/components/DashboardEmptyState.vue +48 -3
- package/components/DashboardMain.vue +924 -676
- package/components/DashboardPanel.vue +84 -21
- package/components/DeliveryCompany.vue +34 -20
- package/components/Dialog/DeleteConfirmation.vue +20 -21
- package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
- package/components/Dialog/ReusablePrompt.vue +30 -29
- package/components/Dialog/UpdateMoreAction.vue +62 -59
- package/components/DocumentForm.vue +14 -21
- package/components/DocumentManagement.vue +162 -108
- package/components/DocumentViewer.vue +29 -12
- package/components/DrawImage.vue +29 -21
- package/components/EntryPass/QrTemplatePreview.vue +27 -16
- package/components/EntryPassInformation.vue +40 -10
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +84 -97
- package/components/EquipmentManagementMain.vue +81 -103
- package/components/Facility/BookingSetup.vue +43 -52
- package/components/Feedback/Form.vue +26 -12
- package/components/FeedbackMain.vue +35 -49
- package/components/FileInput.vue +75 -58
- package/components/FileInputWithList.vue +59 -12
- package/components/FormDialog.vue +60 -21
- package/components/HidAccessLogDashboard.vue +512 -490
- package/components/HidCameraCaptureDialog.vue +216 -0
- package/components/HidEnabledGate.vue +67 -9
- package/components/HidIntercomManagement.vue +1322 -653
- package/components/HidQrCodeConfiguration.vue +1030 -258
- package/components/HidReaderForm.vue +142 -59
- package/components/HidReaderManagement.vue +211 -323
- package/components/HidServiceSettingsPanel.vue +44 -13
- package/components/HidUserEnrollment.vue +1185 -412
- package/components/HygieneUpdateMoreAction.vue +44 -44
- package/components/IncidentReport/Authorities.vue +30 -18
- package/components/IncidentReport/IncidentInformation.vue +10 -10
- package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
- package/components/IncidentReport/affectedEntities.vue +41 -20
- package/components/Input/Date.vue +3 -0
- package/components/Input/DatePicker.vue +1 -2
- package/components/Input/DateTimePicker.vue +1 -1
- package/components/Input/File.vue +55 -10
- package/components/Input/FileV2.vue +39 -6
- package/components/Input/InputPhoneNumberV2.vue +2 -1
- package/components/Input/ListGroupSelection.vue +56 -8
- package/components/Input/NRICNumber.vue +1 -0
- package/components/Input/NewDate.vue +1 -0
- package/components/Input/Number.vue +1 -0
- package/components/Input/Password.vue +1 -0
- package/components/Input/PhoneNumber.vue +21 -1
- package/components/Input/VehicleNumber.vue +1 -0
- package/components/InputLabel.vue +2 -2
- package/components/InvitationClientForm.vue +21 -19
- package/components/InvitationForm.vue +15 -21
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +263 -36
- package/components/Layout/NavigationDrawer.vue +300 -30
- package/components/ListItem.vue +33 -3
- package/components/LocalPagination.vue +27 -3
- package/components/ManageChecklistMain.vue +136 -86
- package/components/MemberInformation.vue +32 -16
- package/components/MemberMain.vue +113 -93
- package/components/MyAttendanceMain.vue +3 -5
- package/components/NavigationItem.vue +83 -3
- package/components/Nfc/NFCPatrolReportMain.vue +47 -33
- package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
- package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
- package/components/Nfc/NFCPatrolSettings.vue +14 -4
- package/components/Nfc/NFCTagForm.vue +22 -8
- package/components/Nfc/NFCTagMain.vue +41 -43
- package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
- package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
- package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
- package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
- package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
- package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
- package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
- package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
- package/components/NotificationSettings.vue +373 -0
- package/components/NumberSettingField.vue +9 -2
- package/components/OnlineFormConfigurationForm.vue +725 -237
- package/components/OnlineFormFill.vue +213 -106
- package/components/OnlineFormRenderPreview.vue +25 -30
- package/components/OnlineFormsConfiguration.vue +113 -109
- package/components/OrgViewDialog.vue +34 -16
- package/components/OvernightParkingAvailability.vue +49 -26
- package/components/OvernightParkingManagement.vue +78 -18
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +109 -53
- package/components/PasswordConfirmation.vue +44 -26
- package/components/PeopleForm.vue +41 -20
- package/components/PhotoUpload.vue +110 -48
- package/components/PlaceholderComponent.vue +35 -19
- package/components/PlanActionsMenu.vue +12 -2
- package/components/PlanStatusBadge.vue +7 -8
- package/components/PlateNumberDisplay.vue +9 -6
- package/components/QrTemplate/PrintDialog.vue +66 -43
- package/components/RolePermissionFormCreate.vue +104 -79
- package/components/RolePermissionFormPreviewUpdate.vue +111 -109
- package/components/RolePermissionMain.vue +183 -81
- package/components/ScanVisitorQRCode.vue +99 -35
- package/components/ScheduleAreaMain.vue +34 -59
- package/components/ScheduleTaskForm.vue +61 -42
- package/components/ScheduleTaskMain.vue +56 -69
- package/components/SearchVehicleNumberUser.vue +39 -37
- package/components/ServiceProviderFormCreate.vue +29 -20
- package/components/ServiceProviderInvitationPrompt.vue +155 -0
- package/components/ServiceProviderMain.vue +601 -432
- package/components/Signature.vue +40 -34
- package/components/SignaturePad.vue +5 -29
- package/components/SiteSettings.vue +41 -11
- package/components/SiteSettingsWorkOrder.vue +61 -30
- package/components/SlideCardGroup.vue +35 -11
- package/components/Snackbar.vue +36 -3
- package/components/SpecificAttr.vue +18 -8
- package/components/StatusChip.vue +156 -0
- package/components/StockCard.vue +17 -12
- package/components/SubscriptionPlanTable.vue +207 -140
- package/components/SwitchContext.vue +201 -9
- package/components/TableHygiene.vue +36 -46
- package/components/TableList.vue +10 -10
- package/components/TableListSecondary.vue +105 -54
- package/components/TableMain.vue +251 -78
- package/components/TableV3.vue +61 -70
- package/components/TiptapEditor.vue +33 -40
- package/components/TiptapEditorQr.vue +5 -2
- package/components/Tooltip/Info.vue +69 -24
- package/components/UnitMain.vue +40 -60
- package/components/UnitPersonCard.vue +42 -13
- package/components/VehicleAddSelection.vue +48 -32
- package/components/VehicleForm.vue +112 -129
- package/components/VehicleManagement.vue +26 -12
- package/components/VehicleUpdateMoreAction.vue +59 -46
- package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
- package/components/VisitorForm.vue +774 -210
- package/components/VisitorFormSelection.vue +40 -37
- package/components/VisitorManagement.vue +239 -82
- package/components/VisitorPassKeyQRScanner.vue +65 -18
- package/components/VisitorSearchResultSummary.vue +60 -16
- package/components/VisitorSocketPopUp.vue +22 -14
- package/components/VisitorsReportPreview.vue +44 -29
- package/components/WorkOrder/Create.vue +26 -17
- package/components/WorkOrder/ListView.vue +38 -7
- package/components/WorkOrder/Main.vue +62 -59
- package/composables/useFeedback.ts +3 -3
- package/composables/useHidAmico.ts +269 -0
- package/composables/useHidNavigation.ts +2 -9
- package/composables/useMember.ts +3 -3
- package/composables/useNFCPatrolDailyReport.ts +13 -0
- package/composables/useNFCPatrolMonthlyReport.ts +22 -56
- package/composables/useNFCPatrolReport.ts +13 -0
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOnlineFormPages.ts +2 -0
- package/composables/useRole.ts +22 -0
- package/composables/useServiceProvider.ts +3 -3
- package/composables/useServiceProviderInvitation.ts +145 -0
- package/composables/useSipWebPhone.ts +336 -0
- package/composables/useSubscriptionPlan.ts +62 -0
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/composables/useWebUsb.ts +127 -37
- package/composables/useWorkOrder.ts +17 -3
- package/error.vue +30 -6
- package/nuxt.config.ts +36 -0
- package/package.json +25 -4
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
- package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/pages/notification-settings.vue +19 -0
- package/pages/payment-method-linked.vue +34 -10
- package/pages/require-customer.vue +48 -10
- package/pages/require-organization-membership.vue +32 -8
- package/pages/unauthorized.vue +29 -4
- package/plugins/secure-member.client.ts +21 -56
- package/plugins/vuetify.ts +69 -2
- package/types/nfc-patrol-report.ts +3 -2
- package/types/site.d.ts +71 -0
- package/types/subscription.ts +18 -11
- package/utils/breadcrumb.test.ts +108 -0
- package/utils/breadcrumb.ts +82 -0
- package/utils/dashboard.test.ts +118 -0
- package/utils/dashboard.ts +53 -0
- package/utils/data.test.ts +95 -0
- package/utils/data.ts +40 -2
- package/utils/hid-discover.test.ts +102 -0
- package/utils/list-page.test.ts +60 -0
- package/utils/list-page.ts +38 -0
- package/utils/materialSymbols.ts +363 -0
- package/utils/nav-sections.test.ts +190 -0
- package/utils/nav-sections.ts +151 -0
- package/utils/nfc-patrol-report.test.ts +130 -0
- package/utils/nfc-patrol-report.ts +101 -0
- package/utils/role.test.ts +69 -0
- package/utils/role.ts +10 -0
- package/utils/route-name.test.ts +85 -0
- package/utils/route-name.ts +24 -0
- package/utils/status.test.ts +110 -0
- package/utils/status.ts +135 -0
- package/utils/table-card.test.ts +84 -0
- package/utils/theme-aa-ledger.ts +148 -0
- package/utils/theme.test.ts +485 -7
- package/utils/theme.ts +295 -110
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.editorconfig +0 -12
- package/.github/workflows/main.yml +0 -17
- package/.github/workflows/publish-staging.yml +0 -47
- package/.github/workflows/publish.yml +0 -39
- package/PUBLISHING.md +0 -269
- package/components/HidIdentityMapping.vue +0 -975
- package/components/ScheduleTaskAreaFormDialog.vue +0 -144
- package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
- package/components/ScheduleTastTicketMain.vue +0 -182
- package/components/TableWithButton.vue +0 -94
- package/test/visitor-socket.test.mjs +0 -36
|
@@ -1,87 +1,77 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
</v-btn>
|
|
16
|
-
<v-btn
|
|
17
|
-
class="text-none"
|
|
18
|
-
rounded="pill"
|
|
19
|
-
variant="tonal"
|
|
20
|
-
size="large"
|
|
21
|
-
@click="assignDialog = true"
|
|
3
|
+
<!--
|
|
4
|
+
The design's title row. Was two `tonal` pills and a floating-label search
|
|
5
|
+
floating above the card with no page name at all; the search now sits on
|
|
6
|
+
the card's own toolbar row, where the design puts it. Same models, same
|
|
7
|
+
handlers, same permission gates.
|
|
8
|
+
-->
|
|
9
|
+
<v-col cols="12">
|
|
10
|
+
<!-- Nav calls this screen "Cards" (under Access Mgmt) - the title matches
|
|
11
|
+
the navigation, it is not new copy. -->
|
|
12
|
+
<PageHeader title="Cards">
|
|
13
|
+
<template #actions>
|
|
14
|
+
<AppButton
|
|
22
15
|
v-if="canUpdateAccessCard && canAssignAccessCard"
|
|
16
|
+
variant="ghost"
|
|
17
|
+
@click="assignDialog = true"
|
|
23
18
|
>
|
|
24
19
|
Assign to Unit
|
|
25
|
-
</
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class="ml-2"
|
|
36
|
-
style="max-width: 250px"
|
|
37
|
-
/>
|
|
38
|
-
</v-row>
|
|
20
|
+
</AppButton>
|
|
21
|
+
<AppButton
|
|
22
|
+
v-if="canCreate && canCreateAccessCard"
|
|
23
|
+
icon="mdi-plus"
|
|
24
|
+
@click="setCard()"
|
|
25
|
+
>
|
|
26
|
+
Add Access Card
|
|
27
|
+
</AppButton>
|
|
28
|
+
</template>
|
|
29
|
+
</PageHeader>
|
|
39
30
|
</v-col>
|
|
40
31
|
<!-- Available Card Stats -->
|
|
41
32
|
<v-col cols="12" class="mb-2" v-if="canViewAllCards">
|
|
42
33
|
<AccessCardAvailableStats ref="statsRef" :site-id="siteId" />
|
|
43
34
|
</v-col>
|
|
44
35
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
</v-toolbar>
|
|
73
|
-
<v-data-table
|
|
74
|
-
:headers="headers"
|
|
75
|
-
:items="items"
|
|
76
|
-
item-value="_id"
|
|
77
|
-
items-per-page="10"
|
|
78
|
-
fixed-header
|
|
79
|
-
hide-default-footer
|
|
80
|
-
@click:row="tableRowClickHandler"
|
|
81
|
-
style="max-height: calc(100vh - (200px))"
|
|
36
|
+
<!--
|
|
37
|
+
Was `v-card` + a `grey-lighten-4` `v-toolbar` (a white slab that stayed
|
|
38
|
+
white in dark mode) + a bare `v-data-table`. `TableMain` is the same three
|
|
39
|
+
things drawn as the design: same headers, items, page size, refresh,
|
|
40
|
+
page range, pager and row click.
|
|
41
|
+
-->
|
|
42
|
+
<TableMain
|
|
43
|
+
v-if="canViewAllCards"
|
|
44
|
+
:headers="headers"
|
|
45
|
+
:items="items"
|
|
46
|
+
:loading="loading"
|
|
47
|
+
:page="page"
|
|
48
|
+
:pages="pages"
|
|
49
|
+
:pageRange="pageRange"
|
|
50
|
+
:items-per-page="10"
|
|
51
|
+
show-header
|
|
52
|
+
@refresh="getCards"
|
|
53
|
+
@update:page="page = $event; getCards()"
|
|
54
|
+
@row-click="(data: any) => tableRowClickHandler(null, data)"
|
|
55
|
+
>
|
|
56
|
+
<template #prepend-additional>
|
|
57
|
+
<AppField
|
|
58
|
+
v-model="searchText"
|
|
59
|
+
search
|
|
60
|
+
compact
|
|
61
|
+
placeholder="Search Card, Unit..."
|
|
62
|
+
klass="app-toolbar__search"
|
|
82
63
|
/>
|
|
83
|
-
</
|
|
84
|
-
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<!--
|
|
67
|
+
A failed request is not a site with no access cards. Without this the
|
|
68
|
+
table drew its neutral "No data available" for a 500, which reads as
|
|
69
|
+
"there are no cards here" - a statement the server never made.
|
|
70
|
+
-->
|
|
71
|
+
<template v-if="getCardError" #no-data>
|
|
72
|
+
<DashboardEmptyState error />
|
|
73
|
+
</template>
|
|
74
|
+
</TableMain>
|
|
85
75
|
|
|
86
76
|
<!-- Create Dialog -->
|
|
87
77
|
<v-dialog v-model="createDialog" width="650" persistent>
|
|
@@ -144,6 +134,10 @@
|
|
|
144
134
|
</v-row>
|
|
145
135
|
</template>
|
|
146
136
|
<script setup lang="ts">
|
|
137
|
+
// Relative import, never Nuxt auto-import: this layer's `utils/` are auto-imported
|
|
138
|
+
// into the consuming apps but not reliably into the layer's own components.
|
|
139
|
+
import { readListPage } from "../utils/list-page";
|
|
140
|
+
|
|
147
141
|
definePageMeta({
|
|
148
142
|
middleware: ["01-auth", "02-org"],
|
|
149
143
|
memberOnly: true,
|
|
@@ -281,6 +275,7 @@ const {
|
|
|
281
275
|
data: getCardReq,
|
|
282
276
|
refresh: getCards,
|
|
283
277
|
status: getAllReqStatus,
|
|
278
|
+
error: getCardError,
|
|
284
279
|
} = useLazyAsyncData(
|
|
285
280
|
"get-all-cards",
|
|
286
281
|
() =>
|
|
@@ -307,21 +302,27 @@ watch(searchText, (val) => {
|
|
|
307
302
|
|
|
308
303
|
const loading = computed(() => getAllReqStatus.value === "pending");
|
|
309
304
|
|
|
305
|
+
// This endpoint answers with a NESTED envelope - `{ data: { items, pages,
|
|
306
|
+
// pageRange } }` - so the page totals are read off `reply.data`, through the
|
|
307
|
+
// one helper that knows how a paginated reply is shaped. The totals are the
|
|
308
|
+
// server's count of the WHOLE list; they are never re-derived from the rows on
|
|
309
|
+
// this page. Applying it unconditionally matters: when a later request fails,
|
|
310
|
+
// the reply goes null and the header falls back to the placeholder instead of
|
|
311
|
+
// keeping the previous page's total on screen as if it were still true.
|
|
310
312
|
watchEffect(() => {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
},
|
|
313
|
+
const listPage = readListPage<Record<string, any>>(getCardReq.value?.data);
|
|
314
|
+
items.value = listPage.items.map((item: any) => ({
|
|
315
|
+
...item,
|
|
316
|
+
cardCounts: {
|
|
317
|
+
...item.cardCounts,
|
|
318
|
+
available: {
|
|
319
|
+
...item.cardCounts?.available,
|
|
320
|
+
physical: (item.available?.physical ?? []).filter((c: any) => !c.damage).length,
|
|
320
321
|
},
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
322
|
+
},
|
|
323
|
+
}));
|
|
324
|
+
pages.value = listPage.pages;
|
|
325
|
+
pageRange.value = listPage.pageRange;
|
|
325
326
|
});
|
|
326
327
|
|
|
327
328
|
function setCard({ mode = "create", dialog = true, data = {} as TCard } = {}) {
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-dialog v-model="showDialog" max-width="450">
|
|
3
|
-
<v-card>
|
|
4
|
-
<v-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
{{ prop.mode === "edit" ? "Edit Equipment" : "Add New Equipment" }}
|
|
8
|
-
</span>
|
|
9
|
-
</v-row>
|
|
10
|
-
</v-toolbar>
|
|
3
|
+
<v-card class="screen-modal">
|
|
4
|
+
<v-card-title class="text-capitalize">
|
|
5
|
+
{{ prop.mode === "edit" ? "Edit Equipment" : "Add New Equipment" }}
|
|
6
|
+
</v-card-title>
|
|
11
7
|
|
|
12
|
-
<v-card-text class="pa-4">
|
|
8
|
+
<v-card-text class="screen-modal__body pa-4">
|
|
13
9
|
<v-form ref="formRef" v-model="valid">
|
|
14
10
|
<v-row no-gutters class="pa-0">
|
|
15
11
|
<v-col cols="12" class="pa-0 mb-2">
|
|
@@ -39,7 +35,7 @@
|
|
|
39
35
|
variant="outlined"
|
|
40
36
|
placeholder="Enter equipment name"
|
|
41
37
|
hide-details="auto"
|
|
42
|
-
class="mb-0"
|
|
38
|
+
class="mb-0 filter-field"
|
|
43
39
|
/>
|
|
44
40
|
</v-col>
|
|
45
41
|
|
|
@@ -58,35 +54,35 @@
|
|
|
58
54
|
variant="outlined"
|
|
59
55
|
placeholder="Select unitOfMeasurement"
|
|
60
56
|
hide-details="auto"
|
|
61
|
-
class="mb-0"
|
|
57
|
+
class="mb-0 filter-field"
|
|
62
58
|
/>
|
|
63
59
|
</v-col>
|
|
64
60
|
</v-row>
|
|
65
61
|
</v-form>
|
|
66
62
|
</v-card-text>
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
<!-- Was a `v-toolbar` footer: a grey slab holding a bare-text Cancel and
|
|
65
|
+
a `rounded-0`, `color="black"` primary - a square black button that
|
|
66
|
+
ignored the theme. Same two buttons on the card's own surface, with
|
|
67
|
+
the design's radius and accent fill. -->
|
|
68
|
+
<v-card-actions class="screen-modal__footer">
|
|
69
69
|
<v-row no-gutters>
|
|
70
|
-
<v-col cols="6" class="
|
|
70
|
+
<v-col cols="6" class="pr-2">
|
|
71
71
|
<v-btn
|
|
72
72
|
block
|
|
73
|
-
variant="
|
|
74
|
-
class="text-none"
|
|
75
|
-
size="large"
|
|
73
|
+
variant="outlined"
|
|
74
|
+
class="text-none screen-btn-ghost"
|
|
76
75
|
@click="close"
|
|
77
|
-
height="48"
|
|
78
76
|
>
|
|
79
77
|
Cancel
|
|
80
78
|
</v-btn>
|
|
81
79
|
</v-col>
|
|
82
80
|
|
|
83
|
-
<v-col cols="6" class="
|
|
81
|
+
<v-col cols="6" class="pl-2">
|
|
84
82
|
<v-btn
|
|
85
83
|
block
|
|
86
84
|
variant="flat"
|
|
87
|
-
|
|
88
|
-
class="text-none font-weight-bold rounded-0"
|
|
89
|
-
height="48"
|
|
85
|
+
class="text-none screen-btn-primary"
|
|
90
86
|
:loading="submitting"
|
|
91
87
|
@click="submit"
|
|
92
88
|
>
|
|
@@ -94,7 +90,7 @@
|
|
|
94
90
|
</v-btn>
|
|
95
91
|
</v-col>
|
|
96
92
|
</v-row>
|
|
97
|
-
</v-
|
|
93
|
+
</v-card-actions>
|
|
98
94
|
</v-card>
|
|
99
95
|
</v-dialog>
|
|
100
96
|
</template>
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card width="100%" :loading="processing">
|
|
3
|
-
<
|
|
4
|
-
<v-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<v-row no-gutters class="ga-1">
|
|
2
|
+
<v-card width="100%" class="screen-modal passkey" :loading="processing">
|
|
3
|
+
<div class="passkey__head">
|
|
4
|
+
<v-card-title>{{ isEditMode ? "Edit" : "Assign" }} Pass & Keys</v-card-title>
|
|
5
|
+
<AppButton
|
|
6
|
+
variant="icon"
|
|
7
|
+
icon="mdi-close"
|
|
8
|
+
aria-label="Close"
|
|
9
|
+
@click="emit('close')"
|
|
10
|
+
/>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="screen-modal__body passkey__body">
|
|
14
|
+
<!-- `.filter-field` is the design's input, and it is a descendant
|
|
15
|
+
selector, so both autocompletes get it from this one class. -->
|
|
16
|
+
<v-row no-gutters class="ga-1 filter-field">
|
|
18
17
|
<v-col cols="12">
|
|
19
|
-
<span class="
|
|
20
|
-
Name: {{ prop.visitor.name }}
|
|
21
|
-
</span>
|
|
18
|
+
<span class="passkey__name">Name: {{ prop.visitor.name }}</span>
|
|
22
19
|
</v-col>
|
|
23
20
|
|
|
24
21
|
<!-- Pass section -->
|
|
@@ -27,15 +24,11 @@
|
|
|
27
24
|
|
|
28
25
|
<!-- Existing pass -->
|
|
29
26
|
<div v-if="existingPass" class="d-flex align-center ga-2 mb-2">
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
color="blue"
|
|
33
|
-
variant="tonal"
|
|
34
|
-
size="small"
|
|
35
|
-
>
|
|
27
|
+
<StatusChip tone="info" :dot="false">
|
|
28
|
+
<v-icon icon="mdi-card-bulleted-outline" size="13" />
|
|
36
29
|
{{ existingPass.prefixAndName }}
|
|
37
|
-
</
|
|
38
|
-
<span class="
|
|
30
|
+
</StatusChip>
|
|
31
|
+
<span class="passkey__current">(current)</span>
|
|
39
32
|
</div>
|
|
40
33
|
|
|
41
34
|
<!-- New pass selector -->
|
|
@@ -79,21 +72,26 @@
|
|
|
79
72
|
v-if="existingKeys.length > 0"
|
|
80
73
|
class="d-flex flex-wrap ga-1 mb-2"
|
|
81
74
|
>
|
|
82
|
-
|
|
75
|
+
<!-- The design's tag chip, keeping the remove control the product
|
|
76
|
+
already had: the tone and the shape are the shared ones, only
|
|
77
|
+
the close button is drawn inside rather than by Vuetify. -->
|
|
78
|
+
<StatusChip
|
|
83
79
|
v-for="key in existingKeys.filter((k) => k.status != 'Removed')"
|
|
84
80
|
:key="key.keyId"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
variant="tonal"
|
|
88
|
-
size="small"
|
|
89
|
-
closable
|
|
90
|
-
@click:close="removeExistingKey(key)"
|
|
81
|
+
tone="warn"
|
|
82
|
+
:dot="false"
|
|
91
83
|
>
|
|
84
|
+
<v-icon icon="mdi-key" size="13" />
|
|
92
85
|
{{ key.prefixAndName }}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
<button
|
|
87
|
+
class="passkey__remove"
|
|
88
|
+
:aria-label="`Remove ${key.prefixAndName}`"
|
|
89
|
+
@click="removeExistingKey(key)"
|
|
90
|
+
>
|
|
91
|
+
<v-icon icon="mdi-close-circle" size="14" />
|
|
92
|
+
</button>
|
|
93
|
+
</StatusChip>
|
|
94
|
+
<span class="passkey__current align-self-center">(current)</span>
|
|
97
95
|
</div>
|
|
98
96
|
|
|
99
97
|
<!-- New keys selector -->
|
|
@@ -132,41 +130,16 @@
|
|
|
132
130
|
</v-row>
|
|
133
131
|
|
|
134
132
|
<v-row v-if="errorMessage" no-gutters class="mt-2">
|
|
135
|
-
<p class="
|
|
136
|
-
{{ errorMessage }}
|
|
137
|
-
</p>
|
|
138
|
-
</v-row>
|
|
139
|
-
</v-card-text>
|
|
140
|
-
|
|
141
|
-
<v-toolbar density="compact">
|
|
142
|
-
<v-row no-gutters>
|
|
143
|
-
<v-col cols="6">
|
|
144
|
-
<v-btn
|
|
145
|
-
tile
|
|
146
|
-
block
|
|
147
|
-
variant="text"
|
|
148
|
-
class="text-none"
|
|
149
|
-
size="48"
|
|
150
|
-
text="Cancel"
|
|
151
|
-
@click="emit('close')"
|
|
152
|
-
/>
|
|
153
|
-
</v-col>
|
|
154
|
-
<v-col cols="6">
|
|
155
|
-
<v-btn
|
|
156
|
-
tile
|
|
157
|
-
block
|
|
158
|
-
variant="flat"
|
|
159
|
-
color="black"
|
|
160
|
-
class="text-none"
|
|
161
|
-
size="48"
|
|
162
|
-
:text="isEditMode ? 'Save' : 'Assign'"
|
|
163
|
-
:disabled="!canSubmit"
|
|
164
|
-
:loading="processing"
|
|
165
|
-
@click="handleSubmit"
|
|
166
|
-
/>
|
|
167
|
-
</v-col>
|
|
133
|
+
<p class="passkey__error">{{ errorMessage }}</p>
|
|
168
134
|
</v-row>
|
|
169
|
-
</
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="passkey__footer">
|
|
138
|
+
<AppButton variant="ghost" @click="emit('close')">Cancel</AppButton>
|
|
139
|
+
<AppButton :disabled="!canSubmit" @click="handleSubmit">
|
|
140
|
+
{{ isEditMode ? "Save" : "Assign" }}
|
|
141
|
+
</AppButton>
|
|
142
|
+
</div>
|
|
170
143
|
</v-card>
|
|
171
144
|
</template>
|
|
172
145
|
|
|
@@ -448,4 +421,53 @@ async function handleSubmit() {
|
|
|
448
421
|
}
|
|
449
422
|
</script>
|
|
450
423
|
|
|
451
|
-
<style scoped
|
|
424
|
+
<style scoped>
|
|
425
|
+
.passkey__head {
|
|
426
|
+
display: flex;
|
|
427
|
+
align-items: center;
|
|
428
|
+
justify-content: space-between;
|
|
429
|
+
padding-right: 12px;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.passkey__body {
|
|
433
|
+
padding: 4px 20px 0;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.passkey__name,
|
|
437
|
+
.passkey__current {
|
|
438
|
+
font-size: var(--fs-cell);
|
|
439
|
+
color: var(--text2);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.passkey__error {
|
|
443
|
+
width: 100%;
|
|
444
|
+
margin: 0;
|
|
445
|
+
text-align: center;
|
|
446
|
+
font-size: var(--fs-cell);
|
|
447
|
+
color: var(--err);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* The chip's own remove control - `currentColor`, so it stays on whatever
|
|
451
|
+
tone the chip carries rather than picking a second colour. */
|
|
452
|
+
.passkey__remove {
|
|
453
|
+
display: inline-flex;
|
|
454
|
+
margin-left: 2px;
|
|
455
|
+
padding: 0;
|
|
456
|
+
border: none;
|
|
457
|
+
background: none;
|
|
458
|
+
color: inherit;
|
|
459
|
+
cursor: pointer;
|
|
460
|
+
opacity: 0.75;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.passkey__remove:hover {
|
|
464
|
+
opacity: 1;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.passkey__footer {
|
|
468
|
+
display: flex;
|
|
469
|
+
justify-content: flex-end;
|
|
470
|
+
gap: 8px;
|
|
471
|
+
padding: 16px 20px 20px;
|
|
472
|
+
}
|
|
473
|
+
</style>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE BUTTON.
|
|
3
|
+
|
|
4
|
+
A `<button>`, not a restyled `v-btn`. Vuetify's button carries an overlay
|
|
5
|
+
layer, a ripple, a min-width, uppercase letter-spacing and its own height
|
|
6
|
+
scale, all at a specificity a class has to out-shout one declaration at a
|
|
7
|
+
time - which is why the product's primary buttons still read as Vuetify grey
|
|
8
|
+
under a correct token set.
|
|
9
|
+
|
|
10
|
+
Variants, straight from the handoff:
|
|
11
|
+
primary 40px pill-radius accent fill, 13.5px/800, white label
|
|
12
|
+
ghost 40px, 1px border, card fill, 13.5px/700 in `--text2` - the SAME
|
|
13
|
+
box as primary; the handoff differs only in fill, border and weight
|
|
14
|
+
icon 32px transparent square, 8px radius
|
|
15
|
+
row 30px bordered square - the `more_vert` at the end of a table row
|
|
16
|
+
|
|
17
|
+
Two more the handoff does NOT draw, added as NEW design on the owner's
|
|
18
|
+
ruling of 2026-08-14 - see `assets/css/primitives.css` for why check-out is
|
|
19
|
+
tonal and not solid red:
|
|
20
|
+
ok 40px solid `--ok` fill, white label - CHECK IN
|
|
21
|
+
err 40px tonal `--err` fill, `--err` label, `--err` edge - CHECK OUT
|
|
22
|
+
-->
|
|
23
|
+
<template>
|
|
24
|
+
<component
|
|
25
|
+
:is="to ? resolveLink : 'button'"
|
|
26
|
+
:to="to || undefined"
|
|
27
|
+
:type="to ? undefined : type"
|
|
28
|
+
:disabled="disabled || undefined"
|
|
29
|
+
class="app-btn"
|
|
30
|
+
:class="variant === 'primary' ? '' : `app-btn--${variant}`"
|
|
31
|
+
>
|
|
32
|
+
<v-icon v-if="icon" :icon="icon" :size="iconSize" />
|
|
33
|
+
<slot />
|
|
34
|
+
</component>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { resolveComponent } from "vue";
|
|
39
|
+
|
|
40
|
+
const props = defineProps({
|
|
41
|
+
variant: {
|
|
42
|
+
type: String as PropType<"primary" | "ghost" | "icon" | "row" | "ok" | "err">,
|
|
43
|
+
default: "primary",
|
|
44
|
+
},
|
|
45
|
+
icon: { type: String, default: "" },
|
|
46
|
+
type: { type: String, default: "button" },
|
|
47
|
+
disabled: { type: Boolean, default: false },
|
|
48
|
+
/** Given, the button routes - same `to` a `v-btn` would have taken. */
|
|
49
|
+
to: { type: [String, Object] as PropType<any>, default: undefined },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const resolveLink = resolveComponent("NuxtLink");
|
|
53
|
+
|
|
54
|
+
const iconSize = computed(() => {
|
|
55
|
+
if (props.variant === "primary") return 18;
|
|
56
|
+
if (props.variant === "row") return 17;
|
|
57
|
+
/* The check-in tick and the check-out cross are drawn at 16px - a glyph, not
|
|
58
|
+
a second focal point beside a two-word label. */
|
|
59
|
+
if (props.variant === "ok" || props.variant === "err") return 16;
|
|
60
|
+
return 18;
|
|
61
|
+
});
|
|
62
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE CARD.
|
|
3
|
+
|
|
4
|
+
14px radius, a 1px `--border` rule and the design's one-pixel shadow, on
|
|
5
|
+
`--card`. A plain element rather than a `v-card`, because `v-card` brings its
|
|
6
|
+
own elevation, radius and surface colour at a specificity that a class has to
|
|
7
|
+
fight - which is why the radius kept coming back as 4px in Phase 3.
|
|
8
|
+
-->
|
|
9
|
+
<template>
|
|
10
|
+
<div class="app-card" :class="{ 'app-card--flush': flush }">
|
|
11
|
+
<slot />
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
defineProps({
|
|
17
|
+
/** Let content (a menu, a dropdown) escape the card's rounded corners. */
|
|
18
|
+
flush: { type: Boolean, default: false },
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE DATE FIELD.
|
|
3
|
+
|
|
4
|
+
The same 40px box with a calendar glyph, as the handoff draws the two date
|
|
5
|
+
ends of a filter row. `<input type="date">` - the browser's own picker, which
|
|
6
|
+
is the one a phone user already knows and the one that is localised, keyboard
|
|
7
|
+
accessible and free.
|
|
8
|
+
-->
|
|
9
|
+
<template>
|
|
10
|
+
<label v-bind="rootAttrs" class="app-field" :class="{ 'app-field--compact': compact }">
|
|
11
|
+
<v-icon class="app-field__icon" icon="mdi-calendar-month-outline" size="17" />
|
|
12
|
+
<span v-if="label" class="app-field__label">{{ label }}</span>
|
|
13
|
+
<input
|
|
14
|
+
class="app-field__input"
|
|
15
|
+
v-bind="inputAttrs"
|
|
16
|
+
type="date"
|
|
17
|
+
:value="model"
|
|
18
|
+
:min="min || undefined"
|
|
19
|
+
:max="max || undefined"
|
|
20
|
+
:disabled="disabled"
|
|
21
|
+
@input="model = ($event.target as HTMLInputElement).value"
|
|
22
|
+
/>
|
|
23
|
+
</label>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
import { computed, useAttrs } from "vue";
|
|
28
|
+
|
|
29
|
+
/** Same split as `AppField`, and for the same reason: the root is a `<label>`,
|
|
30
|
+
so an `aria-label` left to fall through would never reach the input. */
|
|
31
|
+
defineOptions({ inheritAttrs: false });
|
|
32
|
+
|
|
33
|
+
const attrs = useAttrs();
|
|
34
|
+
const rootAttrs = computed(() => ({ class: attrs.class, style: attrs.style }));
|
|
35
|
+
const inputAttrs = computed(() => {
|
|
36
|
+
const { class: _c, style: _s, ...rest } = attrs;
|
|
37
|
+
return rest;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const model = defineModel({ type: String, default: "" });
|
|
41
|
+
|
|
42
|
+
defineProps({
|
|
43
|
+
label: { type: String, default: "" },
|
|
44
|
+
min: { type: String, default: "" },
|
|
45
|
+
max: { type: String, default: "" },
|
|
46
|
+
compact: { type: Boolean, default: false },
|
|
47
|
+
disabled: { type: Boolean, default: false },
|
|
48
|
+
});
|
|
49
|
+
</script>
|