@7365admin1/layer-common 3.2.1-staging.75 → 3.2.2-staging.100
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/notification-preferences.md +17 -0
- package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
- package/CHANGELOG.md +6 -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 +46 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +203 -0
- 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/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 +285 -207
- package/components/DocumentManagement.vue +129 -93
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +4 -6
- package/components/EquipmentManagementMain.vue +9 -7
- package/components/FeedbackMain.vue +35 -49
- package/components/FormDialog.vue +60 -21
- package/components/HidIntercomManagement.vue +40 -24
- package/components/HidQrCodeConfiguration.vue +24 -2
- package/components/HidUserEnrollment.vue +68 -31
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +289 -37
- package/components/Layout/NavigationDrawer.vue +299 -30
- package/components/ManageChecklistMain.vue +17 -17
- package/components/MemberMain.vue +79 -60
- package/components/MyAttendanceMain.vue +3 -5
- package/components/Nfc/NFCPatrolRouteMain.vue +64 -96
- package/components/NotificationSettings.vue +361 -0
- package/components/OnlineFormsConfiguration.vue +1 -1
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +104 -45
- 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/ServiceProviderMain.vue +113 -168
- package/components/SiteSettings.vue +21 -8
- package/components/SpecificAttr.vue +10 -2
- package/components/StatusChip.vue +93 -0
- 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 +100 -7
- package/components/VisitorManagement.vue +219 -68
- package/components/WorkOrder/Main.vue +42 -51
- package/composables/useHidAmico.ts +26 -2
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOrg.ts +1 -1
- package/composables/useRole.ts +22 -0
- package/composables/useSipWebPhone.ts +28 -3
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/nuxt.config.ts +31 -0
- package/package.json +3 -2
- package/pages/notification-settings.vue +19 -0
- package/plugins/vuetify.ts +32 -1
- package/types/org.d.ts +2 -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 +325 -7
- package/utils/theme.ts +268 -137
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
|
|
4
|
-
<v-row no-gutters>
|
|
5
|
-
<v-btn class="text-none" rounded="pill" variant="tonal" @click="setBuilding()" size="large"
|
|
6
|
-
v-if="canCreate && canCreateBuilding">
|
|
7
|
-
Add Block
|
|
8
|
-
</v-btn>
|
|
9
|
-
</v-row>
|
|
10
|
-
</v-col>
|
|
3
|
+
<!-- Title row; nav calls this screen "Blocks". The Add pill moves onto it. -->
|
|
11
4
|
<v-col cols="12">
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
dense clearable class="mx-2" style="width: 200px;" />
|
|
20
|
-
</template>
|
|
21
|
-
|
|
22
|
-
<template #append>
|
|
23
|
-
<v-row no-gutters justify="end" align="center">
|
|
24
|
-
<span class="mr-2 text-caption text-fontgray">
|
|
25
|
-
{{ pageRange }}
|
|
26
|
-
</span>
|
|
27
|
-
<local-pagination v-model="page" :length="pages" @update:value="getBuildings()" />
|
|
28
|
-
</v-row>
|
|
29
|
-
</template>
|
|
30
|
-
</v-toolbar>
|
|
31
|
-
|
|
32
|
-
<v-data-table :headers="headers" :items="items" item-value="_id" items-per-page="20" fixed-header
|
|
33
|
-
hide-default-footer @click:row="tableRowClickHandler" style="max-height: calc(100vh - (200px))">
|
|
34
|
-
<template #item.createdAt="{ value }">
|
|
35
|
-
{{ new Date(value).toLocaleDateString() }}
|
|
36
|
-
</template>
|
|
37
|
-
<template #item.levels="{ item }">
|
|
38
|
-
{{ item.levels.length }}
|
|
39
|
-
</template>
|
|
40
|
-
</v-data-table>
|
|
41
|
-
</v-card>
|
|
5
|
+
<PageHeader title="Blocks">
|
|
6
|
+
<template #actions>
|
|
7
|
+
<AppButton v-if="canCreate && canCreateBuilding" icon="mdi-plus" @click="setBuilding()">
|
|
8
|
+
Add Block
|
|
9
|
+
</AppButton>
|
|
10
|
+
</template>
|
|
11
|
+
</PageHeader>
|
|
42
12
|
</v-col>
|
|
43
13
|
|
|
14
|
+
<!--
|
|
15
|
+
Was `v-card` + a `grey-lighten-4` `v-toolbar` + a bare `v-data-table`.
|
|
16
|
+
`TableMain` draws the same card, the same refresh/search/page-range/pager
|
|
17
|
+
row and the same table - with the design's uppercase header row.
|
|
18
|
+
-->
|
|
19
|
+
<TableMain :headers="headers" :items="items" :loading="loading" :page="page" :pages="pages"
|
|
20
|
+
:pageRange="pageRange" show-header @refresh="getBuildings()"
|
|
21
|
+
@update:page="page = $event; getBuildings()" @row-click="(data: any) => tableRowClickHandler(null, data)">
|
|
22
|
+
<template #prepend-additional>
|
|
23
|
+
<AppField v-model="searchInput" search compact placeholder="Search" klass="app-toolbar__search" />
|
|
24
|
+
</template>
|
|
25
|
+
<template #item.createdAt="{ value }">
|
|
26
|
+
{{ new Date(value).toLocaleDateString() }}
|
|
27
|
+
</template>
|
|
28
|
+
<template #item.levels="{ item }">
|
|
29
|
+
{{ item.levels.length }}
|
|
30
|
+
</template>
|
|
31
|
+
</TableMain>
|
|
32
|
+
|
|
44
33
|
<!-- Create Dialog -->
|
|
45
34
|
<v-dialog v-model="createDialog" width="450" persistent>
|
|
46
35
|
<BuildingForm :site="site" mode="add" :blocks="items" @cancel="createDialog = false" @success="successCreate()"
|
|
@@ -1,50 +1,34 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
|
|
4
|
-
<v-row no-gutters>
|
|
5
|
-
<v-btn class="text-none" rounded="pill" variant="tonal" @click="setBuilding()" size="large"
|
|
6
|
-
v-if="canCreate && canCreateUnit">
|
|
7
|
-
Add Unit
|
|
8
|
-
</v-btn>
|
|
9
|
-
<v-btn class="text-none ml-2" rounded="pill" variant="tonal" @click="openBulkUploadFilePicker()" size="large"
|
|
10
|
-
:loading="bulkUploadLoading" v-if="canCreate && canCreateUnit">
|
|
11
|
-
<v-icon start>mdi-upload</v-icon>
|
|
12
|
-
Bulk Upload
|
|
13
|
-
</v-btn>
|
|
14
|
-
<input ref="bulkUploadFileInput" type="file" accept=".xlsx,.xls,.csv" class="d-none"
|
|
15
|
-
@change="handleBulkUploadFileChange" />
|
|
16
|
-
</v-row>
|
|
17
|
-
</v-col>
|
|
3
|
+
<!-- Title row; nav calls this screen "Units". Both pills move onto it. -->
|
|
18
4
|
<v-col cols="12">
|
|
19
|
-
<
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{{ pageRange }}
|
|
33
|
-
</span>
|
|
34
|
-
<local-pagination v-model="page" :length="pages" @update:value="getBuildingUnit()" />
|
|
35
|
-
</v-row>
|
|
36
|
-
</template>
|
|
37
|
-
</v-toolbar>
|
|
38
|
-
|
|
39
|
-
<v-data-table :headers="props.headers" :items="items" item-value="_id" items-per-page="20" fixed-header
|
|
40
|
-
hide-default-footer @click:row="tableRowClickHandler" style="max-height: calc(100vh - (200px))">
|
|
41
|
-
<template #item.block="{ value, item }">
|
|
42
|
-
{{ value ? `Block ${value}` : "" }}
|
|
43
|
-
</template>
|
|
44
|
-
</v-data-table>
|
|
45
|
-
</v-card>
|
|
5
|
+
<PageHeader title="Units">
|
|
6
|
+
<template #actions>
|
|
7
|
+
<AppButton v-if="canCreate && canCreateUnit" variant="ghost" icon="mdi-upload"
|
|
8
|
+
:disabled="bulkUploadLoading" @click="openBulkUploadFilePicker()">
|
|
9
|
+
Bulk Upload
|
|
10
|
+
</AppButton>
|
|
11
|
+
<AppButton v-if="canCreate && canCreateUnit" icon="mdi-plus" @click="setBuilding()">
|
|
12
|
+
Add Unit
|
|
13
|
+
</AppButton>
|
|
14
|
+
</template>
|
|
15
|
+
</PageHeader>
|
|
16
|
+
<input ref="bulkUploadFileInput" type="file" accept=".xlsx,.xls,.csv" class="d-none"
|
|
17
|
+
@change="handleBulkUploadFileChange" />
|
|
46
18
|
</v-col>
|
|
47
19
|
|
|
20
|
+
<!-- Was `v-card` + a `grey-lighten-4` `v-toolbar` + a bare `v-data-table`. -->
|
|
21
|
+
<TableMain :headers="props.headers" :items="items" :loading="loading" :page="page" :pages="pages"
|
|
22
|
+
:pageRange="pageRange" show-header @refresh="getBuildingUnit()"
|
|
23
|
+
@update:page="page = $event; getBuildingUnit()" @row-click="(data: any) => tableRowClickHandler(null, data)">
|
|
24
|
+
<template #prepend-additional>
|
|
25
|
+
<AppField v-model="searchInput" search compact placeholder="Search" klass="app-toolbar__search" />
|
|
26
|
+
</template>
|
|
27
|
+
<template #item.block="{ value, item }">
|
|
28
|
+
{{ value ? `Block ${value}` : "" }}
|
|
29
|
+
</template>
|
|
30
|
+
</TableMain>
|
|
31
|
+
|
|
48
32
|
<!-- Create Dialog -->
|
|
49
33
|
<v-dialog v-model="createDialog" :width="smAndUp ? '50svw' : '100%'" persistent>
|
|
50
34
|
<BuildingUnitFormAdd :site="site" @cancel="createDialog = false" @success="successCreate()"
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
+
<!-- Title left, primary action right - the design's opening row. -->
|
|
4
|
+
<v-col cols="12">
|
|
5
|
+
<PageHeader title="Bulletin Board">
|
|
6
|
+
<template #actions>
|
|
7
|
+
<AppButton v-if="canCreateBulletinBoard" icon="mdi-plus" @click="handleCreateEvent">
|
|
8
|
+
Add Announcement
|
|
9
|
+
</AppButton>
|
|
10
|
+
</template>
|
|
11
|
+
</PageHeader>
|
|
12
|
+
</v-col>
|
|
13
|
+
|
|
3
14
|
<TableMain :headers="headers" :items="paginatePlaceholderItem" v-model:search="searchInput"
|
|
4
15
|
:loading="getAnnouncementPending" :page="page" :pages="pages" :pageRange="pageRange"
|
|
5
|
-
@refresh="getAnnouncementsRefresh" :show-header="APP_CONSTANTS.RESIDENT === props.recipients" @update:page="handleUpdatePage" @row-click="handleRowClick"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
@refresh="getAnnouncementsRefresh" :show-header="APP_CONSTANTS.RESIDENT === props.recipients" @update:page="handleUpdatePage" @row-click="handleRowClick">
|
|
17
|
+
<!--
|
|
18
|
+
The tabs move ONTO the toolbar row (the design draws them there)
|
|
19
|
+
instead of a stacked strip above the table. Same options, same
|
|
20
|
+
order, same `toRoute` - which already sets the status itself.
|
|
21
|
+
-->
|
|
22
|
+
<template #tabs>
|
|
23
|
+
<button v-for="tab in tabOptions" :key="tab.status" type="button" role="tab" class="app-tab"
|
|
24
|
+
:class="{ 'app-tab--active': status === tab.status }" :aria-selected="status === tab.status"
|
|
25
|
+
@click="status = tab.status as any; toRoute(tab.status)">
|
|
26
|
+
{{ tab.name }}
|
|
27
|
+
</button>
|
|
15
28
|
</template>
|
|
16
29
|
|
|
17
30
|
<template v-slot:item.createdAt="{ item }">
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE IN-CARD TOP ROW.
|
|
3
|
+
|
|
4
|
+
The design does NOT put a screen's tabs above its card - they are the card's
|
|
5
|
+
own first row, with the pagination on the opposite end of that same line, and
|
|
6
|
+
the tab underline meets the card's rule. Today those tabs sit in a
|
|
7
|
+
`v-toolbar`'s extension slot, which stacks them BELOW a separate bar and
|
|
8
|
+
costs a whole row of height.
|
|
9
|
+
|
|
10
|
+
Two shapes, both from the handoff: with tabs (`0 20px`, the tabs supply the
|
|
11
|
+
vertical padding) and without (`10px 14px`, a refresh button on the left).
|
|
12
|
+
This component only places things - the tabs, the count and the pager are
|
|
13
|
+
passed in and keep their own behaviour.
|
|
14
|
+
-->
|
|
15
|
+
<template>
|
|
16
|
+
<div class="app-toolbar" :class="{ 'app-toolbar--plain': !$slots.tabs }">
|
|
17
|
+
<div v-if="$slots.tabs" class="app-tabs" role="tablist">
|
|
18
|
+
<slot name="tabs" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<slot name="left" />
|
|
22
|
+
|
|
23
|
+
<div class="app-toolbar__spacer" />
|
|
24
|
+
|
|
25
|
+
<div class="app-toolbar__right">
|
|
26
|
+
<span v-if="count" class="app-toolbar__count">{{ count }}</span>
|
|
27
|
+
<slot name="right" />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script setup lang="ts">
|
|
33
|
+
defineProps({
|
|
34
|
+
/** The design's `1-1 of 1` range, printed as the screen already computes it. */
|
|
35
|
+
count: { type: String, default: "" },
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
@@ -17,11 +17,7 @@
|
|
|
17
17
|
>
|
|
18
18
|
<template #extension>
|
|
19
19
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
20
|
-
<
|
|
21
|
-
class="w-100 px-3 d-flex align-center ga-5 py-2"
|
|
22
|
-
flat
|
|
23
|
-
:height="60"
|
|
24
|
-
>
|
|
20
|
+
<div class="filter-bar">
|
|
25
21
|
<InputDateTimePicker
|
|
26
22
|
v-model:utc="startDate"
|
|
27
23
|
density="compact"
|
|
@@ -47,7 +43,7 @@
|
|
|
47
43
|
item-value="value"
|
|
48
44
|
label="Status"
|
|
49
45
|
/>
|
|
50
|
-
</
|
|
46
|
+
</div>
|
|
51
47
|
</v-row>
|
|
52
48
|
</template>
|
|
53
49
|
|
|
@@ -60,25 +56,23 @@
|
|
|
60
56
|
: ""
|
|
61
57
|
}}
|
|
62
58
|
</template>
|
|
59
|
+
<!--
|
|
60
|
+
A countdown, not a status word, so it passes the tone the screen has
|
|
61
|
+
already worked out from the time left rather than a word to map.
|
|
62
|
+
-->
|
|
63
63
|
<template #item.closeIn="{ item }">
|
|
64
|
-
<
|
|
64
|
+
<StatusChip
|
|
65
65
|
class="text-capitalize"
|
|
66
|
-
|
|
67
|
-
:
|
|
68
|
-
|
|
69
|
-
>
|
|
70
|
-
{{ remainingTime[item._id] || "00h 00m" }}
|
|
71
|
-
</v-chip>
|
|
66
|
+
:tone="getCloseInTone(item._id)"
|
|
67
|
+
:label="remainingTime[item._id] || '00h 00m'"
|
|
68
|
+
/>
|
|
72
69
|
</template>
|
|
73
70
|
<template #item.status="{ value }">
|
|
74
|
-
<
|
|
71
|
+
<StatusChip
|
|
75
72
|
class="text-capitalize"
|
|
76
|
-
:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
>
|
|
80
|
-
{{ value || "No Status" }}
|
|
81
|
-
</v-chip>
|
|
73
|
+
:status="value"
|
|
74
|
+
:label="value || 'No Status'"
|
|
75
|
+
/>
|
|
82
76
|
</template>
|
|
83
77
|
<template #item.completedAt="{ value }">
|
|
84
78
|
{{
|
|
@@ -159,33 +153,22 @@ const downloadingId = ref<string | null>(null);
|
|
|
159
153
|
const { getCleaningSchedules, downloadChecklistPdf } = useCleaningSchedules();
|
|
160
154
|
const { formatDate } = useUtils();
|
|
161
155
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
case "accepted":
|
|
174
|
-
return "success";
|
|
175
|
-
case "closed":
|
|
176
|
-
case "rejected":
|
|
177
|
-
return "error";
|
|
178
|
-
default:
|
|
179
|
-
return "secondary";
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const getCloseInColor = (id: string): string => {
|
|
156
|
+
/**
|
|
157
|
+
* The status word now picks its own colour, from the one shared mapping in
|
|
158
|
+
* `utils/status.ts` - which carries every word this screen used to switch on,
|
|
159
|
+
* with the tone it used to give it. The screen no longer keeps a private
|
|
160
|
+
* palette that only it agrees with.
|
|
161
|
+
*
|
|
162
|
+
* The countdown is not a status word, so the thresholds stay here; only the
|
|
163
|
+
* NAMES change, from Vuetify colours to the design's tones. Same three bands,
|
|
164
|
+
* same boundaries.
|
|
165
|
+
*/
|
|
166
|
+
const getCloseInTone = (id: string): "info" | "warn" | "err" => {
|
|
184
167
|
const secs = remainingSeconds.value[id];
|
|
185
|
-
if (secs === undefined) return "
|
|
186
|
-
if (secs <= 0) return "
|
|
187
|
-
if (secs < 3 * 3600) return "
|
|
188
|
-
return "
|
|
168
|
+
if (secs === undefined) return "info";
|
|
169
|
+
if (secs <= 0) return "err";
|
|
170
|
+
if (secs < 3 * 3600) return "warn";
|
|
171
|
+
return "info";
|
|
189
172
|
};
|
|
190
173
|
|
|
191
174
|
const canDownloadSchedule = computed(() => props.canDownloadSchedule);
|