@7365admin1/layer-common 3.2.1 → 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/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 +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/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/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 +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,49 +1,46 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<!--
|
|
4
|
+
THE DESIGN'S PAGE SCAFFOLD (same shape as Service Providers).
|
|
5
|
+
|
|
6
|
+
`Create role` moves from a bare row above the card onto the title line,
|
|
7
|
+
on the right, where the design puts a screen's primary action. It is the
|
|
8
|
+
same button under the same `canCreateRole` permission opening the same
|
|
9
|
+
dialog. The refresh, the range, the pager and the table are unchanged.
|
|
10
|
+
-->
|
|
11
|
+
<PageHeader title="Roles and Permissions">
|
|
12
|
+
<template #actions>
|
|
13
|
+
<AppButton v-if="canCreateRole" @click="createDialog = true">
|
|
13
14
|
Create role
|
|
14
|
-
</
|
|
15
|
-
</
|
|
16
|
-
</
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</template>
|
|
44
|
-
</v-toolbar>
|
|
45
|
-
|
|
46
|
-
<v-data-table
|
|
15
|
+
</AppButton>
|
|
16
|
+
</template>
|
|
17
|
+
</PageHeader>
|
|
18
|
+
|
|
19
|
+
<AppCard class="table-card">
|
|
20
|
+
<!-- `v-card`'s `:loading` bar, drawn explicitly: a loading screen must
|
|
21
|
+
still say so now that the card is not a `v-card`. -->
|
|
22
|
+
<v-progress-linear v-if="loading" indeterminate height="2" />
|
|
23
|
+
|
|
24
|
+
<CardToolbar :count="pageRange">
|
|
25
|
+
<template #left>
|
|
26
|
+
<AppButton
|
|
27
|
+
variant="icon"
|
|
28
|
+
icon="mdi-refresh"
|
|
29
|
+
aria-label="Refresh"
|
|
30
|
+
@click="getRoles()"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<template #right>
|
|
35
|
+
<local-pagination
|
|
36
|
+
v-model="page"
|
|
37
|
+
:length="pages"
|
|
38
|
+
@update:value="getRoles()"
|
|
39
|
+
/>
|
|
40
|
+
</template>
|
|
41
|
+
</CardToolbar>
|
|
42
|
+
|
|
43
|
+
<v-data-table
|
|
47
44
|
:headers="props.headers"
|
|
48
45
|
:items="items"
|
|
49
46
|
item-value="_id"
|
|
@@ -58,7 +55,11 @@
|
|
|
58
55
|
<span class="text-caption font-weight-bold text-capitalize">
|
|
59
56
|
permissions
|
|
60
57
|
</span>
|
|
61
|
-
<
|
|
58
|
+
<StatusChip
|
|
59
|
+
:dot="false"
|
|
60
|
+
tone="info"
|
|
61
|
+
:label="String(getPermissionCount(value))"
|
|
62
|
+
/>
|
|
62
63
|
</template>
|
|
63
64
|
|
|
64
65
|
<template #item.action-table="{ item }" v-if="canDeleteRole">
|
|
@@ -73,9 +74,8 @@
|
|
|
73
74
|
</v-list>
|
|
74
75
|
</v-menu>
|
|
75
76
|
</template>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
</v-col>
|
|
77
|
+
</v-data-table>
|
|
78
|
+
</AppCard>
|
|
79
79
|
|
|
80
80
|
<!-- dialogs -->
|
|
81
81
|
<v-dialog v-model="createDialog" width="500" persistent>
|
|
@@ -105,45 +105,93 @@
|
|
|
105
105
|
/>
|
|
106
106
|
</v-dialog>
|
|
107
107
|
|
|
108
|
-
<
|
|
109
|
-
v-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
<template #description>
|
|
118
|
-
<p class="text-subtitle-2">
|
|
119
|
-
Are you sure you want to delete this role? This action cannot be
|
|
120
|
-
undone.
|
|
121
|
-
</p>
|
|
122
|
-
</template>
|
|
108
|
+
<v-dialog v-model="confirmDialog" max-width="450">
|
|
109
|
+
<v-card>
|
|
110
|
+
<v-toolbar>
|
|
111
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
112
|
+
<span class="font-weight-bold text-h6 text-capitalize">
|
|
113
|
+
Delete Role
|
|
114
|
+
</span>
|
|
115
|
+
</v-row>
|
|
116
|
+
</v-toolbar>
|
|
123
117
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
118
|
+
<v-card-text>
|
|
119
|
+
<span v-if="!hasAssignedMembers" class="text-subtitle-2">
|
|
120
|
+
Are you sure you want to delete this role? This action cannot be
|
|
121
|
+
undone.
|
|
122
|
+
</span>
|
|
123
|
+
<template v-else>
|
|
124
|
+
<p class="text-subtitle-2 mb-4">
|
|
125
|
+
There are currently
|
|
126
|
+
<span class="text-error font-weight-bold">
|
|
127
|
+
{{ deletionPreview.members.length }}
|
|
128
|
+
{{ deletionPreview.members.length === 1 ? "user" : "users" }}
|
|
129
|
+
</span>
|
|
130
|
+
assigned to this role. Reassign each user before deleting it so
|
|
131
|
+
they do not lose access.
|
|
132
|
+
</p>
|
|
133
|
+
|
|
134
|
+
<v-row
|
|
135
|
+
v-for="member in deletionPreview.members"
|
|
136
|
+
:key="member._id"
|
|
137
|
+
no-gutters
|
|
138
|
+
class="mb-3 pa-3 border rounded-lg"
|
|
139
|
+
align="center"
|
|
140
|
+
>
|
|
141
|
+
<v-col cols="12" sm="5" class="text-body-2 mb-2 mb-sm-0">
|
|
142
|
+
{{ member.name || member.user }}
|
|
143
|
+
</v-col>
|
|
144
|
+
<v-col cols="12" sm="7">
|
|
145
|
+
<v-select
|
|
146
|
+
v-model="memberReassignments[member._id]"
|
|
147
|
+
:items="deletionPreview.replacementRoles"
|
|
148
|
+
item-title="name"
|
|
149
|
+
item-value="_id"
|
|
150
|
+
label="Reassign role"
|
|
151
|
+
density="comfortable"
|
|
152
|
+
hide-details
|
|
153
|
+
/>
|
|
154
|
+
</v-col>
|
|
155
|
+
</v-row>
|
|
156
|
+
</template>
|
|
157
|
+
</v-card-text>
|
|
158
|
+
|
|
159
|
+
<v-toolbar class="pa-0" density="compact">
|
|
160
|
+
<v-row no-gutters>
|
|
161
|
+
<v-col cols="6" class="pa-0">
|
|
162
|
+
<v-btn
|
|
163
|
+
block
|
|
164
|
+
variant="text"
|
|
165
|
+
class="text-none"
|
|
166
|
+
size="large"
|
|
167
|
+
height="48"
|
|
168
|
+
@click="closeDeleteDialog"
|
|
169
|
+
:disabled="deleteLoading"
|
|
170
|
+
>
|
|
171
|
+
Cancel
|
|
172
|
+
</v-btn>
|
|
173
|
+
</v-col>
|
|
174
|
+
<v-col cols="6" class="pa-0">
|
|
175
|
+
<v-btn
|
|
176
|
+
block
|
|
177
|
+
variant="flat"
|
|
178
|
+
color="black"
|
|
179
|
+
class="text-none font-weight-bold rounded-0"
|
|
180
|
+
height="48"
|
|
181
|
+
@click="handleDeleteRole"
|
|
182
|
+
:loading="deleteLoading"
|
|
183
|
+
:disabled="hasAssignedMembers && !canConfirmDeletion"
|
|
184
|
+
>
|
|
185
|
+
{{ hasAssignedMembers ? "Confirm" : "Delete" }}
|
|
186
|
+
</v-btn>
|
|
187
|
+
</v-col>
|
|
188
|
+
</v-row>
|
|
189
|
+
</v-toolbar>
|
|
190
|
+
</v-card>
|
|
191
|
+
</v-dialog>
|
|
144
192
|
|
|
145
193
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
146
|
-
</
|
|
194
|
+
</div>
|
|
147
195
|
</template>
|
|
148
196
|
|
|
149
197
|
<script setup lang="ts">
|
|
@@ -367,10 +415,49 @@ async function refreshRolesAfterUpdate(savedPermissions: string[]) {
|
|
|
367
415
|
const confirmDialog = ref(false);
|
|
368
416
|
const selectedRoleId = ref<string | null>(null);
|
|
369
417
|
const deleteLoading = ref(false);
|
|
418
|
+
const deletionPreview = ref({
|
|
419
|
+
members: [] as Array<Record<string, any>>,
|
|
420
|
+
replacementRoles: [] as Array<Record<string, any>>,
|
|
421
|
+
});
|
|
422
|
+
const memberReassignments = ref<Record<string, string>>({});
|
|
423
|
+
|
|
424
|
+
const hasAssignedMembers = computed(
|
|
425
|
+
() => deletionPreview.value.members.length > 0
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
const canConfirmDeletion = computed(() =>
|
|
429
|
+
deletionPreview.value.members.every(
|
|
430
|
+
(member) => Boolean(memberReassignments.value[member._id])
|
|
431
|
+
)
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
const { getDeletionPreview, deleteWithReassignments } = useRole();
|
|
370
435
|
|
|
371
|
-
function openDeleteDialog(id: string) {
|
|
436
|
+
async function openDeleteDialog(id: string) {
|
|
372
437
|
selectedRoleId.value = id;
|
|
373
|
-
|
|
438
|
+
deletionPreview.value = { members: [], replacementRoles: [] };
|
|
439
|
+
memberReassignments.value = {};
|
|
440
|
+
deleteLoading.value = true;
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
const res = await getDeletionPreview(id);
|
|
444
|
+
deletionPreview.value = res.data;
|
|
445
|
+
confirmDialog.value = true;
|
|
446
|
+
} catch (error: any) {
|
|
447
|
+
const errorMessage =
|
|
448
|
+
error?.response?._data?.message || "Failed to load role assignments.";
|
|
449
|
+
showMessage(errorMessage, "error");
|
|
450
|
+
selectedRoleId.value = null;
|
|
451
|
+
} finally {
|
|
452
|
+
deleteLoading.value = false;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function closeDeleteDialog() {
|
|
457
|
+
confirmDialog.value = false;
|
|
458
|
+
selectedRoleId.value = null;
|
|
459
|
+
deletionPreview.value = { members: [], replacementRoles: [] };
|
|
460
|
+
memberReassignments.value = {};
|
|
374
461
|
}
|
|
375
462
|
|
|
376
463
|
function showMessage(msg: string, color: string) {
|
|
@@ -383,17 +470,23 @@ async function handleDeleteRole() {
|
|
|
383
470
|
if (!selectedRoleId.value) return;
|
|
384
471
|
deleteLoading.value = true;
|
|
385
472
|
try {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
473
|
+
const reassignments = deletionPreview.value.members.map((member) => ({
|
|
474
|
+
memberId: member._id,
|
|
475
|
+
roleId: memberReassignments.value[member._id],
|
|
476
|
+
}));
|
|
477
|
+
const res = hasAssignedMembers.value
|
|
478
|
+
? await deleteWithReassignments(selectedRoleId.value, reassignments)
|
|
479
|
+
: await deleteRole(selectedRoleId.value);
|
|
480
|
+
|
|
481
|
+
closeDeleteDialog();
|
|
389
482
|
showMessage(res.message, "success");
|
|
390
483
|
getRoles();
|
|
391
484
|
} catch (error: any) {
|
|
392
|
-
const errorMessage =
|
|
485
|
+
const errorMessage =
|
|
486
|
+
error?.response?._data?.message || "Failed to delete role.";
|
|
393
487
|
showMessage(errorMessage, "error");
|
|
394
488
|
} finally {
|
|
395
489
|
deleteLoading.value = false;
|
|
396
|
-
selectedRoleId.value = null;
|
|
397
490
|
}
|
|
398
491
|
}
|
|
399
492
|
</script>
|
|
@@ -60,7 +60,9 @@ const props = defineProps({
|
|
|
60
60
|
|
|
61
61
|
const { standardFormatDate } = useUtils();
|
|
62
62
|
|
|
63
|
-
const { validateVisitorQrCode } = useVisitor();
|
|
63
|
+
const { validateVisitorQrCode, getVisitors } = useVisitor();
|
|
64
|
+
|
|
65
|
+
const { getPassKeysByPageSearch } = usePassKey();
|
|
64
66
|
|
|
65
67
|
const emits = defineEmits([
|
|
66
68
|
"closeDialog",
|
|
@@ -97,8 +99,6 @@ const handleScanVisitorQRCode = async () => {
|
|
|
97
99
|
showNotFoundMessage.value = false;
|
|
98
100
|
// Automatically navigate to the scanned URL if it's a valid link
|
|
99
101
|
if (isValidUrl(qrCodeMessage)) {
|
|
100
|
-
console.log("QR Code value", qrCodeMessage);
|
|
101
|
-
console.log("Last _id", qrCodeMessage.split("/").pop());
|
|
102
102
|
// get the last _id in the url
|
|
103
103
|
const id = qrCodeMessage.split("/").pop();
|
|
104
104
|
// check if it's a valid mongodb _id
|
|
@@ -108,7 +108,6 @@ const handleScanVisitorQRCode = async () => {
|
|
|
108
108
|
props.site as string,
|
|
109
109
|
id as string
|
|
110
110
|
);
|
|
111
|
-
console.log("validateVisitorQrCode", response);
|
|
112
111
|
if (response.errorMessage) {
|
|
113
112
|
showNotFoundMessage.value = true;
|
|
114
113
|
errorMessage.value =
|
|
@@ -122,9 +121,38 @@ const handleScanVisitorQRCode = async () => {
|
|
|
122
121
|
emits("openVisitorDataFromScannedQrCodeDialog", response);
|
|
123
122
|
}
|
|
124
123
|
} else {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
const result: any = await getPassKeysByPageSearch({
|
|
125
|
+
sites: [props.site as string],
|
|
126
|
+
search: qrCodeMessage,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (result.items?.length < 1) {
|
|
130
|
+
showMessage(`Pass or Key ${qrCodeMessage} not found.`, "error");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const data: any = await getVisitors({
|
|
135
|
+
site: props.site as string,
|
|
136
|
+
passOrKey: {
|
|
137
|
+
keyId: result.items[0]._id,
|
|
138
|
+
type: result.items[0].passType == "pass-key" ? "key" : "pass",
|
|
139
|
+
},
|
|
140
|
+
checkedOut: false,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (data.items?.length < 1) {
|
|
144
|
+
showMessage(
|
|
145
|
+
`There are no unchecked-out visitor transactions for the scanned ${
|
|
146
|
+
result.items[0].passType == "pass-key" ? "Key" : "Pass"
|
|
147
|
+
} ${qrCodeMessage}.`,
|
|
148
|
+
"error"
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
closeDialog();
|
|
155
|
+
emits("openVisitorDataFromScannedQrCodeDialog", data.items[0]);
|
|
128
156
|
}
|
|
129
157
|
|
|
130
158
|
// window.location.href = qrCodeMessage;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<template #extension>
|
|
22
22
|
<v-row no-gutters class="w-100 d-flex flex-column">
|
|
23
23
|
<v-card
|
|
24
|
-
class="w-100
|
|
24
|
+
class="w-100 filter-bar d-flex align-center ga-5"
|
|
25
25
|
flat
|
|
26
26
|
:height="60"
|
|
27
27
|
>
|
|
@@ -68,20 +68,18 @@
|
|
|
68
68
|
<v-spacer />
|
|
69
69
|
|
|
70
70
|
<v-col cols="auto">
|
|
71
|
-
<
|
|
71
|
+
<StatusChip
|
|
72
72
|
v-if="isScheduleClosed"
|
|
73
|
-
|
|
74
|
-
variant="tonal"
|
|
73
|
+
status="Closed"
|
|
75
74
|
class="text-capitalize mr-2"
|
|
76
75
|
>
|
|
77
|
-
<v-icon
|
|
76
|
+
<v-icon size="14">mdi-lock</v-icon>
|
|
78
77
|
Closed – View Only
|
|
79
|
-
</
|
|
78
|
+
</StatusChip>
|
|
80
79
|
<v-btn
|
|
81
80
|
v-if="canViewHistory"
|
|
82
|
-
class="text-none mr-2"
|
|
83
|
-
|
|
84
|
-
color="primary"
|
|
81
|
+
class="text-none mr-2 screen-btn-primary"
|
|
82
|
+
variant="flat"
|
|
85
83
|
aria-label="History"
|
|
86
84
|
@click="openHistory"
|
|
87
85
|
>
|
|
@@ -89,9 +87,8 @@
|
|
|
89
87
|
</v-btn>
|
|
90
88
|
<v-btn
|
|
91
89
|
v-if="canGenerateChecklist"
|
|
92
|
-
class="text-none"
|
|
93
|
-
|
|
94
|
-
color="primary"
|
|
90
|
+
class="text-none screen-btn-primary"
|
|
91
|
+
variant="flat"
|
|
95
92
|
aria-label="Generate"
|
|
96
93
|
@click="_generateChecklist"
|
|
97
94
|
:loading="submitting"
|
|
@@ -114,20 +111,13 @@
|
|
|
114
111
|
</span>
|
|
115
112
|
</div>
|
|
116
113
|
<div v-else-if="isScheduleClosed">
|
|
117
|
-
<
|
|
118
|
-
size="small"
|
|
119
|
-
color="error"
|
|
120
|
-
variant="tonal"
|
|
121
|
-
class="text-capitalize"
|
|
122
|
-
>Closed</v-chip
|
|
123
|
-
>
|
|
114
|
+
<StatusChip status="Closed" class="text-capitalize" />
|
|
124
115
|
</div>
|
|
125
116
|
<div v-else>
|
|
126
117
|
<v-btn
|
|
127
118
|
v-if="canManageScheduleTasks"
|
|
128
119
|
variant="outlined"
|
|
129
|
-
|
|
130
|
-
class="text-capitalize px-4 py-2"
|
|
120
|
+
class="text-capitalize screen-btn-ghost"
|
|
131
121
|
@click.stop="() => _acceptAreaChecklist(item._id)"
|
|
132
122
|
>
|
|
133
123
|
Accept
|
|
@@ -146,14 +136,11 @@
|
|
|
146
136
|
</template>
|
|
147
137
|
|
|
148
138
|
<template #item.status="{ value }">
|
|
149
|
-
<
|
|
139
|
+
<StatusChip
|
|
150
140
|
class="text-capitalize"
|
|
151
|
-
:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
>
|
|
155
|
-
{{ value || "No Status" }}
|
|
156
|
-
</v-chip>
|
|
141
|
+
:status="value"
|
|
142
|
+
:label="value || 'No Status'"
|
|
143
|
+
/>
|
|
157
144
|
</template>
|
|
158
145
|
</TableMain></v-col
|
|
159
146
|
>
|
|
@@ -262,27 +249,6 @@ watchEffect(() => {
|
|
|
262
249
|
}
|
|
263
250
|
});
|
|
264
251
|
|
|
265
|
-
const getStatusColor = (status: unknown): string => {
|
|
266
|
-
if (!status) return "grey";
|
|
267
|
-
|
|
268
|
-
const normalized = String(status).toLowerCase();
|
|
269
|
-
|
|
270
|
-
switch (normalized) {
|
|
271
|
-
case "open":
|
|
272
|
-
return "grey";
|
|
273
|
-
case "ongoing":
|
|
274
|
-
return "primary";
|
|
275
|
-
case "completed":
|
|
276
|
-
case "accepted":
|
|
277
|
-
return "success";
|
|
278
|
-
case "closed":
|
|
279
|
-
case "rejected":
|
|
280
|
-
return "error";
|
|
281
|
-
default:
|
|
282
|
-
return "secondary";
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
252
|
function showMessage(msg: string, color: string = "error") {
|
|
287
253
|
message.value = msg;
|
|
288
254
|
messageColor.value = color;
|
|
@@ -21,10 +21,8 @@
|
|
|
21
21
|
<v-row no-gutters align="center" class="w-100">
|
|
22
22
|
<v-col cols="auto" v-if="canCreateScheduleTask">
|
|
23
23
|
<v-btn
|
|
24
|
-
class="text-none"
|
|
25
|
-
|
|
26
|
-
variant="tonal"
|
|
27
|
-
size="large"
|
|
24
|
+
class="text-none screen-btn-primary"
|
|
25
|
+
variant="flat"
|
|
28
26
|
@click="openFormDialog"
|
|
29
27
|
>
|
|
30
28
|
Create Schedule Task
|
|
@@ -33,7 +31,7 @@
|
|
|
33
31
|
|
|
34
32
|
<v-spacer />
|
|
35
33
|
|
|
36
|
-
<v-col cols="auto">
|
|
34
|
+
<v-col cols="auto" class="filter-field">
|
|
37
35
|
<v-text-field
|
|
38
36
|
v-model="searchInput"
|
|
39
37
|
density="compact"
|
|
@@ -49,28 +47,31 @@
|
|
|
49
47
|
|
|
50
48
|
<template #item.areas="{ value }">
|
|
51
49
|
<div v-if="value && value.length > 0">
|
|
52
|
-
<
|
|
50
|
+
<StatusChip
|
|
53
51
|
v-for="(area, idx) in value.slice(0, 2)"
|
|
54
52
|
:key="idx"
|
|
55
|
-
size="small"
|
|
56
53
|
class="mr-1 mb-1"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
:dot="false"
|
|
55
|
+
tone="neutral"
|
|
56
|
+
:label="area.name"
|
|
57
|
+
/>
|
|
58
|
+
<StatusChip
|
|
59
|
+
v-if="value.length > 2"
|
|
60
|
+
class="mb-1"
|
|
61
|
+
:dot="false"
|
|
62
|
+
tone="neutral"
|
|
63
|
+
:label="`+${value.length - 2} more`"
|
|
64
|
+
/>
|
|
63
65
|
</div>
|
|
64
66
|
<span v-else>N/A</span>
|
|
65
67
|
</template>
|
|
66
68
|
|
|
67
69
|
<template #item.status="{ value }">
|
|
68
|
-
<
|
|
69
|
-
:color="value === 'active' ? 'success' : 'default'"
|
|
70
|
+
<StatusChip
|
|
70
71
|
class="text-capitalize"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
:status="value"
|
|
73
|
+
:label="value || 'N/A'"
|
|
74
|
+
/>
|
|
74
75
|
</template>
|
|
75
76
|
</TableMain>
|
|
76
77
|
|
|
@@ -270,15 +271,11 @@
|
|
|
270
271
|
Status:
|
|
271
272
|
</v-col>
|
|
272
273
|
<v-col cols="7" class="text-subtitle-2">
|
|
273
|
-
<
|
|
274
|
-
:color="
|
|
275
|
-
selectedTask.status === 'active' ? 'success' : 'default'
|
|
276
|
-
"
|
|
277
|
-
size="small"
|
|
274
|
+
<StatusChip
|
|
278
275
|
class="text-capitalize"
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
276
|
+
:status="selectedTask.status"
|
|
277
|
+
:label="selectedTask.status || 'N/A'"
|
|
278
|
+
/>
|
|
282
279
|
</v-col>
|
|
283
280
|
</v-row>
|
|
284
281
|
|