@7365admin1/layer-common 1.11.19 → 1.11.21

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.
@@ -1,55 +1,122 @@
1
1
  <template>
2
- <v-form v-model="isInternalFormValid" ref="internalFormRef" :readonly="viewMode">
2
+ <v-form
3
+ v-model="isInternalFormValid"
4
+ ref="internalFormRef"
5
+ :readonly="viewMode"
6
+ >
3
7
  <v-row no-gutters>
4
8
  <v-col cols="12">
5
- <v-row no-gutters class="d-flex align-center ga-2 justify-space-between">
6
- <InputLabel class="text-capitalize" title="Overnight Parking Approval Hours" />
9
+ <v-row no-gutters class="d-flex align-center ga-2 justify-end">
10
+ <span class="d-flex ga-2 align-center">
11
+ <p>Allow Opening Barrier via Plate Number?</p>
12
+ <v-switch
13
+ v-model="isAllowPlateNumberOpenBarrier"
14
+ hide-details
15
+ color="success"
16
+ size="large"
17
+ />
18
+ </span>
19
+ </v-row>
20
+ <v-row
21
+ no-gutters
22
+ class="d-flex align-center ga-2 justify-space-between"
23
+ >
24
+ <InputLabel
25
+ class="text-capitalize"
26
+ title="Overnight Parking Approval Hours"
27
+ />
7
28
  <span class="d-flex ga-2 align-center">
8
29
  <p>Auto Approve Parking?</p>
9
- <v-switch v-model="autoApproveOvernightParking" hide-details color="success" size="large" />
30
+ <v-switch
31
+ v-model="autoApproveOvernightParking"
32
+ hide-details
33
+ color="success"
34
+ size="large"
35
+ />
10
36
  </span>
11
37
  </v-row>
12
38
  <v-card class="mt-2">
13
39
  <v-card-text>
14
- <template v-for="(slot, index) in overnightParkingSlotsArray" :key="slot.day">
40
+ <template
41
+ v-for="(slot, index) in overnightParkingSlotsArray"
42
+ :key="slot.day"
43
+ >
15
44
  <v-row no-gutters class="mb-4">
16
- <v-col cols="12" class="text-capitalize mb-2 text-subtitle-1 d-flex align-center justify-space-between">
45
+ <v-col
46
+ cols="12"
47
+ class="text-capitalize mb-2 text-subtitle-1 d-flex align-center justify-space-between"
48
+ >
17
49
  <span class="font-weight-black">{{ slot.day }}</span>
18
- <v-icon :icon="slot?.isEnabled ? 'mdi-check-circle' : 'mdi-check-circle-outline'" size="20"
50
+ <v-icon
51
+ :icon="
52
+ slot?.isEnabled
53
+ ? 'mdi-check-circle'
54
+ : 'mdi-check-circle-outline'
55
+ "
56
+ size="20"
19
57
  :class="{ 'cursor-pointer': !viewMode }"
20
- :color="slot.isEnabled ? 'green-darken-2' : 'black-lighten-1'"
21
- @click="!viewMode && toggleEnabled(slot.day)" />
58
+ :color="
59
+ slot.isEnabled ? 'green-darken-2' : 'black-lighten-1'
60
+ "
61
+ @click="!viewMode && toggleEnabled(slot.day)"
62
+ />
22
63
  </v-col>
23
64
 
24
65
  <v-col cols="6" class="pr-2">
25
66
  <InputLabel title="Start" :required="slot.isEnabled" />
26
- <v-select v-model="slot.startTime" :disabled="!slot.isEnabled" :items="allTimeSlots" item-value="time"
27
- item-title="time" density="comfortable" :rules="slot.isEnabled ? [requiredRule] : []"
28
- @update:model-value="slot.endTime = null" />
67
+ <v-select
68
+ v-model="slot.startTime"
69
+ :disabled="!slot.isEnabled"
70
+ :items="allTimeSlots"
71
+ item-value="time"
72
+ item-title="time"
73
+ density="comfortable"
74
+ :rules="slot.isEnabled ? [requiredRule] : []"
75
+ @update:model-value="slot.endTime = null"
76
+ />
29
77
  </v-col>
