@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.
@@ -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 Settings
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-col cols="12">
14
- <v-row no-gutters>
15
- <v-col cols="12">
16
- <span class="text-h4 font-weight-bold"> Site Settings </span>
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
- <v-col cols="12">
20
- <v-row no-gutters>
21
- <v-col cols="12" lg="4" class="mt-2">
22
- <NumberSettingField
23
- v-model="blocks"
24
- title="No. of blocks"
25
- type="blocks"
26
- :read-only="!canManageSiteSettings"
27
- :existing-block-number="existingBlockNumber"
28
- :site-id="siteId"
29
- :disabled="existingBlockNumber === blocks"
30
- @success="refreshSiteData"
31
- />
32
- </v-col>
33
- </v-row>
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
- v-model="guardPosts"
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-row no-gutters>
57
- <v-form v-model="gracePeriodValid">
58
- <v-row>
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-col>
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
- :site="siteId"
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
- v-model="prefix"
143
- label="Prefix"
144
- :disabled="isLoading || !canManageSiteSettings"
145
- @input="handleInput"
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
- :site="siteId"
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 { getSiteById, updateSitebyId } = useSiteSettings();
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
- if (!val) return;
246
-
247
- blocks.value = val.metadata?.block || 0;
248
- guardPosts.value = val.metadata?.guardPosts || 0;
249
- gracePeriod.value = val.metadata?.gracePeriod || 0;
250
-
251
- existingBlockNumber.value = blocks.value;
252
- existingGuardPostNumber.value = guardPosts.value;
253
- existingGracePeriodNumber.value = gracePeriod.value;
254
- deliveryCompanies.value = Array.isArray(val.deliveryCompanyList)
255
- ? [...val.deliveryCompanyList]
256
- : [];
257
-
258
- uploadedSiteLogo.value = val.metadata?.incidentLogo || "";
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.prefix;
307
- noOfDigits.value = workOrderSetting.value.noOfDigits;
322
+ prefix.value = workOrderSetting.value?.prefix;
323
+ noOfDigits.value = workOrderSetting.value?.noOfDigits;
308
324
  }
309
325
  });
310
326