@7365admin1/layer-common 1.10.5 → 1.10.7
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/AccessCardDetailsDialog.vue +144 -0
- package/components/AccessCardPreviewDialog.vue +7 -2
- package/components/AccessCardQrTagging.vue +314 -34
- package/components/AccessCardQrTaggingPrintQr.vue +75 -0
- package/components/AccessManagement.vue +5 -1
- package/components/AreaChecklistHistoryLogs.vue +9 -0
- package/components/BuildingForm.vue +36 -5
- package/components/BuildingManagement/buildings.vue +18 -9
- package/components/BuildingManagement/units.vue +12 -114
- package/components/BuildingUnitFormAdd.vue +39 -30
- package/components/BuildingUnitFormEdit.vue +265 -116
- package/components/CleaningScheduleMain.vue +60 -13
- package/components/Dialog/DeleteConfirmation.vue +2 -2
- package/components/Dialog/UpdateMoreAction.vue +2 -2
- package/components/EntryPassInformation.vue +215 -0
- package/components/Input/InputPhoneNumberV2.vue +11 -0
- package/components/ManageChecklistMain.vue +29 -3
- package/components/PlateNumberDisplay.vue +9 -1
- package/components/ScheduleAreaMain.vue +56 -0
- package/components/TableHygiene.vue +265 -113
- package/components/UnitPersonCard.vue +63 -0
- package/components/VehicleAddSelection.vue +2 -2
- package/components/VehicleForm.vue +169 -47
- package/components/VehicleManagement.vue +169 -43
- package/components/VisitorForm.vue +19 -0
- package/components/VisitorManagement.vue +1 -1
- package/components/WorkOrder/Main.vue +2 -1
- package/composables/useAccessManagement.ts +63 -0
- package/composables/useFeedback.ts +2 -2
- package/composables/usePeople.ts +14 -3
- package/composables/useVehicle.ts +15 -1
- package/composables/useWorkOrder.ts +2 -2
- package/package.json +3 -2
- package/types/cleaner-schedule.d.ts +1 -0
- package/types/html2pdf.d.ts +19 -0
- package/types/people.d.ts +4 -0
- package/types/site.d.ts +8 -0
- package/types/vehicle.d.ts +3 -4
- package/.playground/app.vue +0 -41
- package/.playground/eslint.config.mjs +0 -6
- package/.playground/nuxt.config.ts +0 -22
- package/.playground/pages/feedback.vue +0 -30
- package/components/AccessCardHistoryDialog.vue +0 -133
|
@@ -1,64 +1,194 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card width="100%">
|
|
2
|
+
<v-card width="100%" :loading="getUnitPeoplePending">
|
|
3
3
|
<v-toolbar>
|
|
4
4
|
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
|
-
<span class="font-weight-bold text-h5">
|
|
5
|
+
<span class="font-weight-bold text-h5"> Unit Info ( {{ buildingUnit.name }} ) </span>
|
|
6
6
|
</v-row>
|
|
7
7
|
</v-toolbar>
|
|
8
|
-
<v-
|
|
9
|
-
<v-
|
|
10
|
-
<v-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
8
|
+
<v-toolbar density="compact">
|
|
9
|
+
<v-tabs v-model="tab" class="mt-0" active-class="bg-success">
|
|
10
|
+
<v-tab value="general">General</v-tab>
|
|
11
|
+
<v-tab value="residents">Residents/Tenants</v-tab>
|
|
12
|
+
<v-tab value="vehicles">Vehicles</v-tab>
|
|
13
|
+
<v-tab value="others">Others</v-tab>
|
|
14
|
+
</v-tabs>
|
|
15
|
+
</v-toolbar>
|
|
16
|
+
<v-card-text style="max-height: 100vh; min-height: 60svh; overflow-y: auto">
|
|
17
|
+
<template v-if="tab === 'general'">
|
|
18
|
+
<v-form v-model="validForm" :disabled="disable">
|
|
19
|
+
<v-row>
|
|
20
|
+
<v-col cols="12" md="6" lg="4" class="mt-2">
|
|
21
|
+
<v-row no-gutters>
|
|
22
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Unit Name" />
|
|
23
|
+
<v-col cols="12">
|
|
24
|
+
<v-text-field v-model="buildingUnit.name" density="comfortable"
|
|
25
|
+
:rules="[requiredRule]"></v-text-field>
|
|
26
|
+
</v-col>
|
|
27
|
+
</v-row>
|
|
28
|
+
</v-col>
|
|
29
|
+
|
|
30
|
+
<v-col cols="12" md="6" lg="4" class="mt-2">
|
|
31
|
+
<v-row no-gutters>
|
|
32
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Level" required />
|
|
33
|
+
<v-col cols="12">
|
|
34
|
+
<v-select v-model="buildingUnit.level" :items="buildingLevels" density="comfortable"
|
|
35
|
+
:rules="[requiredRule]"></v-select>
|
|
36
|
+
</v-col>
|
|
37
|
+
</v-row>
|
|
38
|
+
</v-col>
|
|
39
|
+
|
|
40
|
+
<v-col cols="12" md="6" lg="4">
|
|
41
|
+
<v-row>
|
|
42
|
+
<v-col cols="12" class="mt-2">
|
|
43
|
+
<v-row no-gutters>
|
|
44
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Block Name" />
|
|
45
|
+
<v-col cols="12">
|
|
46
|
+
<v-text-field :model-value="`Block ${buildingUnit.block} (${buildingUnit.buildingName})`"
|
|
47
|
+
density="comfortable" readonly class="no-pointer"></v-text-field>
|
|
48
|
+
</v-col>
|
|
49
|
+
</v-row>
|
|
50
|
+
</v-col>
|
|
51
|
+
</v-row>
|
|
52
|
+
</v-col>
|
|
53
|
+
</v-row>
|
|
54
|
+
|
|
55
|
+
<v-divider class="mb-10" />
|
|
56
|
+
<v-row>
|
|
57
|
+
<v-col cols="12" md="6" class="mt-2">
|
|
58
|
+
<v-row no-gutters>
|
|
59
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Billing Address Line 1" />
|
|
60
|
+
<v-col cols="12">
|
|
61
|
+
<v-text-field :model-value="siteData?.address?.line1" density="compact" readonly
|
|
62
|
+
class="no-pointer"></v-text-field>
|
|
63
|
+
</v-col>
|
|
64
|
+
</v-row>
|
|
65
|
+
</v-col>
|
|
66
|
+
<v-col cols="12" md="6" class="mt-2">
|
|
67
|
+
<v-row no-gutters>
|
|
68
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Billing Address Line 2" />
|
|
69
|
+
<v-col cols="12">
|
|
70
|
+
<v-text-field :model-value="siteData?.address?.line2" density="compact" readonly
|
|
71
|
+
class="no-pointer"></v-text-field>
|
|
72
|
+
</v-col>
|
|
73
|
+
</v-row>
|
|
74
|
+
</v-col>
|
|
75
|
+
<v-col cols="12" md="4" lg="4" class="mt-2">
|
|
76
|
+
<v-row no-gutters>
|
|
77
|
+
<InputLabel class="text-capitalize font-weight-bold" title="City" />
|
|
78
|
+
<v-col cols="12">
|
|
79
|
+
<v-text-field :model-value="siteData?.address?.city" density="compact" readonly
|
|
80
|
+
class="no-pointer"></v-text-field>
|
|
81
|
+
</v-col>
|
|
82
|
+
</v-row>
|
|
83
|
+
</v-col>
|
|
84
|
+
<v-col cols="12" md="4" lg="4" class="mt-2">
|
|
85
|
+
<v-row no-gutters>
|
|
86
|
+
<InputLabel class="text-capitalize font-weight-bold" title="State" />
|
|
87
|
+
<v-col cols="12">
|
|
88
|
+
<v-text-field :model-value="siteData?.address?.state" density="compact" readonly
|
|
89
|
+
class="no-pointer"></v-text-field>
|
|
90
|
+
</v-col>
|
|
91
|
+
</v-row>
|
|
92
|
+
</v-col>
|
|
93
|
+
<v-col cols="12" md="4" lg="4" class="mt-2">
|
|
94
|
+
<v-row no-gutters>
|
|
95
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Country" />
|
|
96
|
+
<v-col cols="12">
|
|
97
|
+
<v-text-field :model-value="siteData?.address?.country" density="compact" readonly
|
|
98
|
+
class="no-pointer"></v-text-field>
|
|
99
|
+
</v-col>
|
|
100
|
+
</v-row>
|
|
101
|
+
</v-col>
|
|
102
|
+
<v-col cols="12" md="4" lg="4" class="mt-2">
|
|
103
|
+
<v-row no-gutters>
|
|
104
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Postal Code" />
|
|
105
|
+
<v-col cols="12">
|
|
106
|
+
<v-text-field :model-value="siteData?.address?.postalCode" density="compact" readonly
|
|
107
|
+
class="no-pointer"></v-text-field>
|
|
108
|
+
</v-col>
|
|
109
|
+
</v-row>
|
|
110
|
+
</v-col>
|
|
111
|
+
</v-row>
|
|
112
|
+
</v-form>
|
|
113
|
+
</template>
|
|
114
|
+
|
|
115
|
+
<template v-else-if="tab === 'residents'">
|
|
116
|
+
<v-card width="100%">
|
|
117
|
+
<v-expansion-panels v-model="panels" variant="accordion" >
|
|
118
|
+
|
|
119
|
+
<!-- Residents -->
|
|
120
|
+
<v-expansion-panel value="residents">
|
|
121
|
+
<v-expansion-panel-title class="bg-primary">
|
|
122
|
+
Residents ({{ unitResidentsArray.length }})
|
|
123
|
+
</v-expansion-panel-title>
|
|
124
|
+
|
|
125
|
+
<v-expansion-panel-text>
|
|
126
|
+
<template v-if="unitResidentsArray.length" v-for="resident, residentIndex in unitResidentsArray" :key="residentIndex">
|
|
127
|
+
<UnitPersonCard
|
|
128
|
+
:person="resident" :readOnly="true" />
|
|
129
|
+
<v-divider v-if="(residentIndex + 1) !== unitResidentsArray.length" thickness="2" color="primary" class="my-4" />
|
|
130
|
+
</template>
|
|
131
|
+
<p v-else class="font-weight-semibold text-caption ml-2 mt-5">
|
|
132
|
+
**No residents to display**
|
|
133
|
+
</p>
|
|
134
|
+
</v-expansion-panel-text>
|
|
135
|
+
</v-expansion-panel>
|
|
136
|
+
|
|
137
|
+
<!-- Tenants -->
|
|
138
|
+
<v-expansion-panel value="tenants">
|
|
139
|
+
<v-expansion-panel-title class="bg-primary">
|
|
140
|
+
Tenants ({{ unitTenantsArray.length }})
|
|
141
|
+
</v-expansion-panel-title>
|
|
142
|
+
|
|
143
|
+
<v-expansion-panel-text>
|
|
144
|
+
<template v-if="unitTenantsArray.length" v-for="(tenant, tenantIndex) in unitTenantsArray" :key="tenant._id || tenantIndex">
|
|
145
|
+
<UnitPersonCard :person="tenant" :readOnly="true" />
|
|
146
|
+
<v-divider v-if="(tenantIndex + 1) !== unitTenantsArray.length" thickness="2" color="primary" class="my-4" />
|
|
147
|
+
</template>
|
|
148
|
+
<p v-else class=" font-weight-semibold text-caption ml-2 mt-5">
|
|
149
|
+
**No tenants to display**
|
|
150
|
+
</p>
|
|
151
|
+
</v-expansion-panel-text>
|
|
152
|
+
</v-expansion-panel>
|
|
153
|
+
|
|
154
|
+
<!-- Guests -->
|
|
155
|
+
<v-expansion-panel value="guests">
|
|
156
|
+
<v-expansion-panel-title class="bg-primary">
|
|
157
|
+
Guests ({{ unitGuestsArray.length }})
|
|
158
|
+
</v-expansion-panel-title>
|
|
159
|
+
|
|
160
|
+
<v-expansion-panel-text>
|
|
161
|
+
<template v-if="unitGuestsArray.length" v-for="(guest, guestIndex) in unitGuestsArray" :key="guest._id || guestIndex">
|
|
162
|
+
<UnitPersonCard :person="guest" :readOnly="true" />
|
|
163
|
+
<v-divider v-if="(guestIndex + 1) !== unitGuestsArray.length" thickness="2" color="primary" class="my-4" />
|
|
164
|
+
</template>
|
|
165
|
+
<p v-else class=" font-weight-semibold text-caption ml-2 mt-5">
|
|
166
|
+
**No guests to display**
|
|
167
|
+
</p>
|
|
168
|
+
</v-expansion-panel-text>
|
|
169
|
+
</v-expansion-panel>
|
|
170
|
+
|
|
171
|
+
</v-expansion-panels>
|
|
172
|
+
</v-card>
|
|
173
|
+
</template>
|
|
174
|
+
<template v-else-if="tab === 'vehicles'">
|
|
175
|
+
<p class="font-weight-semibold text-caption ml-2 mt-5">
|
|
176
|
+
**No vehicles to display**
|
|
177
|
+
</p>
|
|
178
|
+
</template>
|
|
179
|
+
|
|
180
|
+
<template v-else-if="tab === 'others'">
|
|
181
|
+
<v-row>
|
|
182
|
+
<!-- <v-col cols="12" md="6" class="mt-2">
|
|
183
|
+
<v-row no-gutters>
|
|
184
|
+
<InputLabel class="text-capitalize font-weight-bold" title="Owner" />
|
|
185
|
+
<v-col cols="12">
|
|
186
|
+
<v-autocomplete v-model="buildingUnit.ownerName" v-model:search="ownerNameInput" :items="peopleItems"
|
|
187
|
+
item-title="name" return-object density="comfortable">
|
|
188
|
+
</v-autocomplete>
|
|
58
189
|
</v-col>
|
|
59
190
|
</v-row>
|
|
60
|
-
</v-col>
|
|
61
|
-
|
|
191
|
+
</v-col> -->
|
|
62
192
|
|
|
63
193
|
<v-col cols="12">
|
|
64
194
|
<v-row>
|
|
@@ -83,7 +213,6 @@
|
|
|
83
213
|
</v-row>
|
|
84
214
|
</v-col>
|
|
85
215
|
|
|
86
|
-
|
|
87
216
|
<v-col cols="12">
|
|
88
217
|
<v-row>
|
|
89
218
|
<v-col cols="12" md="6" class="mt-2">
|
|
@@ -106,65 +235,52 @@
|
|
|
106
235
|
|
|
107
236
|
</v-row>
|
|
108
237
|
</v-col>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<v-col cols="12" md="6" class="mt-2">
|
|
112
|
-
<v-row no-gutters>
|
|
113
|
-
<InputLabel class="text-capitalize font-weight-bold" title="Category" required />
|
|
114
|
-
<v-col cols="12">
|
|
115
|
-
<v-autocomplete v-model="buildingUnit.category" :items="unitCategories" density="comfortable"
|
|
116
|
-
:rules="[requiredRule]"></v-autocomplete>
|
|
117
|
-
</v-col>
|
|
118
|
-
</v-row>
|
|
119
|
-
</v-col>
|
|
120
|
-
|
|
121
|
-
<v-col cols="12">
|
|
122
|
-
<v-row>
|
|
123
|
-
<v-col cols="6" md="6" class="mt-2">
|
|
124
|
-
<v-row no-gutters>
|
|
125
|
-
<InputLabel class="text-capitalize font-weight-bold" title="Level" required />
|
|
126
|
-
<v-col cols="12">
|
|
127
|
-
<v-select v-model="buildingUnit.level" :items="buildingLevels" density="comfortable"
|
|
128
|
-
:rules="[requiredRule]"></v-select>
|
|
129
|
-
</v-col>
|
|
130
|
-
</v-row>
|
|
131
|
-
</v-col>
|
|
132
|
-
</v-row>
|
|
133
|
-
</v-col>
|
|
134
|
-
|
|
135
|
-
<v-col cols="12" class="mt-5">
|
|
136
|
-
<InputLabel class="text-capitalize" title="Upload Files (Lease of Contract, Cert. of Occupancy)" />
|
|
137
|
-
<InputFileV2 v-model="buildingUnit.buildingUnitFiles" :multiple="false" :max-length="10"
|
|
138
|
-
title="Upload PDF Files" accept="application/pdf" />
|
|
139
|
-
</v-col>
|
|
140
|
-
|
|
141
|
-
<v-col cols="12" class="my-2">
|
|
142
|
-
<v-row no-gutters>
|
|
143
|
-
<v-col cols="12" class="text-center">
|
|
144
|
-
<span class="text-none text-subtitle-2 font-weight-medium text-error">
|
|
145
|
-
{{ message }}
|
|
146
|
-
</span>
|
|
147
|
-
</v-col>
|
|
148
|
-
</v-row>
|
|
149
|
-
</v-col>
|
|
150
238
|
</v-row>
|
|
151
|
-
</
|
|
239
|
+
</template>
|
|
152
240
|
</v-card-text>
|
|
153
241
|
|
|
154
|
-
<v-
|
|
242
|
+
<v-col cols="12" class="my-2">
|
|
155
243
|
<v-row no-gutters>
|
|
156
|
-
<v-col cols="
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
</
|
|
244
|
+
<v-col cols="12" class="text-center">
|
|
245
|
+
<span class="text-none text-subtitle-2 font-weight-medium text-error">
|
|
246
|
+
{{ message }}
|
|
247
|
+
</span>
|
|
160
248
|
</v-col>
|
|
249
|
+
</v-row>
|
|
250
|
+
</v-col>
|
|
161
251
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
252
|
+
<v-toolbar class="pa-0" density="compact">
|
|
253
|
+
<v-row no-gutters>
|
|
254
|
+
<v-col :cols="canUpdateUnit || canDeleteUnit ? 6 : 12" class="pa-0">
|
|
255
|
+
<v-btn block variant="text" class="text-none" size="large" @click="emit('cancel')" height="48">
|
|
256
|
+
Close
|
|
166
257
|
</v-btn>
|
|
167
258
|
</v-col>
|
|
259
|
+
|
|
260
|
+
<v-col v-if="canUpdateUnit || canDeleteUnit" cols="6" class="pa-0">
|
|
261
|
+
<v-menu>
|
|
262
|
+
<template #activator="{ props }">
|
|
263
|
+
<v-btn block variant="flat" color="black" class="text-none" height="48" v-bind="props" tile
|
|
264
|
+
:disabled="!canUpdateUnit && !canDeleteUnit">
|
|
265
|
+
More actions
|
|
266
|
+
</v-btn>
|
|
267
|
+
</template>
|
|
268
|
+
|
|
269
|
+
<v-list class="pa-0">
|
|
270
|
+
<v-list-item v-if="canUpdateUnit" @click="submit">
|
|
271
|
+
<v-list-item-title class="text-subtitle-2">
|
|
272
|
+
Save
|
|
273
|
+
</v-list-item-title>
|
|
274
|
+
</v-list-item>
|
|
275
|
+
|
|
276
|
+
<v-list-item v-if="canDeleteUnit" @click="emit('delete-unit')" class="text-red">
|
|
277
|
+
<v-list-item-title class="text-subtitle-2">
|
|
278
|
+
Delete Unit
|
|
279
|
+
</v-list-item-title>
|
|
280
|
+
</v-list-item>
|
|
281
|
+
</v-list>
|
|
282
|
+
</v-menu>
|
|
283
|
+
</v-col>
|
|
168
284
|
</v-row>
|
|
169
285
|
</v-toolbar>
|
|
170
286
|
</v-card>
|
|
@@ -186,7 +302,7 @@ const prop = defineProps({
|
|
|
186
302
|
ownerName: "",
|
|
187
303
|
building: "",
|
|
188
304
|
buildingName: "",
|
|
189
|
-
category: "",
|
|
305
|
+
// category: "",
|
|
190
306
|
block: null,
|
|
191
307
|
level: 0,
|
|
192
308
|
status: "active",
|
|
@@ -197,6 +313,14 @@ const prop = defineProps({
|
|
|
197
313
|
leaseEnd: "",
|
|
198
314
|
}),
|
|
199
315
|
},
|
|
316
|
+
canUpdateUnit: {
|
|
317
|
+
type: Boolean,
|
|
318
|
+
default: true,
|
|
319
|
+
},
|
|
320
|
+
canDeleteUnit: {
|
|
321
|
+
type: Boolean,
|
|
322
|
+
default: true,
|
|
323
|
+
}
|
|
200
324
|
});
|
|
201
325
|
|
|
202
326
|
const buildingUnit = ref({
|
|
@@ -208,7 +332,7 @@ const buildingUnit = ref({
|
|
|
208
332
|
building: "",
|
|
209
333
|
buildingName: "",
|
|
210
334
|
level: 0,
|
|
211
|
-
category: "",
|
|
335
|
+
// category: "",
|
|
212
336
|
block: null,
|
|
213
337
|
status: "active",
|
|
214
338
|
buildingUnitFiles: [],
|
|
@@ -218,20 +342,27 @@ const buildingUnit = ref({
|
|
|
218
342
|
leaseEnd: "",
|
|
219
343
|
});
|
|
220
344
|
|
|
345
|
+
|
|
346
|
+
const siteData = ref<TSite | null>(null);
|
|
347
|
+
|
|
221
348
|
buildingUnit.value = JSON.parse(JSON.stringify(prop.roomFacility));
|
|
222
349
|
|
|
223
|
-
const emit = defineEmits(["cancel", "success", "success:create-more"]);
|
|
350
|
+
const emit = defineEmits(["cancel", "success", "success:create-more", "delete-unit"]);
|
|
351
|
+
|
|
224
352
|
|
|
225
353
|
const ownerNameInput = ref("");
|
|
354
|
+
const panels = ref<string[]>(['residents']);
|
|
355
|
+
|
|
356
|
+
const tab = ref('general')
|
|
226
357
|
|
|
227
358
|
const validForm = ref(false);
|
|
228
359
|
|
|
229
360
|
const { getAll } = useBuilding();
|
|
230
|
-
const {
|
|
361
|
+
const { getPeopleByUnit } = usePeople();
|
|
231
362
|
const { debounce } = useUtils();
|
|
232
363
|
|
|
233
364
|
const buildings = ref<Record<string, any>[]>([]);
|
|
234
|
-
const peopleItems = ref<
|
|
365
|
+
const peopleItems = ref<TPeople[]>([]);
|
|
235
366
|
|
|
236
367
|
const { data: getBuildingReq } = useLazyAsyncData(
|
|
237
368
|
"get-all-buildings",
|
|
@@ -248,15 +379,27 @@ watchEffect(() => {
|
|
|
248
379
|
}
|
|
249
380
|
});
|
|
250
381
|
|
|
251
|
-
const { data: getUnitPeople, refresh: getUnitPeopleRefresh } = useLazyAsyncData(
|
|
382
|
+
const { data: getUnitPeople, refresh: getUnitPeopleRefresh, pending: getUnitPeoplePending } = useLazyAsyncData(
|
|
252
383
|
"get-unit-people",
|
|
253
|
-
async () =>
|
|
384
|
+
async () => await getPeopleByUnit(buildingUnit.value._id, { limit: 10, site: prop.site })
|
|
254
385
|
);
|
|
255
386
|
|
|
256
|
-
watch(getUnitPeople, (newData:
|
|
387
|
+
watch(getUnitPeople, (newData: TPeople[]) => {
|
|
257
388
|
peopleItems.value = newData ?? [];
|
|
258
389
|
});
|
|
259
390
|
|
|
391
|
+
const unitResidentsArray = computed(() => {
|
|
392
|
+
return peopleItems.value.filter((person) => person?.type === 'resident');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const unitTenantsArray = computed(() => {
|
|
396
|
+
return peopleItems.value.filter((person) => person?.type === 'tenant');
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const unitGuestsArray = computed(() => {
|
|
400
|
+
return peopleItems.value.filter((person) => person?.type === 'guest');
|
|
401
|
+
});
|
|
402
|
+
|
|
260
403
|
buildingUnit.value.site = prop.site;
|
|
261
404
|
|
|
262
405
|
const selectedBuilding = computed(() => {
|
|
@@ -285,7 +428,7 @@ function arraysAreEqual(a: any[], b: any[]): boolean {
|
|
|
285
428
|
const hasChanges = computed(() => {
|
|
286
429
|
return (
|
|
287
430
|
prop.roomFacility.name !== buildingUnit.value.name ||
|
|
288
|
-
prop.roomFacility.category !== buildingUnit.value.category ||
|
|
431
|
+
// prop.roomFacility.category !== buildingUnit.value.category ||
|
|
289
432
|
prop.roomFacility.level !== buildingUnit.value.level ||
|
|
290
433
|
!arraysAreEqual(
|
|
291
434
|
prop.roomFacility.buildingUnitFiles,
|
|
@@ -306,7 +449,7 @@ async function submit() {
|
|
|
306
449
|
await updateById(buildingUnit.value._id ?? "", {
|
|
307
450
|
name: buildingUnit.value.name,
|
|
308
451
|
level: buildingUnit.value.level,
|
|
309
|
-
category: buildingUnit.value.category,
|
|
452
|
+
// category: buildingUnit.value.category,
|
|
310
453
|
buildingUnitFiles: buildingUnit.value.buildingUnitFiles || [],
|
|
311
454
|
companyName: buildingUnit.value.companyName,
|
|
312
455
|
companyRegistrationNumber: buildingUnit.value.companyRegistrationNumber || "",
|
|
@@ -338,3 +481,9 @@ watch(ownerNameInput, (newVal) => {
|
|
|
338
481
|
debounceSearchOwner();
|
|
339
482
|
}, { immediate: false });
|
|
340
483
|
</script>
|
|
484
|
+
|
|
485
|
+
<style lang="scss" scoped>
|
|
486
|
+
.no-pointer {
|
|
487
|
+
pointer-events: none;
|
|
488
|
+
}
|
|
489
|
+
</style>
|
|
@@ -57,8 +57,13 @@
|
|
|
57
57
|
}}
|
|
58
58
|
</template>
|
|
59
59
|
<template #item.closeIn="{ item }">
|
|
60
|
-
<v-chip
|
|
61
|
-
|
|
60
|
+
<v-chip
|
|
61
|
+
class="text-capitalize"
|
|
62
|
+
variant="flat"
|
|
63
|
+
:color="getCloseInColor(item._id)"
|
|
64
|
+
pill
|
|
65
|
+
>
|
|
66
|
+
{{ remainingTime[item._id] || "00h 00m" }}
|
|
62
67
|
</v-chip>
|
|
63
68
|
</template>
|
|
64
69
|
<template #item.status="{ value }">
|
|
@@ -99,6 +104,7 @@
|
|
|
99
104
|
<script lang="ts" setup>
|
|
100
105
|
import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
|
|
101
106
|
import useCleaningSchedules from "../composables/useCleaningSchedules";
|
|
107
|
+
import useUtils from "../composables/useUtils";
|
|
102
108
|
|
|
103
109
|
const props = defineProps({
|
|
104
110
|
orgId: { type: String, required: true },
|
|
@@ -191,18 +197,13 @@ const {
|
|
|
191
197
|
);
|
|
192
198
|
|
|
193
199
|
function calculateRemainingTime(
|
|
194
|
-
|
|
200
|
+
closeIn: Date | string | number | null | undefined
|
|
195
201
|
) {
|
|
196
|
-
if (!
|
|
197
|
-
const _date = new Date(
|
|
202
|
+
if (!closeIn) return -1;
|
|
203
|
+
const _date = new Date(closeIn);
|
|
198
204
|
if (isNaN(_date.getTime())) return -1;
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const differenceInMillis = currentTime - creationTime;
|
|
202
|
-
const differenceInSeconds = Math.floor(differenceInMillis / 1000);
|
|
203
|
-
const desiredDurationInSeconds = 24 * 60 * 60;
|
|
204
|
-
const remainingTimeInSeconds = desiredDurationInSeconds - differenceInSeconds;
|
|
205
|
-
return remainingTimeInSeconds;
|
|
205
|
+
const remainingMs = _date.getTime() - Date.now();
|
|
206
|
+
return Math.floor(remainingMs / 1000);
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
const formatTime = (seconds: number) => {
|
|
@@ -218,7 +219,7 @@ const formatTime = (seconds: number) => {
|
|
|
218
219
|
const updateRemainingTime = () => {
|
|
219
220
|
items.value.forEach((item) => {
|
|
220
221
|
const itemId = item._id as string;
|
|
221
|
-
const _time = calculateRemainingTime(item.
|
|
222
|
+
const _time = calculateRemainingTime(item.closeIn as string);
|
|
222
223
|
remainingSeconds.value[itemId] = _time;
|
|
223
224
|
remainingTime.value[itemId] = _time <= 0 ? "00h 00m" : formatTime(_time);
|
|
224
225
|
});
|
|
@@ -234,6 +235,41 @@ watchEffect(() => {
|
|
|
234
235
|
updateRemainingTime();
|
|
235
236
|
});
|
|
236
237
|
|
|
238
|
+
let _countdownInterval: ReturnType<typeof setInterval> | null = null;
|
|
239
|
+
|
|
240
|
+
onMounted(() => {
|
|
241
|
+
_countdownInterval = setInterval(updateRemainingTime, 60_000);
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const { $socket } = useNuxtApp() as any;
|
|
245
|
+
if ($socket) {
|
|
246
|
+
$socket.emit("join:cleaning-schedule", props.site);
|
|
247
|
+
|
|
248
|
+
$socket.on("cleaning-schedule:updated", (payload: { siteId: string }) => {
|
|
249
|
+
if (payload?.siteId !== props.site) return;
|
|
250
|
+
getCleanerChecklistRefresh();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
$socket.on("cleaning-schedule:expired", () => {
|
|
254
|
+
getCleanerChecklistRefresh();
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
} catch (_) {}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
onUnmounted(() => {
|
|
261
|
+
if (_countdownInterval) clearInterval(_countdownInterval);
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const { $socket } = useNuxtApp() as any;
|
|
265
|
+
if ($socket) {
|
|
266
|
+
$socket.emit("leave:cleaning-schedule", props.site);
|
|
267
|
+
$socket.off("cleaning-schedule:updated");
|
|
268
|
+
$socket.off("cleaning-schedule:expired");
|
|
269
|
+
}
|
|
270
|
+
} catch (_) {}
|
|
271
|
+
});
|
|
272
|
+
|
|
237
273
|
async function downloadItem(item: any) {
|
|
238
274
|
const id = item?._id;
|
|
239
275
|
if (!id) return;
|
|
@@ -248,6 +284,14 @@ async function downloadItem(item: any) {
|
|
|
248
284
|
}
|
|
249
285
|
}
|
|
250
286
|
|
|
287
|
+
const selectedScheduleStatus = useState<string>(
|
|
288
|
+
"selectedScheduleStatus",
|
|
289
|
+
() => ""
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
const isItemClosed = (item: any): boolean =>
|
|
293
|
+
String(item?.status ?? "").toLowerCase() === "closed";
|
|
294
|
+
|
|
251
295
|
function onRowClick(data: any) {
|
|
252
296
|
const item = data?.item ?? data;
|
|
253
297
|
const id = item?._id || item?.id || item?.areaId;
|
|
@@ -260,6 +304,9 @@ function onRowClick(data: any) {
|
|
|
260
304
|
return;
|
|
261
305
|
}
|
|
262
306
|
|
|
307
|
+
// Store status so nested pages know whether the schedule is closed
|
|
308
|
+
selectedScheduleStatus.value = String(item?.status ?? "");
|
|
309
|
+
|
|
263
310
|
if (props.type === "toilet") {
|
|
264
311
|
const path = `/${props.orgId}/${props.site}/toilet-checklist/${id}`;
|
|
265
312
|
navigateTo(path);
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-5 px-7 text-center">
|
|
4
4
|
<span> {{ promptTitle }}</span>
|
|
5
5
|
|
|
6
|
-
<
|
|
6
|
+
<div v-if="message" class="text-error mt-2">
|
|
7
7
|
{{ message }} Do you want to delete anyway?
|
|
8
|
-
</
|
|
8
|
+
</div>
|
|
9
9
|
</v-card-text>
|
|
10
10
|
|
|
11
11
|
<v-toolbar class="pa-0" density="compact">
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
<v-toolbar class="pa-0" density="compact">
|
|
21
21
|
<v-row no-gutters>
|
|
22
|
-
<v-col cols="6" class="pa-0">
|
|
22
|
+
<v-col :cols="(canUpdate || canDelete) ? 6 : 12" class="pa-0">
|
|
23
23
|
<v-btn
|
|
24
24
|
block
|
|
25
25
|
variant="text"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
</v-btn>
|
|
33
33
|
</v-col>
|
|
34
34
|
|
|
35
|
-
<v-col cols="6" class="pa-0" >
|
|
35
|
+
<v-col v-if="canUpdate || canDelete" cols="6" class="pa-0" >
|
|
36
36
|
<v-menu contained>
|
|
37
37
|
<template #activator="{ props }">
|
|
38
38
|
<v-btn
|