30
78
 
31
79
  <v-col cols="6" class="pl-2">
32
80
  <InputLabel title="End" :required="slot.isEnabled" />
33
- <v-select v-model="slot.endTime" :disabled="!slot.isEnabled"
34
- :items="generateTimeSlots(0.5, slot.startTime ?? undefined)" item-value="time" item-title="time"
35
- density="comfortable" :rules="slot.isEnabled ? [requiredRule] : []" />
81
+ <v-select
82
+ v-model="slot.endTime"
83
+ :disabled="!slot.isEnabled"
84
+ :items="generateTimeSlots(0.5, slot.startTime ?? undefined)"
85
+ item-value="time"
86
+ item-title="time"
87
+ density="comfortable"
88
+ :rules="slot.isEnabled ? [requiredRule] : []"
89
+ />
36
90
  </v-col>
37
91
 
38
92
  <v-col v-if="!viewMode" cols="12" class="mt-1">
39
- <div class="d-flex align-center ga-1 cursor-pointer text-caption text-medium-emphasis"
40
- @click="applyToOtherDays(slot.day)">
93
+ <div
94
+ class="d-flex align-center ga-1 cursor-pointer text-caption text-medium-emphasis"
95
+ @click="applyToOtherDays(slot.day)"
96
+ >
41
97
  <v-icon icon="mdi-content-copy" size="14" />
42
98
  <span>Apply to other days</span>
43
99
  </div>
44
100
  </v-col>
45
101
  </v-row>
46
102
 
47
- <v-divider v-if="index < overnightParkingSlotsArray.length - 1" class="mb-4" />
103
+ <v-divider
104
+ v-if="index < overnightParkingSlotsArray.length - 1"
105
+ class="mb-4"
106
+ />
48
107
  </template>
49
108
 
50
109
  <v-col cols="12" align="end">
51
- <v-btn color="primary" class="text-none mt-2" size="large" :loading="loading.updating" variant="flat"
52
- text="Save" :disabled="viewMode || !isValid" @click="handleSave('save')" />
110
+ <v-btn
111
+ color="primary"
112
+ class="text-none mt-2"
113
+ size="large"
114
+ :loading="loading.updating"
115
+ variant="flat"
116
+ text="Save"
117
+ :disabled="viewMode || !isValid"
118
+ @click="handleSave('save')"
119
+ />
53
120
  </v-col>
54
121
  </v-card-text>
55
122
  </v-card>
@@ -57,24 +124,46 @@
57
124
  </v-row>
58
125
  </v-form>
59
126
 
60
- <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" style="z-index: 3000;" />
127
+ <Snackbar
128
+ v-model="messageSnackbar"
129
+ :text="message"
130
+ :color="messageColor"
131
+ style="z-index: 3000"
132
+ />
61
133
 
62
134
  <v-dialog v-model="applyDialog.open" max-width="360" persistent>
63
135
  <v-card>
64
- <v-card-title class="text-subtitle-1 font-weight-bold pt-4 px-4">Apply to days</v-card-title>
136
+ <v-card-title class="text-subtitle-1 font-weight-bold pt-4 px-4"
137
+ >Apply to days</v-card-title
138
+ >
65
139
  <v-card-text class="px-4 pb-2">
66
140
  <p class="text-caption text-medium-emphasis mb-3">
67
- Select the days to apply <span class="font-weight-bold text-capitalize">{{ applyDialog.sourceDay }}</span>'s
68
- hours to.
141
+ Select the days to apply
142
+ <span class="font-weight-bold text-capitalize">{{
143
+ applyDialog.sourceDay
144
+ }}</span
145
+ >'s hours to.
69
146
  </p>
