@7365admin1/layer-common 3.2.1 → 3.2.2-staging.101
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/.changeset/camera-wall-gated-endpoint.md +47 -0
- package/.changeset/camera-wall-plain-english.md +60 -0
- package/.changeset/camera-wall-selection-and-guidance.md +65 -0
- package/.changeset/dark-primary-contrast.md +39 -0
- package/.changeset/dashboard-dark-theme-contrast.md +15 -0
- package/.changeset/notification-preferences.md +17 -0
- package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
- package/.changeset/service-provider-invitation-actions.md +28 -0
- package/assets/css/primitives.css +680 -0
- package/assets/css/screens.css +541 -0
- package/assets/css/shell.css +258 -0
- package/assets/css/tokens.css +220 -0
- package/components/AccessCardQrTagging.vue +1 -1
- package/components/AccessManagement.vue +53 -72
- package/components/AppButton.vue +52 -0
- package/components/AppCard.vue +20 -0
- package/components/AppDateField.vue +35 -0
- package/components/AppField.vue +51 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +203 -0
- package/components/AreaChecklistHistoryLogs.vue +51 -44
- package/components/AreaChecklistHistoryMain.vue +6 -2
- package/components/AreaMain.vue +9 -17
- package/components/AttendanceMain.vue +4 -3
- package/components/Avatar/Main.vue +54 -2
- package/components/BillingMain.vue +26 -30
- package/components/BuildingManagement/buildings.vue +27 -38
- package/components/BuildingManagement/units.vue +26 -42
- package/components/BulletinBoardManagement.vue +23 -10
- package/components/CameraWall.vue +1034 -0
- package/components/CameraWallTile.vue +818 -0
- package/components/CardToolbar.vue +37 -0
- package/components/CleaningScheduleMain.vue +29 -46
- package/components/ClientMain.vue +90 -78
- package/components/DashboardEmptyState.vue +11 -1
- package/components/DashboardMain.vue +349 -275
- package/components/DashboardPanel.vue +1 -1
- package/components/DocumentManagement.vue +129 -93
- package/components/EntryPassInformation.vue +4 -4
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +4 -6
- package/components/EquipmentManagementMain.vue +9 -7
- package/components/FeedbackMain.vue +42 -56
- package/components/FormDialog.vue +60 -21
- package/components/HidAccessLogDashboard.vue +119 -44
- package/components/HidCameraCaptureDialog.vue +199 -0
- package/components/HidIntercomManagement.vue +825 -202
- package/components/HidQrCodeConfiguration.vue +927 -93
- package/components/HidServiceSettingsPanel.vue +20 -1
- package/components/HidUserEnrollment.vue +454 -106
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +289 -37
- package/components/Layout/NavigationDrawer.vue +340 -30
- package/components/ManageChecklistMain.vue +17 -17
- package/components/MemberMain.vue +79 -60
- package/components/MyAttendanceMain.vue +3 -5
- package/components/NavigationItem.vue +9 -0
- package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
- package/components/NotificationSettings.vue +361 -0
- package/components/OnlineFormConfigurationForm.vue +620 -224
- package/components/OnlineFormFill.vue +36 -8
- package/components/OnlineFormsConfiguration.vue +7 -3
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +104 -45
- package/components/QrTemplate/CreditCardLandscape.vue +19 -12
- package/components/QrTemplate/PrintDialog.vue +209 -196
- package/components/RolePermissionMain.vue +184 -91
- package/components/ScanVisitorQRCode.vue +35 -7
- package/components/ScheduleAreaMain.vue +15 -49
- package/components/ScheduleTaskMain.vue +23 -26
- package/components/ScheduleTastTicketMain.vue +18 -43
- package/components/ServiceProviderInvitationPrompt.vue +150 -0
- package/components/ServiceProviderMain.vue +346 -157
- package/components/SiteSettings.vue +25 -10
- package/components/SiteSettingsWorkOrder.vue +61 -30
- package/components/SpecificAttr.vue +10 -2
- package/components/StatusChip.vue +93 -0
- package/components/StockCard.vue +17 -12
- package/components/SwitchContext.vue +33 -1
- package/components/TableHygiene.vue +29 -46
- package/components/TableList.vue +1 -1
- package/components/TableListSecondary.vue +1 -1
- package/components/TableMain.vue +161 -77
- package/components/TableV3.vue +61 -66
- package/components/UnitMain.vue +9 -17
- package/components/VehicleManagement.vue +20 -9
- package/components/VisitorForm.vue +294 -78
- package/components/VisitorManagement.vue +221 -71
- package/components/VisitorSocketPopUp.vue +56 -2
- package/components/WorkOrder/Main.vue +42 -51
- package/composables/useComment.ts +3 -3
- package/composables/useHidAmico.ts +151 -0
- package/composables/useHidNavigation.ts +0 -7
- package/composables/useLocalAuth.ts +9 -36
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOnlineFormPages.ts +2 -0
- package/composables/useOrg.ts +1 -1
- package/composables/useRole.ts +22 -0
- package/composables/useServiceProviderInvitation.ts +145 -0
- package/composables/useSipWebPhone.ts +336 -0
- package/composables/useSiteSettings.ts +50 -0
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/composables/useVisitorSocket.ts +26 -0
- package/composables/useWebUsb.ts +127 -37
- package/nuxt.config.ts +31 -0
- package/package.json +4 -1
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/pages/notification-settings.vue +19 -0
- package/plugins/secure-member.client.ts +21 -56
- package/plugins/vuetify.ts +68 -9
- package/test/visitor-socket.test.mjs +36 -0
- package/types/org.d.ts +2 -0
- package/types/site.d.ts +71 -0
- package/utils/camera-wall.test.ts +571 -0
- package/utils/camera-wall.ts +926 -0
- package/utils/nav-sections.test.ts +190 -0
- package/utils/nav-sections.ts +151 -0
- package/utils/status.test.ts +77 -0
- package/utils/status.ts +90 -0
- package/utils/theme.test.ts +534 -0
- package/utils/theme.ts +294 -0
- package/components/HidIdentityMapping.vue +0 -975
- package/middleware/member.ts +0 -4
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +0 -23
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
|
|
3
|
+
<!--
|
|
4
|
+
The design draws settings as a stack of bordered sections with one open
|
|
5
|
+
at a time. `multiple` is BEHAVIOUR - which sections a user may have open
|
|
6
|
+
together - so it is left exactly as it was and only the drawing changes.
|
|
7
|
+
-->
|
|
8
|
+
<v-expansion-panels multiple v-model="openPanels" class="screen-accordion">
|
|
4
9
|
<!-- SITE SETTINGS -->
|
|
5
|
-
<v-expansion-panel v-if="canManageSiteInformation"
|
|
10
|
+
<v-expansion-panel v-if="canManageSiteInformation">
|
|
6
11
|
<v-expansion-panel-title>
|
|
7
12
|
<v-icon class="mr-2">mdi-cog</v-icon>
|
|
8
13
|
Site Information
|
|
@@ -22,7 +27,7 @@
|
|
|
22
27
|
<v-img v-if="coverPhotoUrl" :src="coverPhotoUrl" aspect-ratio="16/9" cover rounded="lg"
|
|
23
28
|
class="w-100 border" />
|
|
24
29
|
<div v-else
|
|
25
|
-
class="w-100 d-flex align-center justify-center rounded-lg
|
|
30
|
+
class="w-100 d-flex align-center justify-center rounded-lg border border-dashed site-settings__placeholder"
|
|
26
31
|
style="aspect-ratio: 16/9">
|
|
27
32
|
<span class="text-caption text-medium-emphasis">No cover photo uploaded</span>
|
|
28
33
|
</div>
|
|
@@ -76,7 +81,7 @@
|
|
|
76
81
|
</v-expansion-panel>
|
|
77
82
|
|
|
78
83
|
<!-- ANPR CAMERA -->
|
|
79
|
-
<v-expansion-panel v-if="showAnprCamera && canManageANPRCamera"
|
|
84
|
+
<v-expansion-panel v-if="showAnprCamera && canManageANPRCamera">
|
|
80
85
|
<v-expansion-panel-title>
|
|
81
86
|
<v-icon class="mr-2">mdi-camera</v-icon>
|
|
82
87
|
ANPR Camera
|
|
@@ -118,7 +123,7 @@
|
|
|
118
123
|
</v-expansion-panel>
|
|
119
124
|
|
|
120
125
|
<!-- CCTV CAMERA -->
|
|
121
|
-
<v-expansion-panel v-if="showCctvCamera && canManageCCTVCamera"
|
|
126
|
+
<v-expansion-panel v-if="showCctvCamera && canManageCCTVCamera">
|
|
122
127
|
<v-expansion-panel-title>
|
|
123
128
|
<v-icon class="mr-2">mdi-cctv</v-icon>
|
|
124
129
|
CCTV Camera
|
|
@@ -129,7 +134,7 @@
|
|
|
129
134
|
</v-expansion-panel-text>
|
|
130
135
|
</v-expansion-panel>
|
|
131
136
|
|
|
132
|
-
<v-expansion-panel v-if="showDeliveryCompanies && canManageDeliveryCompanies"
|
|
137
|
+
<v-expansion-panel v-if="showDeliveryCompanies && canManageDeliveryCompanies">
|
|
133
138
|
<v-expansion-panel-title>
|
|
134
139
|
<v-icon class="mr-2">mdi-truck</v-icon>
|
|
135
140
|
Delivery Companies
|
|
@@ -140,7 +145,7 @@
|
|
|
140
145
|
</v-expansion-panel-text>
|
|
141
146
|
</v-expansion-panel>
|
|
142
147
|
|
|
143
|
-
<v-expansion-panel v-if="showResidentVisitors && canManageResidentVisitors"
|
|
148
|
+
<v-expansion-panel v-if="showResidentVisitors && canManageResidentVisitors">
|
|
144
149
|
<v-expansion-panel-title>
|
|
145
150
|
<v-icon class="mr-2">mdi-car</v-icon>
|
|
146
151
|
Resident Visitors
|
|
@@ -150,7 +155,7 @@
|
|
|
150
155
|
</v-expansion-panel-text>
|
|
151
156
|
</v-expansion-panel>
|
|
152
157
|
|
|
153
|
-
<v-expansion-panel
|
|
158
|
+
<v-expansion-panel>
|
|
154
159
|
<v-expansion-panel-title>
|
|
155
160
|
<v-icon class="mr-2">mdi-face-recognition</v-icon>
|
|
156
161
|
HID Face Reader
|
|
@@ -163,7 +168,7 @@
|
|
|
163
168
|
@refresh="refreshSiteData"
|
|
164
169
|
/>
|
|
165
170
|
<v-divider class="my-4" />
|
|
166
|
-
<HidQrCodeConfiguration :site="siteId" />
|
|
171
|
+
<HidQrCodeConfiguration :site="siteId" :org="orgId" />
|
|
167
172
|
</v-expansion-panel-text>
|
|
168
173
|
</v-expansion-panel>
|
|
169
174
|
|
|
@@ -266,7 +271,9 @@ const { data: siteData, refresh: refreshSiteData } = await useLazyAsyncData(
|
|
|
266
271
|
`site-${props.siteId}`,
|
|
267
272
|
() => getSiteById(props.siteId)
|
|
268
273
|
);
|
|
269
|
-
const orgId = computed(
|
|
274
|
+
const orgId = computed(
|
|
275
|
+
() => props.orgId || String((siteData.value as TSite | null)?.orgId ?? "")
|
|
276
|
+
);
|
|
270
277
|
|
|
271
278
|
watch(
|
|
272
279
|
siteData,
|
|
@@ -419,3 +426,11 @@ const saveANPRSettings = async () => {
|
|
|
419
426
|
}
|
|
420
427
|
};
|
|
421
428
|
</script>
|
|
429
|
+
|
|
430
|
+
<style scoped>
|
|
431
|
+
/* Was `bg-grey-lighten-4`: a literal light grey, so the empty cover-photo tile
|
|
432
|
+
stayed a white slab on the dark page. */
|
|
433
|
+
.site-settings__placeholder {
|
|
434
|
+
background: var(--thead);
|
|
435
|
+
}
|
|
436
|
+
</style>
|
|
@@ -1,33 +1,53 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<!-- The page had no heading of its own: the five service apps mount this
|
|
4
|
+
component bare on `[org]/[site]/settings`, so the name only ever
|
|
5
|
+
appeared in the navigation rail. "Settings" is that rail's own label. -->
|
|
6
|
+
<PageHeader title="Settings" />
|
|
7
|
+
|
|
8
|
+
<v-row no-gutters>
|
|
9
|
+
<!--
|
|
10
|
+
`multiple` and which panel opens first are BEHAVIOUR, so they are
|
|
11
|
+
left exactly as they were - `.screen-accordion` only changes the
|
|
12
|
+
drawing, the same way `SiteSettings` already does it.
|
|
13
|
+
-->
|
|
14
|
+
<v-expansion-panels multiple v-model="openPanels" class="screen-accordion">
|
|
15
|
+
|
|
16
|
+
<!-- Work Order Settings -->
|
|
17
|
+
<v-expansion-panel>
|
|
18
|
+
<v-expansion-panel-title>
|
|
19
|
+
<v-icon class="mr-2">mdi-format-list-numbered</v-icon>
|
|
20
|
+
Work Order Settings
|
|
21
|
+
</v-expansion-panel-title>
|
|
22
|
+
|
|
23
|
+
<v-expansion-panel-text>
|
|
24
|
+
<div class="wo-settings__fields">
|
|
25
|
+
<AppField v-model="prefix" label="Prefix"
|
|
26
|
+
:disabled="isLoading || !canManageSiteSettings" @input="handleInput" />
|
|
27
|
+
|
|
28
|
+
<!-- `return-object` keeps the emitted value a NUMBER, which is
|
|
29
|
+
what `createWorkOrderSettings` puts on the wire today. -->
|
|
30
|
+
<AppSelect v-model="noOfDigits" return-object
|
|
31
|
+
:items="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" label="No. of Digits"
|
|
32
|
+
:disabled="isLoading || !canManageSiteSettings" />
|
|
33
|
+
|
|
34
|
+
<AppField :model-value="orderNumberPreview" label="Preview" readonly />
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Still a `v-btn`: `AppButton` has no loading state, and the
|
|
38
|
+
spinner on save is feedback, not decoration. -->
|
|
39
|
+
<v-btn v-if="canManageSiteSettings" class="screen-btn-primary text-none mt-4"
|
|
40
|
+
variant="flat" :loading="isLoading" @click="save">
|
|
41
|
+
Save
|
|
42
|
+
</v-btn>
|
|
43
|
+
</v-expansion-panel-text>
|
|
44
|
+
</v-expansion-panel>
|
|
45
|
+
|
|
46
|
+
</v-expansion-panels>
|
|
47
|
+
|
|
48
|
+
<Snackbar v-model="toast.show" :text="toast.message" :color="toast.color" />
|
|
49
|
+
</v-row>
|
|
50
|
+
</div>
|
|
31
51
|
</template>
|
|
32
52
|
|
|
33
53
|
<script setup lang="ts">
|
|
@@ -173,4 +193,15 @@ function show(message: string, color: string) {
|
|
|
173
193
|
toast.message = message
|
|
174
194
|
toast.color = color
|
|
175
195
|
}
|
|
176
|
-
</script>
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
<style scoped>
|
|
199
|
+
/* The design stacks a settings form's fields on one gap, rather than leaning on
|
|
200
|
+
each Vuetify field's own 22px of hint space. */
|
|
201
|
+
.wo-settings__fields {
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-direction: column;
|
|
204
|
+
gap: 16px;
|
|
205
|
+
max-width: 420px;
|
|
206
|
+
}
|
|
207
|
+
</style>
|
|
@@ -10,11 +10,19 @@
|
|
|
10
10
|
Back
|
|
11
11
|
</span>
|
|
12
12
|
</v-col>
|
|
13
|
+
<!--
|
|
14
|
+
THE PAGE TITLE ROW. This wrapper is the whole scaffold for every
|
|
15
|
+
single-attribute form page in the estate (18 callers across 10 apps),
|
|
16
|
+
and its title was a `text-h5` span - Vuetify's 24px/400 Roboto, not
|
|
17
|
+
the design's 22px/800. `PageHeader` is placement only: same string,
|
|
18
|
+
same position, same Back link above it, no card added around content
|
|
19
|
+
that already brings its own.
|
|
20
|
+
-->
|
|
13
21
|
<v-col cols="12">
|
|
14
|
-
<
|
|
22
|
+
<PageHeader :title="props.title" />
|
|
15
23
|
</v-col>
|
|
16
24
|
|
|
17
|
-
<v-col cols="12"
|
|
25
|
+
<v-col cols="12">
|
|
18
26
|
<slot name="default"> </slot>
|
|
19
27
|
</v-col>
|
|
20
28
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE STATUS CHIP.
|
|
3
|
+
|
|
4
|
+
A pill in the tone the design assigns to the status WORD, with the 6px
|
|
5
|
+
leading dot. The word -> tone mapping is `utils/status.ts` and is shared, so
|
|
6
|
+
"Open" is the same green on a work order and on a Daily Occurrence Book, and
|
|
7
|
+
a status the design never named comes out neutral rather than guessed.
|
|
8
|
+
|
|
9
|
+
Colour only. The chip prints the label it is given; it does not decide, map,
|
|
10
|
+
translate or shorten a status.
|
|
11
|
+
-->
|
|
12
|
+
<template>
|
|
13
|
+
<span
|
|
14
|
+
class="status-chip"
|
|
15
|
+
:class="[toneClass, { 'status-chip--plain': !dot && !pill, 'status-chip--pill': pill }]"
|
|
16
|
+
>
|
|
17
|
+
<span v-if="dot" class="status-chip__dot" />
|
|
18
|
+
<!-- The slot is for the chips that already carried an icon next to their
|
|
19
|
+
word - a padlock on "Closed - View Only" - so adopting the design's
|
|
20
|
+
pill does not quietly drop it. -->
|
|
21
|
+
<slot>{{ label ?? status }}</slot>
|
|
22
|
+
</span>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import { statusToneClass } from "../utils/status";
|
|
27
|
+
|
|
28
|
+
const props = defineProps({
|
|
29
|
+
/** The status word - what decides the colour. */
|
|
30
|
+
status: { type: String, default: "" },
|
|
31
|
+
/** What to PRINT, when that differs from the word being matched on. */
|
|
32
|
+
label: { type: String, default: undefined },
|
|
33
|
+
/** The design's tag chips (pass IDs, counts) carry no leading dot. */
|
|
34
|
+
dot: { type: Boolean, default: true },
|
|
35
|
+
/**
|
|
36
|
+
* The handoff draws TWO dotless chips, and they are not the same shape: the
|
|
37
|
+
* 7px-radius 11px tag on a pass, and the 999px 11.5px pill a role name sits
|
|
38
|
+
* in on the Members screen. `pill` picks the second.
|
|
39
|
+
*/
|
|
40
|
+
pill: { type: Boolean, default: false },
|
|
41
|
+
/**
|
|
42
|
+
* For the chips that are not a status WORD at all - a countdown, a rating, a
|
|
43
|
+
* count - where the screen has already worked out the meaning and only needs
|
|
44
|
+
* the design's tone. Given, it wins over the word mapping.
|
|
45
|
+
*/
|
|
46
|
+
tone: {
|
|
47
|
+
type: String as PropType<"ok" | "warn" | "err" | "info" | "neutral" | "">,
|
|
48
|
+
default: "",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const toneClass = computed(() =>
|
|
53
|
+
props.tone ? `tone-${props.tone}` : statusToneClass(props.status)
|
|
54
|
+
);
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<style scoped>
|
|
58
|
+
.status-chip {
|
|
59
|
+
display: inline-flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
gap: 6px;
|
|
62
|
+
padding: 4px 10px;
|
|
63
|
+
border-radius: var(--r-pill);
|
|
64
|
+
font-family: var(--font-sans);
|
|
65
|
+
font-size: var(--fs-chip-lg);
|
|
66
|
+
font-weight: var(--fw-chip);
|
|
67
|
+
line-height: 1.4;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
/* `--tone-bg` is set alongside the colour by the `.tone-*` class - see
|
|
70
|
+
assets/css/tokens.css - so one class carries both halves of the pair the
|
|
71
|
+
contrast was measured on. */
|
|
72
|
+
background: var(--tone-bg);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.status-chip--plain {
|
|
76
|
+
border-radius: var(--r-tag);
|
|
77
|
+
font-size: var(--fs-chip);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* The role tag: `padding:3px 10px;border-radius:999px;font-size:11.5px`. */
|
|
81
|
+
.status-chip--pill {
|
|
82
|
+
padding: 3px 10px;
|
|
83
|
+
font-size: var(--fs-chip);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.status-chip__dot {
|
|
87
|
+
width: 6px;
|
|
88
|
+
height: 6px;
|
|
89
|
+
border-radius: var(--r-pill);
|
|
90
|
+
background: currentColor;
|
|
91
|
+
flex: 0 0 6px;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
package/components/StockCard.vue
CHANGED
|
@@ -5,15 +5,18 @@
|
|
|
5
5
|
color="page-background-color fill-height header-row"
|
|
6
6
|
>
|
|
7
7
|
<v-col cols="12" sm="12" md="11" lg="12" xl="12">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
<!-- The loose Back link above the card becomes the design's title row -
|
|
9
|
+
the page name on the left, its action right-aligned on the same line.
|
|
10
|
+
`title` was already declared here and never drawn. -->
|
|
11
|
+
<PageHeader :title="title">
|
|
12
|
+
<template #actions>
|
|
13
|
+
<AppButton variant="ghost" icon="mdi-arrow-left" @click="back">
|
|
12
14
|
Back
|
|
13
|
-
</
|
|
14
|
-
</
|
|
15
|
-
</
|
|
16
|
-
|
|
15
|
+
</AppButton>
|
|
16
|
+
</template>
|
|
17
|
+
</PageHeader>
|
|
18
|
+
|
|
19
|
+
<AppCard class="pa-5 position-relative">
|
|
17
20
|
<!-- Stock Information -->
|
|
18
21
|
<v-row class="pt-2">
|
|
19
22
|
<v-col cols="12">
|
|
@@ -104,7 +107,7 @@
|
|
|
104
107
|
</TableMain>
|
|
105
108
|
</v-col>
|
|
106
109
|
</v-row>
|
|
107
|
-
</
|
|
110
|
+
</AppCard>
|
|
108
111
|
</v-col>
|
|
109
112
|
</v-row>
|
|
110
113
|
</template>
|
|
@@ -181,9 +184,11 @@ watchEffect(() => {
|
|
|
181
184
|
</script>
|
|
182
185
|
|
|
183
186
|
<style scoped>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
/* The scoped `.text-fontgray { color: #7e7e7e }` that used to sit here is gone.
|
|
188
|
+
It out-specified the layer's tokenised rule in `assets/css/screens.css`, which
|
|
189
|
+
is why these four field labels were the estate's one measured 4.06:1 - the
|
|
190
|
+
same fixed grey in both themes. The surface under them is `AppCard`, which is
|
|
191
|
+
itself tokenised, so the ink can safely follow the theme. */
|
|
187
192
|
|
|
188
193
|
:deep() .v-table .v-table__wrapper > table > thead > tr > th:not(:last-child) {
|
|
189
194
|
border-right: thin solid rgba(var(--v-border-color), var(--v-border-opacity));
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
:to="props.route"
|
|
16
16
|
class="text-subtitle-2 font-weight-bold"
|
|
17
17
|
>
|
|
18
|
-
|
|
18
|
+
<!-- The design prints the organisation and site names in full.
|
|
19
|
+
Vuetify's list-item title truncates by default, which cut them
|
|
20
|
+
to "Property Managem…" / "Seventh Cond…" in the 252px rail. -->
|
|
21
|
+
<v-list-item-title class="switch-context__name">
|
|
19
22
|
{{ name || `Select ${prop.title}` }}
|
|
20
23
|
</v-list-item-title>
|
|
21
24
|
</v-list-item>
|
|
@@ -100,9 +103,38 @@ const name = computed(() => {
|
|
|
100
103
|
return prop.items.find((item) => item.value === selected.value)?.text ?? "";
|
|
101
104
|
});
|
|
102
105
|
|
|
106
|
+
/**
|
|
107
|
+
* THE BREADCRUMB'S SITE HALF.
|
|
108
|
+
*
|
|
109
|
+
* The top bar draws `Site name / Page`, but the site's NAME is only ever
|
|
110
|
+
* resolved here - each application loads its own site list and hands it to
|
|
111
|
+
* this selector, so the bar had nothing to print and showed the page alone.
|
|
112
|
+
* The label already on screen is mirrored into shared state for the bar to
|
|
113
|
+
* read. It is a label: no request is made, and what the selector selects is
|
|
114
|
+
* untouched.
|
|
115
|
+
*/
|
|
116
|
+
const shellSiteName = useState<string>("shell-context-site-name", () => "");
|
|
117
|
+
|
|
118
|
+
watchEffect(() => {
|
|
119
|
+
if (prop.title?.toLowerCase() === "site" && name.value) {
|
|
120
|
+
shellSiteName.value = name.value;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
103
124
|
const selectItem = (value: string) => {
|
|
104
125
|
selected.value = value;
|
|
105
126
|
menu.value = false;
|
|
106
127
|
emit("select", value);
|
|
107
128
|
};
|
|
108
129
|
</script>
|
|
130
|
+
|
|
131
|
+
<style scoped>
|
|
132
|
+
/* Wrap onto a second line instead of truncating - the design shows the full
|
|
133
|
+
organisation and site names. */
|
|
134
|
+
.switch-context__name {
|
|
135
|
+
white-space: normal;
|
|
136
|
+
overflow: visible;
|
|
137
|
+
overflow-wrap: anywhere;
|
|
138
|
+
line-height: 1.25;
|
|
139
|
+
}
|
|
140
|
+
</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,16 @@
|
|
|
68
61
|
:max-height="`calc(100vh - (${offset}px))`"
|
|
69
62
|
class="overflow-y-auto"
|
|
70
63
|
>
|
|
71
|
-
<v-
|
|
72
|
-
v-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class="py-10"
|
|
76
|
-
>
|
|
77
|
-
<v-col
|
|
78
|
-
cols="auto"
|
|
79
|
-
class="text-center text-medium-emphasis text-body-2"
|
|
80
|
-
>
|
|
81
|
-
{{ noDataText }}
|
|
82
|
-
</v-col>
|
|
83
|
-
</v-row>
|
|
64
|
+
<div v-if="groupedItems.length === 0" class="table-card__empty">
|
|
65
|
+
<v-icon icon="mdi-magnify-close" size="32" />
|
|
66
|
+
<span>{{ noDataText }}</span>
|
|
67
|
+
</div>
|
|
84
68
|
|
|
85
69
|
<template
|
|
86
70
|
v-for="(group, groupIndex) in groupedItems"
|
|
87
71
|
:key="`group-${groupIndex}`"
|
|
88
72
|
>
|
|
89
|
-
<v-sheet color="
|
|
73
|
+
<v-sheet color="transparent" class="table-card__group" border="b t">
|
|
90
74
|
<v-row no-gutters align="center" class="px-4 py-2">
|
|
91
75
|
<v-col cols="auto" class="d-flex align-center ga-2">
|
|
92
76
|
<span
|
|
@@ -94,39 +78,37 @@
|
|
|
94
78
|
>
|
|
95
79
|
Set {{ group.set }}
|
|
96
80
|
</span>
|
|
97
|
-
<
|
|
81
|
+
<StatusChip
|
|
98
82
|
v-if="group.isScheduleTask"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
variant="tonal"
|
|
102
|
-
prepend-icon="mdi-calendar-clock"
|
|
83
|
+
tone="info"
|
|
84
|
+
:dot="false"
|
|
103
85
|
class="text-none"
|
|
104
86
|
>
|
|
87
|
+
<v-icon size="12">mdi-calendar-clock</v-icon>
|
|
105
88
|
Schedule Task
|
|
106
|
-
</
|
|
89
|
+
</StatusChip>
|
|
107
90
|
<slot name="group-header-chips" :group="group">
|
|
108
|
-
<
|
|
91
|
+
<StatusChip
|
|
109
92
|
v-if="group.completedByName && isGroupComplete(group)"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
variant="tonal"
|
|
113
|
-
prepend-icon="mdi-check-circle-outline"
|
|
93
|
+
status="Completed"
|
|
94
|
+
:dot="false"
|
|
114
95
|
class="text-none"
|
|
115
96
|
>
|
|
97
|
+
<v-icon size="12">mdi-check-circle-outline</v-icon>
|
|
116
98
|
Completed · {{ group.completedByName }}
|
|
117
|
-
</
|
|
118
|
-
<
|
|
99
|
+
</StatusChip>
|
|
100
|
+
<StatusChip
|
|
119
101
|
v-else-if="
|
|
120
102
|
group.completedByName && isGroupInProgress(group)
|
|
121
103
|
"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
prepend-icon="mdi-progress-clock"
|
|
104
|
+
status="Ongoing"
|
|
105
|
+
tone="warn"
|
|
106
|
+
:dot="false"
|
|
126
107
|
class="text-none"
|
|
127
108
|
>
|
|
109
|
+
<v-icon size="12">mdi-progress-clock</v-icon>
|
|
128
110
|
Ongoing · {{ group.completedByName }}
|
|
129
|
-
</
|
|
111
|
+
</StatusChip>
|
|
130
112
|
</slot>
|
|
131
113
|
</v-col>
|
|
132
114
|
<v-spacer />
|
|
@@ -139,7 +121,8 @@
|
|
|
139
121
|
<v-sheet
|
|
140
122
|
v-for="item in group.items"
|
|
141
123
|
:key="item[itemValue]"
|
|
142
|
-
color="
|
|
124
|
+
color="transparent"
|
|
125
|
+
class="table-card__row"
|
|
143
126
|
border="b"
|
|
144
127
|
>
|
|
145
128
|
<v-row no-gutters align="center" class="px-4 py-2">
|
package/components/TableList.vue
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
rounded="lg"
|
|
7
7
|
:loading="loading"
|
|
8
8
|
>
|
|
9
|
-
<v-toolbar density="compact"
|
|
9
|
+
<v-toolbar density="compact" class="px-3 border-b-sm" color="transparent">
|
|
10
10
|
<template #prepend>
|
|
11
11
|
<v-checkbox v-model="selectAll" hide-details class="mx-1"></v-checkbox>
|
|
12
12
|
|