@7365admin1/layer-common 1.11.27 → 1.11.29
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/CHANGELOG.md +12 -0
- package/components/AccessCardAddForm.vue +30 -51
- package/components/AccessCardAssignToUnitForm.vue +19 -35
- package/components/AccessManagement.vue +1 -1
- package/components/InvitationForm.vue +18 -5
- package/components/RolePermissionFormCreate.vue +3 -0
- package/components/RolePermissionFormPreviewUpdate.vue +3 -0
- package/components/ServiceProviderMain.vue +12 -7
- package/components/SiteSettings.vue +144 -128
- package/components/VisitorManagement.vue +361 -602
- package/components/VisitorsReportPreview.vue +400 -0
- package/composables/useAccessManagement.ts +11 -0
- package/composables/useOrg.ts +2 -2
- package/composables/useRole.ts +2 -0
- package/composables/useServiceProvider.ts +12 -4
- package/package.json +1 -1
- package/pages/[org]/[site]/visitor-management/preview/index.vue +21 -0
- package/types/customer.d.ts +1 -1
|
@@ -5,47 +5,57 @@
|
|
|
5
5
|
<v-expansion-panel color="primary">
|
|
6
6
|
<v-expansion-panel-title>
|
|
7
7
|
<v-icon class="mr-2">mdi-cog</v-icon>
|
|
8
|
-
Site
|
|
8
|
+
Site Information
|
|
9
9
|
</v-expansion-panel-title>
|
|
10
10
|
|
|
11
11
|
<v-expansion-panel-text>
|
|
12
|
-
<v-row no-gutters class="d-flex justify-center">
|
|
13
|
-
<v-
|
|
14
|
-
<v-row
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
<v-row no-gutters class="d-flex justify-center py-10">
|
|
13
|
+
<v-form class="w-100">
|
|
14
|
+
<v-row>
|
|
15
|
+
<!-- Cover Photo + Preview -->
|
|
16
|
+
<v-col cols="12" lg="5" class="pt-0">
|
|
17
|
+
<InputLabel title="Cover Photo (displayed in resident app)" class="d-block mb-2" />
|
|
18
|
+
<InputFileV2 v-model="coverPhoto" accept="image/*" title="Upload cover photo" :height="120" />
|
|
19
|
+
</v-col>
|
|
20
|
+
<v-col cols="12" lg="7" class="pl-lg-4 pt-4 pt-lg-0">
|
|
21
|
+
<InputLabel title="Preview" class="d-block mb-2" />
|
|
22
|
+
<v-img v-if="coverPhotoUrl" :src="coverPhotoUrl" aspect-ratio="16/9" cover rounded="lg"
|
|
23
|
+
class="w-100 border" />
|
|
24
|
+
<div v-else
|
|
25
|
+
class="w-100 d-flex align-center justify-center rounded-lg bg-grey-lighten-4 border border-dashed"
|
|
26
|
+
style="aspect-ratio: 16/9">
|
|
27
|
+
<span class="text-caption text-medium-emphasis">No cover photo uploaded</span>
|
|
28
|
+
</div>
|
|
17
29
|
</v-col>
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
<!-- Description + Site Documents -->
|
|
32
|
+
<v-col cols="12" lg="6" class="mt-2">
|
|
33
|
+
<InputLabel title="Description" class="d-block mb-1" />
|
|
34
|
+
<v-textarea v-model="description" variant="outlined" density="compact" rows="4" hide-details no-resize
|
|
35
|
+
style="height: 119px" />
|
|
36
|
+
</v-col>
|
|
37
|
+
<v-col cols="12" lg="6" class="mt-2 pl-lg-2">
|
|
38
|
+
<InputLabel title="Site Documents" class="d-block mb-1" />
|
|
39
|
+
<InputFileV2 v-model="siteDocuments" :multiple="true" accept="*/*" title="Upload documents"
|
|
40
|
+
:height="104" />
|
|
41
|
+
</v-col>
|
|
42
|
+
|
|
43
|
+
<!-- Submit -->
|
|
44
|
+
<v-col cols="12" class="pt-0 d-flex justify-end">
|
|
45
|
+
<v-btn color="primary-button" class="text-none" size="large" variant="flat" text="Save"
|
|
46
|
+
:loading="savingSiteInfo" @click="saveSiteInfo" />
|
|
47
|
+
</v-col>
|
|
48
|
+
|
|
49
|
+
<!-- Divider -->
|
|
50
|
+
<v-col cols="12" class="my-2">
|
|
51
|
+
<v-divider />
|
|
34
52
|
</v-col>
|
|
35
53
|
|
|
36
54
|
<v-col cols="12">
|
|
37
55
|
<v-row no-gutters>
|
|
38
56
|
<v-col cols="12" lg="4" class="mt-2">
|
|
39
|
-
<NumberSettingField
|
|
40
|
-
|
|
41
|
-
title="No. of guard posts"
|
|
42
|
-
type="guard_posts"
|
|
43
|
-
:read-only="!canManageSiteSettings"
|
|
44
|
-
:existing-guard-posts-number="existingGuardPostNumber"
|
|
45
|
-
:site-id="siteId"
|
|
46
|
-
:disabled="existingGuardPostNumber === guardPosts"
|
|
47
|
-
@success="refreshSiteData"
|
|
48
|
-
/>
|
|
57
|
+
<NumberSettingField v-model="blocks" title="No. of blocks" type="blocks" :site-id="siteId"
|
|
58
|
+
:disabled="existingBlockNumber === blocks" @success="refreshSiteData" />
|
|
49
59
|
</v-col>
|
|
50
60
|
</v-row>
|
|
51
61
|
</v-col>
|
|
@@ -53,135 +63,76 @@
|
|
|
53
63
|
<v-col cols="12">
|
|
54
64
|
<v-row no-gutters>
|
|
55
65
|
<v-col cols="12" lg="4" class="mt-2">
|
|
56
|
-
<v-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<v-col cols="6">
|
|
60
|
-
<InputLabel
|
|
61
|
-
class="text-capitalize font-weight-bold"
|
|
62
|
-
title="Grace Period"
|
|
63
|
-
required
|
|
64
|
-
/>
|
|
65
|
-
<v-text-field
|
|
66
|
-
v-model="gracePeriod"
|
|
67
|
-
type="number"
|
|
68
|
-
density="comfortable"
|
|
69
|
-
:readonly="!canManageSiteSettings"
|
|
70
|
-
:rules="[requiredRule]"
|
|
71
|
-
/>
|
|
72
|
-
</v-col>
|
|
73
|
-
|
|
74
|
-
<v-col cols="6">
|
|
75
|
-
<v-btn
|
|
76
|
-
v-if="canManageSiteSettings"
|
|
77
|
-
color="primary"
|
|
78
|
-
class="text-none mt-6"
|
|
79
|
-
size="large"
|
|
80
|
-
variant="flat"
|
|
81
|
-
:disabled="
|
|
82
|
-
!gracePeriodValid ||
|
|
83
|
-
existingGracePeriodNumber === gracePeriod
|
|
84
|
-
"
|
|
85
|
-
:loading="gracePeriodLoading"
|
|
86
|
-
text="Save"
|
|
87
|
-
@click="handleSaveGracePeriod"
|
|
88
|
-
/>
|
|
89
|
-
</v-col>
|
|
90
|
-
</v-row>
|
|
91
|
-
</v-form>
|
|
92
|
-
</v-row>
|
|
66
|
+
<NumberSettingField v-model="guardPosts" title="No. of guard posts" type="guard_posts"
|
|
67
|
+
:site-id="siteId" :disabled="existingGuardPostNumber === guardPosts"
|
|
68
|
+
@success="refreshSiteData" />
|
|
93
69
|
</v-col>
|
|
94
70
|
</v-row>
|
|
95
71
|
</v-col>
|
|
96
72
|
</v-row>
|
|
97
|
-
</v-
|
|
73
|
+
</v-form>
|
|
98
74
|
</v-row>
|
|
99
75
|
</v-expansion-panel-text>
|
|
100
76
|
</v-expansion-panel>
|
|
101
77
|
|
|
102
78
|
<!-- ANPR CAMERA -->
|
|
103
|
-
<v-expansion-panel color="primary">
|
|
79
|
+
<v-expansion-panel v-if="showAnprCamera" color="primary">
|
|
104
80
|
<v-expansion-panel-title>
|
|
105
81
|
<v-icon class="mr-2">mdi-camera</v-icon>
|
|
106
82
|
ANPR Camera
|
|
107
83
|
</v-expansion-panel-title>
|
|
108
84
|
|
|
109
85
|
<v-expansion-panel-text>
|
|
110
|
-
<CameraMain
|
|
111
|
-
:
|
|
112
|
-
:read-only="!canManageSiteSettings"
|
|
113
|
-
:guard-posts="siteData?.metadata?.guardPosts"
|
|
114
|
-
/>
|
|
86
|
+
<CameraMain :site="siteId" :read-only="!canManageSiteSettings"
|
|
87
|
+
:guard-posts="siteData?.metadata?.guardPosts" />
|
|
115
88
|
</v-expansion-panel-text>
|
|
116
89
|
</v-expansion-panel>
|
|
117
90
|
|
|
118
91
|
<!-- CCTV CAMERA -->
|
|
119
|
-
<v-expansion-panel color="primary">
|
|
92
|
+
<v-expansion-panel v-if="showCctvCamera" color="primary">
|
|
120
93
|
<v-expansion-panel-title>
|
|
121
94
|
<v-icon class="mr-2">mdi-cctv</v-icon>
|
|
122
95
|
CCTV Camera
|
|
123
96
|
</v-expansion-panel-title>
|
|
124
97
|
|
|
125
98
|
<v-expansion-panel-text>
|
|
126
|
-
<CameraMain
|
|
127
|
-
:site="siteId"
|
|
128
|
-
type="ip"
|
|
129
|
-
:read-only="!canManageSiteSettings"
|
|
130
|
-
/>
|
|
99
|
+
<CameraMain :site="siteId" type="ip" :read-only="!canManageSiteSettings" />
|
|
131
100
|
</v-expansion-panel-text>
|
|
132
101
|
</v-expansion-panel>
|
|
133
102
|
|
|
134
|
-
<v-expansion-panel color="primary">
|
|
103
|
+
<v-expansion-panel v-if="showWorkOrderSettings" color="primary" class="">
|
|
135
104
|
<v-expansion-panel-title>
|
|
136
105
|
<v-icon class="mr-2">mdi-format-list-numbered</v-icon>
|
|
137
106
|
Work Order Settings
|
|
138
107
|
</v-expansion-panel-title>
|
|
139
108
|
|
|
140
109
|
<v-expansion-panel-text>
|
|
141
|
-
<v-text-field
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
/>
|
|
147
|
-
|
|
148
|
-
<v-select
|
|
149
|
-
v-model="noOfDigits"
|
|
150
|
-
:items="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
|
|
151
|
-
label="No. of Digits"
|
|
152
|
-
:disabled="isLoading || !canManageSiteSettings"
|
|
153
|
-
/>
|
|
110
|
+
<v-text-field v-model="prefix" label="Prefix" :disabled="isLoading || !canManageSiteSettings"
|
|
111
|
+
@input="handleInput" />
|
|
112
|
+
|
|
113
|
+
<v-select v-model="noOfDigits" :items="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" label="No. of Digits"
|
|
114
|
+
:disabled="isLoading || !canManageSiteSettings" />
|
|
154
115
|
|
|
155
116
|
<v-text-field v-model="orderNumberPreview" label="Preview" readonly />
|
|
156
117
|
|
|
157
|
-
<v-btn
|
|
158
|
-
v-if="canManageSiteSettings"
|
|
159
|
-
class="mt-2"
|
|
160
|
-
:loading="isLoading"
|
|
161
|
-
@click="save"
|
|
162
|
-
>
|
|
118
|
+
<v-btn v-if="canManageSiteSettings" class="mt-2" :loading="isLoading" @click="save">
|
|
163
119
|
Save
|
|
164
120
|
</v-btn>
|
|
165
121
|
</v-expansion-panel-text>
|
|
166
122
|
</v-expansion-panel>
|
|
167
123
|
|
|
168
|
-
<v-expansion-panel color="primary">
|
|
124
|
+
<v-expansion-panel v-if="showDeliveryCompanies" color="primary">
|
|
169
125
|
<v-expansion-panel-title>
|
|
170
126
|
<v-icon class="mr-2">mdi-truck</v-icon>
|
|
171
127
|
Delivery Companies
|
|
172
128
|
</v-expansion-panel-title>
|
|
173
129
|
<v-expansion-panel-text class="">
|
|
174
|
-
<DeliveryCompany
|
|
175
|
-
:
|
|
176
|
-
v-model:initial="deliveryCompanies"
|
|
177
|
-
@refresh-site="refreshSiteData"
|
|
178
|
-
@update:companiesValue="handleUpdateCompanies"
|
|
179
|
-
:read-only="!canManageSiteSettings"
|
|
180
|
-
/>
|
|
130
|
+
<DeliveryCompany :site="siteId" v-model:initial="deliveryCompanies" @refresh-site="refreshSiteData"
|
|
131
|
+
@update:companiesValue="handleUpdateCompanies" :read-only="!canManageSiteSettings" />
|
|
181
132
|
</v-expansion-panel-text>
|
|
182
133
|
</v-expansion-panel>
|
|
183
134
|
|
|
184
|
-
<v-expansion-panel color="primary">
|
|
135
|
+
<v-expansion-panel v-if="showResidentVisitors" color="primary">
|
|
185
136
|
<v-expansion-panel-title>
|
|
186
137
|
<v-icon class="mr-2">mdi-car</v-icon>
|
|
187
138
|
Resident Visitors
|
|
@@ -190,6 +141,8 @@
|
|
|
190
141
|
<OvernightParkingAvailability :site="siteId" />
|
|
191
142
|
</v-expansion-panel-text>
|
|
192
143
|
</v-expansion-panel>
|
|
144
|
+
|
|
145
|
+
<slot name="panels" :can-manage="canManageSiteSettings" :site-id="siteId" />
|
|
193
146
|
</v-expansion-panels>
|
|
194
147
|
|
|
195
148
|
<Snackbar v-model="toast.show" :text="toast.message" :color="toast.color" />
|
|
@@ -197,14 +150,25 @@
|
|
|
197
150
|
</template>
|
|
198
151
|
|
|
199
152
|
<script setup lang="ts">
|
|
153
|
+
import useFile from '../composables/useFile';
|
|
154
|
+
import useSiteSettings from '../composables/useSiteSettings';
|
|
155
|
+
import useUtils from '../composables/useUtils';
|
|
156
|
+
import useWorkOrder from '../composables/useWorkOrder';
|
|
157
|
+
|
|
200
158
|
const props = defineProps({
|
|
201
159
|
siteId: { type: String, required: true },
|
|
202
160
|
canManageSiteSettings: { type: Boolean, default: false },
|
|
161
|
+
showAnprCamera: { type: Boolean, default: false },
|
|
162
|
+
showCctvCamera: { type: Boolean, default: false },
|
|
163
|
+
showWorkOrderSettings: { type: Boolean, default: false },
|
|
164
|
+
showDeliveryCompanies: { type: Boolean, default: false },
|
|
165
|
+
showResidentVisitors: { type: Boolean, default: false },
|
|
203
166
|
});
|
|
204
167
|
|
|
205
|
-
const openPanels = ref([0]); // default open first
|
|
206
168
|
|
|
207
|
-
const
|
|
169
|
+
const openPanels = ref([]); // default open first
|
|
170
|
+
|
|
171
|
+
const { getSiteById, updateSitebyId, updateSiteInformation } = useSiteSettings();
|
|
208
172
|
const { requiredRule } = useUtils();
|
|
209
173
|
const { getFileUrl } = useFile();
|
|
210
174
|
const { getWorkOrderSettings, createWorkOrderSettings } = useWorkOrder();
|
|
@@ -228,12 +192,30 @@ const prefix = ref("");
|
|
|
228
192
|
const noOfDigits = ref(1);
|
|
229
193
|
const isLoading = ref(false);
|
|
230
194
|
|
|
195
|
+
const coverPhoto = ref<string[]>([]);
|
|
196
|
+
const description = ref<string>("");
|
|
197
|
+
const siteDocuments = ref<string[]>([]);
|
|
198
|
+
const siteDocumentsNames = ref<Record<string, string>>({});
|
|
199
|
+
const savingSiteInfo = ref<boolean>(false);
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
const coverPhotoUrl = computed(() =>
|
|
203
|
+
coverPhoto.value.length > 0 ? getFileUrl(coverPhoto.value[0]) : "",
|
|
204
|
+
);
|
|
205
|
+
|
|
231
206
|
const toast = reactive({
|
|
232
207
|
show: false,
|
|
233
208
|
message: "",
|
|
234
209
|
color: "",
|
|
235
210
|
});
|
|
236
211
|
|
|
212
|
+
function showSnackbar(text: string, color = "success") {
|
|
213
|
+
toast.message = text;
|
|
214
|
+
toast.color = color;
|
|
215
|
+
toast.show = true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
237
219
|
const { data: siteData, refresh: refreshSiteData } = await useLazyAsyncData(
|
|
238
220
|
`site-${props.siteId}`,
|
|
239
221
|
() => getSiteById(props.siteId)
|
|
@@ -242,24 +224,35 @@ const { data: siteData, refresh: refreshSiteData } = await useLazyAsyncData(
|
|
|
242
224
|
watch(
|
|
243
225
|
siteData,
|
|
244
226
|
(val: TSite) => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
227
|
+
const siteDataValue = val as any;
|
|
228
|
+
if (siteDataValue) {
|
|
229
|
+
blocks.value = siteDataValue.metadata?.block || 0;
|
|
230
|
+
existingBlockNumber.value = blocks.value;
|
|
231
|
+
|
|
232
|
+
guardPosts.value = siteDataValue.metadata?.guardPosts || 0;
|
|
233
|
+
existingGuardPostNumber.value = guardPosts.value;
|
|
234
|
+
deliveryCompanies.value = Array.isArray(
|
|
235
|
+
siteDataValue?.deliveryCompanyList,
|
|
236
|
+
)
|
|
237
|
+
? [...siteDataValue.deliveryCompanyList]
|
|
238
|
+
: [];
|
|
239
|
+
|
|
240
|
+
const info = siteDataValue.siteInformation;
|
|
241
|
+
if (info) {
|
|
242
|
+
coverPhoto.value = info.bgImage ? [info.bgImage] : [];
|
|
243
|
+
description.value = info.description ?? "";
|
|
244
|
+
const docs: { id: string; name: string }[] = info.docs ?? [];
|
|
245
|
+
siteDocuments.value = docs.map((d) => d.id);
|
|
246
|
+
docs.forEach((d) => {
|
|
247
|
+
siteDocumentsNames.value[d.id] = d.name;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
259
251
|
},
|
|
260
252
|
{ immediate: true }
|
|
261
253
|
);
|
|
262
254
|
|
|
255
|
+
|
|
263
256
|
async function handleSaveGracePeriod() {
|
|
264
257
|
gracePeriodLoading.value = true;
|
|
265
258
|
try {
|
|
@@ -276,6 +269,29 @@ async function handleSaveGracePeriod() {
|
|
|
276
269
|
}
|
|
277
270
|
}
|
|
278
271
|
|
|
272
|
+
async function saveSiteInfo() {
|
|
273
|
+
savingSiteInfo.value = true;
|
|
274
|
+
try {
|
|
275
|
+
await updateSiteInformation(props.siteId, {
|
|
276
|
+
bgImage: coverPhoto.value[0] ?? "",
|
|
277
|
+
description: description.value,
|
|
278
|
+
docs: siteDocuments.value.map((id) => ({
|
|
279
|
+
id,
|
|
280
|
+
name: siteDocumentsNames.value[id] ?? "",
|
|
281
|
+
})),
|
|
282
|
+
});
|
|
283
|
+
showSnackbar("Site information updated successfully.");
|
|
284
|
+
} catch (err: any) {
|
|
285
|
+
const msg =
|
|
286
|
+
err?.response?._data?.message ??
|
|
287
|
+
err?.message ??
|
|
288
|
+
"Failed to update site information.";
|
|
289
|
+
showSnackbar(msg, "error");
|
|
290
|
+
} finally {
|
|
291
|
+
savingSiteInfo.value = false;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
279
295
|
async function handleLogo(action: string) {
|
|
280
296
|
try {
|
|
281
297
|
await updateSitebyId(props.siteId, {
|
|
@@ -303,8 +319,8 @@ const { data: workOrderSetting } = await useLazyAsyncData(
|
|
|
303
319
|
|
|
304
320
|
watchEffect(() => {
|
|
305
321
|
if (workOrderSetting.value) {
|
|
306
|
-
prefix.value = workOrderSetting.value
|
|
307
|
-
noOfDigits.value = workOrderSetting.value
|
|
322
|
+
prefix.value = workOrderSetting.value?.prefix;
|
|
323
|
+
noOfDigits.value = workOrderSetting.value?.noOfDigits;
|
|
308
324
|
}
|
|
309
325
|
});
|
|
310
326
|
|