70
- <v-checkbox v-for="day in applyDialog.targetDays" :key="day.day" v-model="day.selected" :label="day.day"
71
- density="compact" hide-details class="text-capitalize" />
147
+ <v-checkbox
148
+ v-for="day in applyDialog.targetDays"
149
+ :key="day.day"
150
+ v-model="day.selected"
151
+ :label="day.day"
152
+ density="compact"
153
+ hide-details
154
+ class="text-capitalize"
155
+ />
72
156
  </v-card-text>
73
157
  <v-card-actions class="px-4 pb-4">
74
158
  <v-spacer />
75
159
  <v-btn variant="text" @click="applyDialog.open = false">Cancel</v-btn>
76
- <v-btn color="primary" variant="flat" :disabled="!applyDialog.targetDays.some(d => d.selected)"
77
- @click="confirmApply">Apply</v-btn>
160
+ <v-btn
161
+ color="primary"
162
+ variant="flat"
163
+ :disabled="!applyDialog.targetDays.some((d) => d.selected)"
164
+ @click="confirmApply"
165
+ >Apply</v-btn
166
+ >
78
167
  </v-card-actions>
79
168
  </v-card>
80
169
  </v-dialog>
@@ -91,9 +180,8 @@ const props = defineProps({
91
180
  type: String,
92
181
  default: "",
93
182
  required: false,
94
- }
95
- })
96
-
183
+ },
184
+ });
97
185
 
98
186
  const model = ref<TOvernightParkingAvailability>({
99
187
  monday: { isEnabled: false, startTime: null, endTime: null },
@@ -103,63 +191,88 @@ const model = ref<TOvernightParkingAvailability>({
103
191
  friday: { isEnabled: false, startTime: null, endTime: null },
104
192
  saturday: { isEnabled: false, startTime: null, endTime: null },
105
193
  sunday: { isEnabled: false, startTime: null, endTime: null },
106
- })
107
-
108
-
109
- const autoApproveOvernightParking = ref(false)
110
- const isHydrating = ref(true)
111
-
112
- const { requiredRule } = useUtils()
113
- const { generateTimeSlots, generateTimeSlotsFromStart } = useFacilityUtils()
114
- const { updateOvernightParkingAvailability, getOvernightParkingAvailability } = useSiteSettings()
115
-
116
- type TDay = Exclude<keyof TOvernightParkingAvailability, 'autoApproveOvernightParking'>
117
- type TOvernightParkingSlot = TOvernightParkingDay & { day: TDay }
118
-
119
- const orderedDays: TDay[] = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
120
-
121
- const allTimeSlots = computed(() => generateTimeSlotsFromStart(0.5))
122
-
123
- const isInternalFormValid = ref(false)
124
- const internalFormRef = ref<InstanceType<typeof import('vuetify/components').VForm>>()
125
-
126
- const messageSnackbar = ref(false)
127
- const message = ref('')
128
- const messageColor = ref<'success' | 'error'>()
194
+ });
195
+
196
+ const isAllowPlateNumberOpenBarrier = ref(false);
197
+ const autoApproveOvernightParking = ref(false);
198
+ const isHydrating = ref(true);
199
+
200
+ const { requiredRule } = useUtils();
201
+ const { generateTimeSlots, generateTimeSlotsFromStart } = useFacilityUtils();
202
+ const { currentUser } = useLocalAuth();
203
+ const {
204
+ getOvernightParkingAvailabilityV2,
205
+ createOrUpdateOvernightParkingAvailabilityV2,
206
+ } = useSiteSettings();
207
+
208
+ type TDay = Exclude<
209
+ keyof TOvernightParkingAvailability,
210
+ "autoApproveOvernightParking"
211
+ >;
212
+ type TOvernightParkingSlot = TOvernightParkingDay & { day: TDay };
213
+
214
+ const orderedDays: TDay[] = [
215
+ "monday",
216
+ "tuesday",
217
+ "wednesday",
218
+ "thursday",
219
+ "friday",
220
+ "saturday",
221
+ "sunday",
222
+ ];
223
+
224
+ const allTimeSlots = computed(() => generateTimeSlotsFromStart(0.5));
225
+
226
+ const isInternalFormValid = ref(false);
227
+ const internalFormRef =
228
+ ref<InstanceType<typeof import("vuetify/components").VForm>>();
229
+
230
+ const messageSnackbar = ref(false);
231
+ const message = ref("");
232
+ const messageColor = ref<"success" | "error">();
129
233
 
