@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
|
@@ -13,26 +13,35 @@
|
|
|
13
13
|
:prepend-icon="prop.icon"
|
|
14
14
|
append-icon="mdi-chevron-down"
|
|
15
15
|
:to="props.route"
|
|
16
|
-
class="
|
|
16
|
+
class="switch-context__activator"
|
|
17
17
|
>
|
|
18
|
-
|
|
18
|
+
<!-- The design prints the organisation and site names in full, but
|
|
19
|
+
mid-word line breaks ("Manageme-nt", "Condominiu-m") are not
|
|
20
|
+
acceptable. Wrap at word boundaries onto up to two lines, then
|
|
21
|
+
ellipsis; the title attribute keeps the full name on hover. -->
|
|
22
|
+
<v-list-item-title
|
|
23
|
+
class="switch-context__name"
|
|
24
|
+
:title="name || `Select ${prop.title}`"
|
|
25
|
+
>
|
|
19
26
|
{{ name || `Select ${prop.title}` }}
|
|
20
27
|
</v-list-item-title>
|
|
21
28
|
</v-list-item>
|
|
22
29
|
</v-list>
|
|
23
30
|
</template>
|
|
24
31
|
|
|
25
|
-
<v-card width="300px"
|
|
26
|
-
<span class="
|
|
32
|
+
<v-card width="300px" elevation="0" class="switch-context__panel">
|
|
33
|
+
<span class="switch-context__heading">
|
|
27
34
|
Switch {{ prop.title }} context
|
|
28
35
|
</span>
|
|
29
36
|
<v-text-field
|
|
30
37
|
v-model="search"
|
|
31
|
-
@keydown.enter="
|
|
38
|
+
@keydown.enter="triggerSearch"
|
|
39
|
+
clearable
|
|
40
|
+
@click:clear="clearSearch"
|
|
32
41
|
density="compact"
|
|
33
42
|
width="100%"
|
|
34
43
|
hide-details
|
|
35
|
-
class="mb-2"
|
|
44
|
+
class="filter-field mb-2"
|
|
36
45
|
>
|
|
37
46
|
<template #prepend-inner>
|
|
38
47
|
<v-icon>mdi-magnify</v-icon>
|
|
@@ -42,7 +51,7 @@
|
|
|
42
51
|
<v-list class="pa-0 my-2" density="compact" max-height="200px">
|
|
43
52
|
<v-list-item
|
|
44
53
|
density="compact"
|
|
45
|
-
class="
|
|
54
|
+
class="switch-context__row"
|
|
46
55
|
v-for="item in prop.items"
|
|
47
56
|
:key="item.value"
|
|
48
57
|
@click="selectItem(item.value)"
|
|
@@ -55,7 +64,41 @@
|
|
|
55
64
|
</v-list-item>
|
|
56
65
|
</v-list>
|
|
57
66
|
|
|
58
|
-
|
|
67
|
+
<!-- The two actions under the list. Seven applications hand-wrote this
|
|
68
|
+
same block, and all seven wrote `color="primary" size="large"` on a
|
|
69
|
+
filled button - `primary` is the design's FOREGROUND token, so the
|
|
70
|
+
fill was off-palette and 44px against the design's 40px. Stating it
|
|
71
|
+
once here means an application passes two labels instead of thirty
|
|
72
|
+
lines. Anything passed to #footer still overrides it wholesale. -->
|
|
73
|
+
<slot name="footer">
|
|
74
|
+
<v-row v-if="prop.manageLabel || prop.addLabel" no-gutters>
|
|
75
|
+
<v-col
|
|
76
|
+
v-if="prop.manageLabel"
|
|
77
|
+
cols="12"
|
|
78
|
+
:class="{ 'mb-2': prop.addLabel }"
|
|
79
|
+
>
|
|
80
|
+
<v-btn
|
|
81
|
+
block
|
|
82
|
+
variant="flat"
|
|
83
|
+
class="screen-btn-primary"
|
|
84
|
+
@click="emit('manage')"
|
|
85
|
+
>
|
|
86
|
+
{{ prop.manageLabel }}
|
|
87
|
+
</v-btn>
|
|
88
|
+
</v-col>
|
|
89
|
+
|
|
90
|
+
<v-col v-if="prop.addLabel" cols="12">
|
|
91
|
+
<v-btn
|
|
92
|
+
block
|
|
93
|
+
variant="flat"
|
|
94
|
+
class="screen-btn-primary"
|
|
95
|
+
@click="emit('add')"
|
|
96
|
+
>
|
|
97
|
+
{{ prop.addLabel }}
|
|
98
|
+
</v-btn>
|
|
99
|
+
</v-col>
|
|
100
|
+
</v-row>
|
|
101
|
+
</slot>
|
|
59
102
|
</v-card>
|
|
60
103
|
</v-menu>
|
|
61
104
|
</template>
|
|
@@ -82,6 +125,25 @@ const prop = defineProps({
|
|
|
82
125
|
required: false,
|
|
83
126
|
default: "",
|
|
84
127
|
},
|
|
128
|
+
/**
|
|
129
|
+
* The default #footer's two buttons. Each draws only when its label is a
|
|
130
|
+
* non-empty string, so an application that states neither gets the empty
|
|
131
|
+
* footer it has today, and one that states only `addLabel` gets one button.
|
|
132
|
+
* Deliberately opt-in by label rather than by a boolean: the wording differs
|
|
133
|
+
* per context ("Manage organization" / "Manage sites") and is not derivable
|
|
134
|
+
* from `title`, which is already "Organizations" in one place and "site" in
|
|
135
|
+
* the other.
|
|
136
|
+
*/
|
|
137
|
+
manageLabel: {
|
|
138
|
+
type: String,
|
|
139
|
+
required: false,
|
|
140
|
+
default: "",
|
|
141
|
+
},
|
|
142
|
+
addLabel: {
|
|
143
|
+
type: String,
|
|
144
|
+
required: false,
|
|
145
|
+
default: "",
|
|
146
|
+
},
|
|
85
147
|
});
|
|
86
148
|
|
|
87
149
|
const menu = defineModel<boolean>("menu", { required: false, default: false });
|
|
@@ -94,15 +156,145 @@ const search = defineModel<string>("search", {
|
|
|
94
156
|
default: "",
|
|
95
157
|
});
|
|
96
158
|
|
|
97
|
-
const emit = defineEmits(["search", "select"]);
|
|
159
|
+
const emit = defineEmits(["search", "select", "manage", "add"]);
|
|
98
160
|
|
|
99
161
|
const name = computed(() => {
|
|
100
162
|
return prop.items.find((item) => item.value === selected.value)?.text ?? "";
|
|
101
163
|
});
|
|
102
164
|
|
|
165
|
+
/**
|
|
166
|
+
* THE BREADCRUMB'S SITE HALF.
|
|
167
|
+
*
|
|
168
|
+
* The top bar draws `Site name / Page`, but the site's NAME is only ever
|
|
169
|
+
* resolved here - each application loads its own site list and hands it to
|
|
170
|
+
* this selector, so the bar had nothing to print and showed the page alone.
|
|
171
|
+
* The label already on screen is mirrored into shared state for the bar to
|
|
172
|
+
* read. It is a label: no request is made, and what the selector selects is
|
|
173
|
+
* untouched.
|
|
174
|
+
*/
|
|
175
|
+
const shellSiteName = useState<string>("shell-context-site-name", () => "");
|
|
176
|
+
|
|
177
|
+
watchEffect(() => {
|
|
178
|
+
if (prop.title?.toLowerCase() === "site" && name.value) {
|
|
179
|
+
shellSiteName.value = name.value;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
let searchDebounce: ReturnType<typeof setTimeout> | null = null;
|
|
184
|
+
let skipNextWatch = false;
|
|
185
|
+
|
|
186
|
+
const triggerSearch = () => {
|
|
187
|
+
if (searchDebounce) clearTimeout(searchDebounce);
|
|
188
|
+
emit("search");
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const clearSearch = () => {
|
|
192
|
+
skipNextWatch = true;
|
|
193
|
+
search.value = "";
|
|
194
|
+
triggerSearch();
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
watch(search, () => {
|
|
198
|
+
if (skipNextWatch) {
|
|
199
|
+
skipNextWatch = false;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (searchDebounce) clearTimeout(searchDebounce);
|
|
203
|
+
searchDebounce = setTimeout(triggerSearch, 1000);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
onBeforeUnmount(() => {
|
|
207
|
+
if (searchDebounce) clearTimeout(searchDebounce);
|
|
208
|
+
});
|
|
209
|
+
|
|
103
210
|
const selectItem = (value: string) => {
|
|
104
211
|
selected.value = value;
|
|
105
212
|
menu.value = false;
|
|
106
213
|
emit("select", value);
|
|
107
214
|
};
|
|
108
215
|
</script>
|
|
216
|
+
|
|
217
|
+
<style scoped>
|
|
218
|
+
/**
|
|
219
|
+
* The rail's context selector and the panel it opens.
|
|
220
|
+
*
|
|
221
|
+
* The panel was a factory `v-card` with `pa-4` and Vuetify's `text-subtitle-2`
|
|
222
|
+
* / `text-caption` scale, so it was the one menu in the shell that did NOT
|
|
223
|
+
* match the profile menu the top bar already draws. It takes the SAME surface
|
|
224
|
+
* as `Layout/Header`'s `.app-profile-menu` - 14px radius, the design's menu
|
|
225
|
+
* shadow, a 1px border - rather than a third treatment invented here.
|
|
226
|
+
*
|
|
227
|
+
* The typography utilities also sat on elements that CONTAIN icons
|
|
228
|
+
* (`prepend-icon`, the chevron, the tick), which is the exact shape the central
|
|
229
|
+
* icon-font guard exists to catch. Tokens carry no such risk.
|
|
230
|
+
*/
|
|
231
|
+
.switch-context__activator {
|
|
232
|
+
font-size: var(--fs-nav);
|
|
233
|
+
font-weight: 700;
|
|
234
|
+
color: var(--text);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* The panel is a `v-card`, and Vuetify puts it inside `.v-overlay__content`.
|
|
238
|
+
`screens.css` gives EVERY card in an overlay the modal shape - 16px and the
|
|
239
|
+
modal shadow - with `!important`, because dialogs open from sixty components
|
|
240
|
+
and the shape had to be stated once. A menu is an overlay too, so the panel
|
|
241
|
+
was silently taking the modal shape: measured 16px and
|
|
242
|
+
`rgba(10, 12, 16, 0.3) 0 24px 64px`, not the 14px `--r-menu` and the lighter
|
|
243
|
+
`--shadow-menu` it asks for.
|
|
244
|
+
`!important` alone does NOT win it back: `.switch-context__panel[data-v-...]`
|
|
245
|
+
and `.v-overlay__content > .v-card` are both two classes, the scope attribute
|
|
246
|
+
counts, and a tie goes to whichever sheet loaded last - which is screens.css.
|
|
247
|
+
Measured still 16px with `!important` on the plain class. So the selector
|
|
248
|
+
carries the overlay class too, to out-specify rather than tie. */
|
|
249
|
+
.v-overlay__content > .switch-context__panel {
|
|
250
|
+
border-radius: var(--r-menu) !important;
|
|
251
|
+
box-shadow: var(--shadow-menu) !important;
|
|
252
|
+
border: 1px solid var(--border);
|
|
253
|
+
background: var(--card);
|
|
254
|
+
padding: 14px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.switch-context__heading {
|
|
258
|
+
display: block;
|
|
259
|
+
font-size: var(--fs-btn);
|
|
260
|
+
font-weight: 800;
|
|
261
|
+
line-height: 1.3;
|
|
262
|
+
color: var(--text);
|
|
263
|
+
margin-bottom: 10px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* The profile menu's row, to the pixel - same padding, same radius, same
|
|
267
|
+
weight - because it is the same control one panel over.
|
|
268
|
+
`!important` ONLY on the radius, and it is not optional: a `v-list` with no
|
|
269
|
+
`rounded` prop stamps Vuetify's own `rounded-0` utility onto every
|
|
270
|
+
`v-list-item` inside it, and that utility IS `!important`. So this rule was
|
|
271
|
+
losing silently - measured `0px`, not the 9px it asks for - and the hover
|
|
272
|
+
painted a square block inside a 14px-rounded panel. The header's rows escape
|
|
273
|
+
it only because they are plain `<button>`s, which is why the two menus did
|
|
274
|
+
not match despite the same value. */
|
|
275
|
+
.switch-context__row {
|
|
276
|
+
border-radius: var(--r-inner-sm) !important;
|
|
277
|
+
font-size: var(--fs-btn);
|
|
278
|
+
font-weight: var(--fw-btn);
|
|
279
|
+
color: var(--text2);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.switch-context__row:hover {
|
|
283
|
+
background: var(--hover);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* Wrap the full organisation/site name at word boundaries onto up to two
|
|
287
|
+
lines, then ellipsis - never break mid-word. Full name is on the title
|
|
288
|
+
attribute for hover. */
|
|
289
|
+
.switch-context__name {
|
|
290
|
+
display: -webkit-box;
|
|
291
|
+
line-clamp: 2;
|
|
292
|
+
-webkit-line-clamp: 2;
|
|
293
|
+
-webkit-box-orient: vertical;
|
|
294
|
+
overflow: hidden;
|
|
295
|
+
white-space: normal;
|
|
296
|
+
overflow-wrap: normal;
|
|
297
|
+
word-break: normal;
|
|
298
|
+
line-height: 1.25;
|
|
299
|
+
}
|
|
300
|
+
</style>
|
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
<slot name="actions">
|
|
6
6
|
<v-btn
|
|
7
7
|
v-if="canCreate"
|
|
8
|
-
class="text-none"
|
|
9
|
-
|
|
10
|
-
variant="tonal"
|
|
11
|
-
size="large"
|
|
8
|
+
class="text-none screen-btn-primary"
|
|
9
|
+
variant="flat"
|
|
12
10
|
@click="emits('create')"
|
|
13
11
|
>
|
|
14
12
|
{{ createLabel }}
|
|
@@ -18,16 +16,11 @@
|
|
|
18
16
|
</v-col>
|
|
19
17
|
|
|
20
18
|
<v-col cols="12">
|
|
21
|
-
<v-card
|
|
22
|
-
width="100%"
|
|
23
|
-
variant="outlined"
|
|
24
|
-
border="thin"
|
|
25
|
-
rounded="lg"
|
|
26
|
-
:loading="loading"
|
|
27
|
-
>
|
|
19
|
+
<v-card width="100%" variant="flat" class="table-card" :loading="loading">
|
|
28
20
|
<v-toolbar
|
|
29
21
|
density="compact"
|
|
30
|
-
color="
|
|
22
|
+
color="transparent"
|
|
23
|
+
class="table-card__toolbar"
|
|
31
24
|
:extension-height="extensionHeight"
|
|
32
25
|
>
|
|
33
26
|
<template #prepend>
|
|
@@ -46,7 +39,7 @@
|
|
|
46
39
|
|
|
47
40
|
<template #append>
|
|
48
41
|
<v-row no-gutters justify="end" align="center">
|
|
49
|
-
<span class="mr-2
|
|
42
|
+
<span class="mr-2 table-card__range">
|
|
50
43
|
{{ pageRange }}
|
|
51
44
|
</span>
|
|
52
45
|
<local-pagination
|
|
@@ -68,25 +61,23 @@
|
|
|
68
61
|
:max-height="`calc(100vh - (${offset}px))`"
|
|
69
62
|
class="overflow-y-auto"
|
|
70
63
|
>
|
|
71
|
-
<v-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
>
|
|
81
|
-
|
|
82
|
-
</v-col>
|
|
83
|
-
</v-row>
|
|
64
|
+
<div v-if="groupedItems.length === 0" class="table-card__empty">
|
|
65
|
+
<!--
|
|
66
|
+
The empty block was hard-coded, so a caller had no way to say
|
|
67
|
+
"this is not empty, the request failed" - every 500 rendered as
|
|
68
|
+
the neutral "nothing here". The default is unchanged.
|
|
69
|
+
-->
|
|
70
|
+
<slot name="no-data">
|
|
71
|
+
<v-icon icon="mdi-magnify-close" size="32" />
|
|
72
|
+
<span>{{ noDataText }}</span>
|
|
73
|
+
</slot>
|
|
74
|
+
</div>
|
|
84
75
|
|
|
85
76
|
<template
|
|
86
77
|
v-for="(group, groupIndex) in groupedItems"
|
|
87
78
|
:key="`group-${groupIndex}`"
|
|
88
79
|
>
|
|
89
|
-
<v-sheet color="
|
|
80
|
+
<v-sheet color="transparent" class="table-card__group" border="b t">
|
|
90
81
|
<v-row no-gutters align="center" class="px-4 py-2">
|
|
91
82
|
<v-col cols="auto" class="d-flex align-center ga-2">
|
|
92
83
|
<span
|
|
@@ -94,39 +85,37 @@
|
|
|
94
85
|
>
|
|
95
86
|
Set {{ group.set }}
|
|
96
87
|
</span>
|
|
97
|
-
<
|
|
88
|
+
<StatusChip
|
|
98
89
|
v-if="group.isScheduleTask"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
variant="tonal"
|
|
102
|
-
prepend-icon="mdi-calendar-clock"
|
|
90
|
+
tone="info"
|
|
91
|
+
:dot="false"
|
|
103
92
|
class="text-none"
|
|
104
93
|
>
|
|
94
|
+
<v-icon size="12">mdi-calendar-clock</v-icon>
|
|
105
95
|
Schedule Task
|
|
106
|
-
</
|
|
96
|
+
</StatusChip>
|
|
107
97
|
<slot name="group-header-chips" :group="group">
|
|
108
|
-
<
|
|
98
|
+
<StatusChip
|
|
109
99
|
v-if="group.completedByName && isGroupComplete(group)"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
variant="tonal"
|
|
113
|
-
prepend-icon="mdi-check-circle-outline"
|
|
100
|
+
status="Completed"
|
|
101
|
+
:dot="false"
|
|
114
102
|
class="text-none"
|
|
115
103
|
>
|
|
104
|
+
<v-icon size="12">mdi-check-circle-outline</v-icon>
|
|
116
105
|
Completed · {{ group.completedByName }}
|
|
117
|
-
</
|
|
118
|
-
<
|
|
106
|
+
</StatusChip>
|
|
107
|
+
<StatusChip
|
|
119
108
|
v-else-if="
|
|
120
109
|
group.completedByName && isGroupInProgress(group)
|
|
121
110
|
"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
prepend-icon="mdi-progress-clock"
|
|
111
|
+
status="Ongoing"
|
|
112
|
+
tone="warn"
|
|
113
|
+
:dot="false"
|
|
126
114
|
class="text-none"
|
|
127
115
|
>
|
|
116
|
+
<v-icon size="12">mdi-progress-clock</v-icon>
|
|
128
117
|
Ongoing · {{ group.completedByName }}
|
|
129
|
-
</
|
|
118
|
+
</StatusChip>
|
|
130
119
|
</slot>
|
|
131
120
|
</v-col>
|
|
132
121
|
<v-spacer />
|
|
@@ -139,7 +128,8 @@
|
|
|
139
128
|
<v-sheet
|
|
140
129
|
v-for="item in group.items"
|
|
141
130
|
:key="item[itemValue]"
|
|
142
|
-
color="
|
|
131
|
+
color="transparent"
|
|
132
|
+
class="table-card__row"
|
|
143
133
|
border="b"
|
|
144
134
|
>
|
|
145
135
|
<v-row no-gutters align="center" class="px-4 py-2">
|
package/components/TableList.vue
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
>
|
|
9
|
-
<v-toolbar density="compact" color="grey-lighten-4">
|
|
2
|
+
<v-card width="100%" elevation="0" class="table-card" :loading="loading">
|
|
3
|
+
<v-toolbar
|
|
4
|
+
density="compact"
|
|
5
|
+
class="table-card__toolbar px-3"
|
|
6
|
+
color="transparent"
|
|
7
|
+
>
|
|
10
8
|
<template #prepend>
|
|
11
9
|
<v-checkbox v-model="selectAll" hide-details class="mx-1"></v-checkbox>
|
|
12
10
|
|
|
@@ -17,7 +15,7 @@
|
|
|
17
15
|
|
|
18
16
|
<template #append>
|
|
19
17
|
<v-row no-gutters justify="end" align="center">
|
|
20
|
-
<span class="mr-2
|
|
18
|
+
<span class="table-card__range mr-2">
|
|
21
19
|
{{ props.pageRange }}
|
|
22
20
|
</span>
|
|
23
21
|
<div class="arrow-navigation">
|
|
@@ -41,7 +39,9 @@
|
|
|
41
39
|
</template>
|
|
42
40
|
|
|
43
41
|
<template v-if="$slots.extension" v-slot:extension>
|
|
44
|
-
<
|
|
42
|
+
<div class="table-card__extension">
|
|
43
|
+
<slot name="extension"></slot>
|
|
44
|
+
</div>
|
|
45
45
|
</template>
|
|
46
46
|
</v-toolbar>
|
|
47
47
|
<div :style="{ overflowY: 'auto', height }">
|
|
@@ -1,55 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
<v-toolbar density="compact" class="px-3 border-b-sm" color="transparent">
|
|
4
|
-
<template #prepend>
|
|
5
|
-
<slot name="title">Title</slot>
|
|
6
|
-
</template>
|
|
1
|
+
<!--
|
|
2
|
+
THE SECOND LIST CARD - the one `ListView` and `WorkOrder/ListView` draw.
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
Was a `v-card` with TWO `v-toolbar`s: the caller's title + action button on
|
|
5
|
+
the top one, and the page range + arrows on a second one under the table. The
|
|
6
|
+
design has neither. The page name and the screen's primary action belong on
|
|
7
|
+
ONE title row ABOVE the card (`PageHeader`), and the range and the pager
|
|
8
|
+
belong on the card's own single top row (`CardToolbar`) - which is exactly
|
|
9
|
+
the shape `TableMain` was converted to.
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
>
|
|
33
|
-
<template v-if="$slots.item" v-slot:item>
|
|
34
|
-
<slot name="item" :items="props.items"></slot>
|
|
11
|
+
VISUAL ONLY. Same headers, same items, same slots, same row click, same
|
|
12
|
+
`page` model, same `update:pagination` payloads, same increment/decrement
|
|
13
|
+
guards. Nothing is fetched, filtered, sorted or emitted differently; the two
|
|
14
|
+
toolbars became one row and moved, and that is the whole change.
|
|
15
|
+
-->
|
|
16
|
+
<template>
|
|
17
|
+
<div class="list-card">
|
|
18
|
+
<!--
|
|
19
|
+
The fold: the caller's `title` slot used to be the `v-toolbar`'s prepend,
|
|
20
|
+
i.e. a heading INSIDE the card, and its action button sat on the same
|
|
21
|
+
bar. Both are the design's page-title row, so they move up to it. Only
|
|
22
|
+
drawn when the caller actually supplies a title - a caller that passes
|
|
23
|
+
none used to get the literal fallback word "Title" on the bar, which is
|
|
24
|
+
not a heading anybody asked for.
|
|
25
|
+
-->
|
|
26
|
+
<PageHeader v-if="$slots.title">
|
|
27
|
+
<template #title>
|
|
28
|
+
<slot name="title" />
|
|
35
29
|
</template>
|
|
36
30
|
|
|
37
|
-
<template v-
|
|
38
|
-
<
|
|
39
|
-
<td v-for="col in props.headers" :key="col.value">
|
|
40
|
-
<slot :name="col.value" :item="item">
|
|
41
|
-
{{ item[col.value] }}
|
|
42
|
-
</slot>
|
|
43
|
-
</td>
|
|
44
|
-
</tr>
|
|
31
|
+
<template v-if="$slots['action-button']" #actions>
|
|
32
|
+
<slot name="action-button"></slot>
|
|
45
33
|
</template>
|
|
46
|
-
</
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
34
|
+
</PageHeader>
|
|
35
|
+
|
|
36
|
+
<AppCard class="table-card">
|
|
37
|
+
<!--
|
|
38
|
+
ONE toolbar. The range keeps the exact string the caller computed and
|
|
39
|
+
the arrows keep their own disabled rules and emits; they have simply
|
|
40
|
+
stopped costing a second bar under the table.
|
|
41
|
+
-->
|
|
42
|
+
<CardToolbar :count="props.pageRange">
|
|
43
|
+
<template #right>
|
|
53
44
|
<div class="arrow-navigation">
|
|
54
45
|
<v-btn
|
|
55
46
|
icon="mdi-chevron-left"
|
|
@@ -66,14 +57,58 @@
|
|
|
66
57
|
@click="increment"
|
|
67
58
|
/>
|
|
68
59
|
</div>
|
|
69
|
-
</
|
|
70
|
-
</
|
|
60
|
+
</template>
|
|
61
|
+
</CardToolbar>
|
|
71
62
|
|
|
72
|
-
|
|
63
|
+
<!--
|
|
64
|
+
`extension` used to be rendered by BOTH toolbars, i.e. the same content
|
|
65
|
+
twice, above and below the table. It is one row now, under the toolbar,
|
|
66
|
+
where `TableMain` puts a filter row.
|
|
67
|
+
-->
|
|
68
|
+
<div v-if="$slots.extension" class="table-card__extension">
|
|
73
69
|
<slot name="extension"></slot>
|
|
74
|
-
</
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<v-data-table
|
|
73
|
+
v-model="selected"
|
|
74
|
+
:headers="props.headers"
|
|
75
|
+
:items="props.items"
|
|
76
|
+
item-value="_id"
|
|
77
|
+
items-per-page="20"
|
|
78
|
+
fixed-header
|
|
79
|
+
hide-default-footer
|
|
80
|
+
selec
|
|
81
|
+
:loading="props.loading"
|
|
82
|
+
@click:row="handleRowClick"
|
|
83
|
+
:style="`max-height:${props.height}`"
|
|
84
|
+
:class="{ 'clickable-rows': props.clickableRows }"
|
|
85
|
+
>
|
|
86
|
+
<template v-if="$slots.item" v-slot:item>
|
|
87
|
+
<slot name="item" :items="props.items"></slot>
|
|
88
|
+
</template>
|
|
89
|
+
|
|
90
|
+
<template v-slot:item="{ item }">
|
|
91
|
+
<tr @click="handleRowClick(item)">
|
|
92
|
+
<td v-for="col in props.headers" :key="col.value">
|
|
93
|
+
<slot :name="col.value" :item="item">
|
|
94
|
+
{{ item[col.value] }}
|
|
95
|
+
</slot>
|
|
96
|
+
</td>
|
|
97
|
+
</tr>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<!--
|
|
101
|
+
Forwarded so a caller can say "this is not empty, the request
|
|
102
|
+
failed". Without it every 500 rendered as the table's neutral
|
|
103
|
+
"No data available". Vuetify's own default is unchanged when the
|
|
104
|
+
caller passes nothing.
|
|
105
|
+
-->
|
|
106
|
+
<template v-if="$slots['no-data']" #no-data>
|
|
107
|
+
<slot name="no-data" />
|
|
108
|
+
</template>
|
|
109
|
+
</v-data-table>
|
|
110
|
+
</AppCard>
|
|
111
|
+
</div>
|
|
77
112
|
</template>
|
|
78
113
|
|
|
79
114
|
<script setup lang="ts">
|
|
@@ -159,6 +194,22 @@ watch(selectAll, (curr) => {
|
|
|
159
194
|
}
|
|
160
195
|
|
|
161
196
|
.clickable-rows tbody tr:hover {
|
|
162
|
-
background-color:
|
|
197
|
+
background-color: var(--hover);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
Every caller wraps its title in `<span class="text-h6 font-weight-regular">`,
|
|
202
|
+
because the old toolbar drew no type of its own. Vuetify's typography classes
|
|
203
|
+
are `!important`, so that span would keep 20px/400 inside the design's
|
|
204
|
+
22px/800 heading and the fold would look like it had not happened. The
|
|
205
|
+
heading owns the type now; the caller's span is left to carry whatever else
|
|
206
|
+
it says. Confined to this card's own page head, so no other `PageHeader` is
|
|
207
|
+
touched - and no app repo has to be edited for the title to land.
|
|
208
|
+
*/
|
|
209
|
+
.list-card :deep(.app-page-head__title) * {
|
|
210
|
+
font-size: inherit !important;
|
|
211
|
+
font-weight: inherit !important;
|
|
212
|
+
line-height: inherit !important;
|
|
213
|
+
letter-spacing: inherit !important;
|
|
163
214
|
}
|
|
164
215
|
</style>
|