@7365admin1/layer-common 1.11.20 → 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,67 +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 { currentUser } = useLocalAuth()
115
- const { getOvernightParkingAvailabilityV2, createOrUpdateOvernightParkingAvailabilityV2 } = useSiteSettings()
116
-
117
- type TDay = Exclude<keyof TOvernightParkingAvailability, 'autoApproveOvernightParking'>
118
- type TOvernightParkingSlot = TOvernightParkingDay & { day: TDay }
119
-
120
- const orderedDays: TDay[] = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
121
-
122
- const allTimeSlots = computed(() => generateTimeSlotsFromStart(0.5))
123
-
124
- const isInternalFormValid = ref(false)
125
- const internalFormRef = ref<InstanceType<typeof import('vuetify/components').VForm>>()
126
-
127
- const messageSnackbar = ref(false)
128
- const message = ref('')
129
- 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">();
130
233
 
131
234
  const loading = reactive({
132
- updating: false
133
- })
235
+ updating: false,
236
+ });
134
237
 
135
238
  const applyDialog = reactive({
136
239
  open: false,
137
- sourceDay: '' as TDay,
240
+ sourceDay: "" as TDay,
138
241
  targetDays: [] as { day: TDay; selected: boolean }[],
139
- })
140
-
141
-
142
-
143
- const userType = computed(() => currentUser.value?.type ?? "site")
144
- const updatedByValue = computed(() => currentUser.value?._id ?? currentUser.value?.email ?? "system")
145
- const { data: availabilityDataReq, refresh: refreshAvailability } = await useLazyAsyncData<Record<string, any>>(
146
- `overnight-parking-availability-${props.site}`,
147
- () => getOvernightParkingAvailabilityV2(props.site, userType.value)
148
- )
149
-
150
- watch(availabilityDataReq, (data) => {
151
- if (!data) return
152
- orderedDays.forEach((day) => {
153
- const slot = overnightParkingSlotsArray.value.find((s) => s.day === day)
154
- if (!slot) return
155
- slot.isEnabled = data?.[day]?.isEnabled ?? false
156
- slot.startTime = data?.[day]?.startTime ?? null
157
- slot.endTime = data?.[day]?.endTime ?? null
158
- })
159
- autoApproveOvernightParking.value =
160
- data?.autoApproveOvernightParking === true || data?.isAutoApproved === true
161
- setTimeout(() => {
162
- isHydrating.value = false
163
- }, 500)
164
- }, { immediate: true })
165
-
166
-
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
+ );
167
276
 
168
277
  const overnightParkingSlotsArray = ref<TOvernightParkingSlot[]>(
169
278
  orderedDays.map((day) => ({
@@ -172,102 +281,128 @@ const overnightParkingSlotsArray = ref<TOvernightParkingSlot[]>(
172
281
  startTime: model.value?.[day]?.startTime ?? null,
173
282
  endTime: model.value?.[day]?.endTime ?? null,
174
283
  }))
175
- )
176
-
177
- watch(overnightParkingSlotsArray, (slots) => {
178
- const updated = {} as TOvernightParkingAvailability
179
- slots.forEach((slot) => {
180
- updated[slot.day] = {
181
- isEnabled: slot.isEnabled,
182
- startTime: slot.startTime,
183
- endTime: slot.endTime,
184
- }
185
- })
186
- model.value = updated
187
- }, { 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
+ );
188
301
 
189
302
  function toggleEnabled(day: TDay) {
190
- const slot = overnightParkingSlotsArray.value.find((s) => s.day === day)
191
- if (!slot) return
192
- internalFormRef.value?.resetValidation()
193
- 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;
194
307
  }
195
308
 
196
309
  function applyToOtherDays(sourceDay: TDay) {
197
- applyDialog.sourceDay = sourceDay
310
+ applyDialog.sourceDay = sourceDay;
198
311
  applyDialog.targetDays = orderedDays
199
312
  .filter((d) => d !== sourceDay)
200
- .map((d) => ({ day: d, selected: false }))
201
- applyDialog.open = true
313
+ .map((d) => ({ day: d, selected: false }));
314
+ applyDialog.open = true;
202
315
  }
203
316
 
204
317
  function confirmApply() {
205
- const source = overnightParkingSlotsArray.value.find((s) => s.day === applyDialog.sourceDay)
206
- if (!source) return
207
- 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);
208
325
  overnightParkingSlotsArray.value.forEach((slot) => {
209
326
  if (selectedDays.includes(slot.day)) {
210
- slot.startTime = source.startTime
211
- slot.endTime = source.endTime
212
- slot.isEnabled = source.isEnabled
327
+ slot.startTime = source.startTime;
328
+ slot.endTime = source.endTime;
329
+ slot.isEnabled = source.isEnabled;
213
330
  }
214
- })
215
- applyDialog.open = false
216
- message.value = `Applied ${applyDialog.sourceDay}'s hours to ${selectedDays.join(', ')}.`
217
- messageColor.value = 'success'
218
- 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;
219
338
  }
220
339
 
221
340
  const isValid = computed(() => {
222
- const slots = overnightParkingSlotsArray.value
223
- const enabledSlots = slots.filter((s) => s.isEnabled)
341
+ const slots = overnightParkingSlotsArray.value;
342
+ const enabledSlots = slots.filter((s) => s.isEnabled);
224
343
  const isAllSlotsValid = slots.every((s) => {
225
- if (!s.isEnabled) return true
226
- return !!s.startTime?.trim() && !!s.endTime?.trim()
227
- })
228
- return isAllSlotsValid && enabledSlots.length > 0
229
- })
230
-
231
- watch(autoApproveOvernightParking, (newValue, oldValue) => {
232
- if (oldValue === newValue || isHydrating.value) return
233
- handleSave('toggle')
234
- }, { immediate: false })
235
-
236
-
237
- async function handleSave(action: 'toggle' | 'save') {
238
- if (!isValid.value && action === 'save') {
239
- message.value = 'Please fill in all required fields.'
240
- messageColor.value = 'error'
241
- messageSnackbar.value = true
242
- 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;
243
374
  }
244
375
 
245
- let payload = {}
376
+ let payload = {};
246
377
 
247
378
  payload = {
248
379
  ...model.value,
380
+ isAllowPlateNumberOpenBarrier: isAllowPlateNumberOpenBarrier.value,
249
381
  autoApproveOvernightParking: autoApproveOvernightParking.value,
250
382
  isAutoApproved: autoApproveOvernightParking.value,
251
383
  updatedBy: updatedByValue.value,
252
- }
384
+ };
253
385
 
254
386
  try {
255
- loading.updating = true
256
- await createOrUpdateOvernightParkingAvailabilityV2(props.site, payload, userType.value)
257
- refreshAvailability()
258
- message.value = 'Overnight parking settings updated successfully.'
259
- messageColor.value = 'success'
260
- messageSnackbar.value = true
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;
261
397
  } catch (error) {
262
- message.value = 'Failed to update overnight parking settings. Please try again.'
263
- messageColor.value = 'error'
264
- messageSnackbar.value = true
398
+ message.value =
399
+ "Failed to update overnight parking settings. Please try again.";
400
+ messageColor.value = "error";
401
+ messageSnackbar.value = true;
265
402
  } finally {
266
- loading.updating = false
403
+ loading.updating = false;
267
404
  }
268
-
269
405
  }
270
-
271
406
  </script>
272
407
 
273
408
  <style scoped></style>