130
234
  const loading = reactive({
131
- updating: false
132
- })
235
+ updating: false,
236
+ });
133
237
 
134
238
  const applyDialog = reactive({
135
239
  open: false,
136
- sourceDay: '' as TDay,
240
+ sourceDay: "" as TDay,
137
241
  targetDays: [] as { day: TDay; selected: boolean }[],
138
- })
139
-
140
-
141
-
142
- const { data: availabilityDataReq, refresh: refreshAvailability } = await useLazyAsyncData<TOvernightParkingAvailability>(
143
- `overnight-parking-availability-${props.site}`,
144
- () => getOvernightParkingAvailability(props.site)
145
- )
146
-
147
- watch(availabilityDataReq, (data) => {
148
- if (!data) return
149
- orderedDays.forEach((day) => {
150
- const slot = overnightParkingSlotsArray.value.find((s) => s.day === day)
151
- if (!slot) return
152
- slot.isEnabled = data?.[day]?.isEnabled ?? false
153
- slot.startTime = data?.[day]?.startTime ?? null
154
- slot.endTime = data?.[day]?.endTime ?? null
155
- setTimeout(() => {
156
- isHydrating.value = false
157
- }, 500)
158
- })
159
-
160
- autoApproveOvernightParking.value = data?.autoApproveOvernightParking === true
161
- }, { immediate: true })
162
-
242
+ });
243
+
244
+ const userType = computed(() => currentUser.value?.type ?? "site");
245
+ const updatedByValue = computed(
246
+ () => currentUser.value?._id ?? currentUser.value?.email ?? "system"
247
+ );
248
+ const { data: availabilityDataReq, refresh: refreshAvailability } =
249
+ await useLazyAsyncData<Record<string, any>>(
250
+ `overnight-parking-availability-${props.site}`,
251
+ () => getOvernightParkingAvailabilityV2(props.site, userType.value)
252
+ );
253
+
254
+ watch(
255
+ availabilityDataReq,
256
+ (data) => {
257
+ if (!data) return;
258
+ orderedDays.forEach((day) => {
259
+ const slot = overnightParkingSlotsArray.value.find((s) => s.day === day);
260
+ if (!slot) return;
261
+ slot.isEnabled = data?.[day]?.isEnabled ?? false;
262
+ slot.startTime = data?.[day]?.startTime ?? null;
263
+ slot.endTime = data?.[day]?.endTime ?? null;
264
+ });
265
+ isAllowPlateNumberOpenBarrier.value =
266
+ data?.isAllowPlateNumberOpenBarrier === true;
267
+ autoApproveOvernightParking.value =
268
+ data?.autoApproveOvernightParking === true ||
269
+ data?.isAutoApproved === true;
270
+ setTimeout(() => {
271
+ isHydrating.value = false;
272
+ }, 500);
273
+ },
274
+ { immediate: true }
275
+ );
163
276
 
164
277
  const overnightParkingSlotsArray = ref<TOvernightParkingSlot[]>(
165
278
  orderedDays.map((day) => ({
@@ -168,105 +281,128 @@ const overnightParkingSlotsArray = ref<TOvernightParkingSlot[]>(
168
281
  startTime: model.value?.[day]?.startTime ?? null,
169
282
  endTime: model.value?.[day]?.endTime ?? null,
170
283
  }))
171
- )
172
-
173
- watch(overnightParkingSlotsArray, (slots) => {
174
- const updated = {} as TOvernightParkingAvailability
175
- slots.forEach((slot) => {
176
- updated[slot.day] = {
177
- isEnabled: slot.isEnabled,
178
- startTime: slot.startTime,
179
- endTime: slot.endTime,
180
- }
181
- })
182
- model.value = updated
183
- }, { deep: true })
284
+ );
285
+
286
+ watch(
287
+ overnightParkingSlotsArray,
288
+ (slots) => {
289
+ const updated = {} as TOvernightParkingAvailability;
290
+ slots.forEach((slot) => {
291
+ updated[slot.day] = {
292
+ isEnabled: slot.isEnabled,
293
+ startTime: slot.startTime,
294
+ endTime: slot.endTime,
295
+ };
296
+ });
297
+ model.value = updated;
298
+ },
299
+ { deep: true }
300
+ );
184
301
 
185
302
  function toggleEnabled(day: TDay) {
186
- const slot = overnightParkingSlotsArray.value.find((s) => s.day === day)
187
- if (!slot) return
188
- internalFormRef.value?.resetValidation()
189
- slot.isEnabled = !slot.isEnabled
303
+ const slot = overnightParkingSlotsArray.value.find((s) => s.day === day);
304
+ if (!slot) return;
305
+ internalFormRef.value?.resetValidation();
306
+ slot.isEnabled = !slot.isEnabled;
190
307
  }
191
308
 
192
309
  function applyToOtherDays(sourceDay: TDay) {
193
- applyDialog.sourceDay = sourceDay
310
+ applyDialog.sourceDay = sourceDay;
194
311
  applyDialog.targetDays = orderedDays
195
312
  .filter((d) => d !== sourceDay)
196
- .map((d) => ({ day: d, selected: false }))
197
- applyDialog.open = true
313
+ .map((d) => ({ day: d, selected: false }));
314
+ applyDialog.open = true;
198
315
  }
199
316
 
200
317
  function confirmApply() {
201
- const source = overnightParkingSlotsArray.value.find((s) => s.day === applyDialog.sourceDay)
202
- if (!source) return
203
- const selectedDays = applyDialog.targetDays.filter((d) => d.selected).map((d) => d.day)
318
+ const source = overnightParkingSlotsArray.value.find(
319
+ (s) => s.day === applyDialog.sourceDay
320
+ );
321
+ if (!source) return;
322
+ const selectedDays = applyDialog.targetDays
323
+ .filter((d) => d.selected)
324
+ .map((d) => d.day);
204
325
  overnightParkingSlotsArray.value.forEach((slot) => {
205
326
  if (selectedDays.includes(slot.day)) {
206
- slot.startTime = source.startTime
207
- slot.endTime = source.endTime
208
- slot.isEnabled = source.isEnabled
327
+ slot.startTime = source.startTime;
328
+ slot.endTime = source.endTime;
329
+ slot.isEnabled = source.isEnabled;
209
330
  }
210
- })
211
- applyDialog.open = false
212
- message.value = `Applied ${applyDialog.sourceDay}'s hours to ${selectedDays.join(', ')}.`
213
- messageColor.value = 'success'
214
- messageSnackbar.value = true
331
+ });
332
+ applyDialog.open = false;
333
+ message.value = `Applied ${
334
+ applyDialog.sourceDay
335
+ }'s hours to ${selectedDays.join(", ")}.`;
336
+ messageColor.value = "success";
337
+ messageSnackbar.value = true;
215
338
  }
216
339
 
217
340
  const isValid = computed(() => {
218
- const slots = overnightParkingSlotsArray.value
219
- const enabledSlots = slots.filter((s) => s.isEnabled)
341
+ const slots = overnightParkingSlotsArray.value;
342
+ const enabledSlots = slots.filter((s) => s.isEnabled);
220
343
  const isAllSlotsValid = slots.every((s) => {
221
- if (!s.isEnabled) return true
222
- return !!s.startTime?.trim() && !!s.endTime?.trim()
223
- })
224
- return isAllSlotsValid && enabledSlots.length > 0
225
- })
226
-
227
- watch(autoApproveOvernightParking, (newValue, oldValue) => {
228
- if (oldValue === newValue || isHydrating.value) return
229
- handleSave('toggle')
230
- }, { immediate: false })
231
-
232
-
233
- async function handleSave(action: 'toggle' | 'save') {
234
- if (!isValid.value && action === 'save') {
235
- message.value = 'Please fill in all required fields.'
236
- messageColor.value = 'error'
237
- messageSnackbar.value = true
238
- return
344
+ if (!s.isEnabled) return true;
345
+ return !!s.startTime?.trim() && !!s.endTime?.trim();
346
+ });
347
+ return isAllSlotsValid && enabledSlots.length > 0;
348
+ });
349
+
350
+ watch(
351
+ isAllowPlateNumberOpenBarrier,
352
+ (newValue, oldValue) => {
353
+ if (oldValue === newValue || isHydrating.value) return;
354
+ handleSave("toggle");
355
+ },
356
+ { immediate: false }
357
+ );
358
+
359
+ watch(
360
+ autoApproveOvernightParking,
361
+ (newValue, oldValue) => {
362
+ if (oldValue === newValue || isHydrating.value) return;
363
+ handleSave("toggle");
364
+ },
365
+ { immediate: false }
366
+ );
367
+
368
+ async function handleSave(action: "toggle" | "save") {
369
+ if (!isValid.value && action === "save") {
370
+ message.value = "Please fill in all required fields.";
371
+ messageColor.value = "error";
372
+ messageSnackbar.value = true;
373
+ return;
239
374
  }
240
375
 
241
- let payload = {}
376
+ let payload = {};
242
377
 
243
- if(action === 'save') {
244
- payload = {
245
- ...model.value
246
- }
247
- } else if (action === 'toggle') {
248
- payload = {
249
- autoApproveOvernightParking: autoApproveOvernightParking.value,
250
- }
251
- }
378
+ payload = {
379
+ ...model.value,
380
+ isAllowPlateNumberOpenBarrier: isAllowPlateNumberOpenBarrier.value,
381
+ autoApproveOvernightParking: autoApproveOvernightParking.value,
382
+ isAutoApproved: autoApproveOvernightParking.value,
383
+ updatedBy: updatedByValue.value,
384
+ };
252
385
 
253
386
  try {
254
- loading.updating = true
255
- await updateOvernightParkingAvailability(props.site, payload)
256
- message.value = 'Overnight parking settings updated successfully.'
257
- messageColor.value = 'success'
258
- messageSnackbar.value = true
259
- refreshAvailability()
387
+ loading.updating = true;
388
+ await createOrUpdateOvernightParkingAvailabilityV2(
389
+ props.site,
390
+ payload,
391
+ userType.value
392
+ );
393
+ refreshAvailability();
394
+ message.value = "Overnight parking settings updated successfully.";
395
+ messageColor.value = "success";
396
+ messageSnackbar.value = true;
260
397
  } catch (error) {
261
- message.value = 'Failed to update overnight parking settings. Please try again.'
262
- messageColor.value = 'error'
263
- messageSnackbar.value = true
398
+ message.value =
399
+ "Failed to update overnight parking settings. Please try again.";
400
+ messageColor.value = "error";
401
+ messageSnackbar.value = true;
264
402
  } finally {
265
- loading.updating = false
403
+ loading.updating = false;
266
404
  }
267
-
268
405
  }
269
-
270
406
  </script>
271
407
 
272
408
  <style scoped></style>