@7365admin1/layer-common 1.11.19 → 1.11.20

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.11.20
4
+
5
+ ### Patch Changes
6
+
7
+ - e9bd022: fix feedbacks page when refreshing or reloading the page
8
+
3
9
  ## 1.11.19
4
10
 
5
11
  ### Patch Changes
@@ -14,6 +14,22 @@
14
14
  @refresh="getAreaChecklistHistoryRefresh"
15
15
  @row-click="handleRowClick"
16
16
  >
17
+ <template #actions>
18
+ <v-row class="w-100" align="center" no-gutters>
19
+ <v-col cols="auto">
20
+ <v-btn
21
+ variant="text"
22
+ color="primary"
23
+ class="text-none"
24
+ @click="back"
25
+ >
26
+ <v-icon left>mdi-arrow-left</v-icon>
27
+ Back
28
+ </v-btn>
29
+ </v-col>
30
+ </v-row>
31
+ </template>
32
+
17
33
  <template #extension>
18
34
  <v-row no-gutters class="w-100 d-flex flex-column">
19
35
  <v-card
@@ -84,7 +100,7 @@ const props = defineProps({
84
100
  scheduleRoute: { type: String, default: "cleaning-schedule" },
85
101
  });
86
102
 
87
- const { formatDate } = useUtils();
103
+ const { formatDate, back, debounce } = useUtils();
88
104
 
89
105
  const loading = ref(false);
90
106
  const message = ref("");
@@ -160,6 +176,18 @@ watchEffect(() => {
160
176
  }
161
177
  });
162
178
 
179
+ const debouncedSearchRefresh = debounce(() => {
180
+ const wasPage = page.value;
181
+ page.value = 1;
182
+ if (wasPage === 1) {
183
+ getAreaChecklistHistoryRefresh();
184
+ }
185
+ }, 450);
186
+
187
+ watch(searchInput, () => {
188
+ debouncedSearchRefresh();
189
+ });
190
+
163
191
  function handleRowClick(data: any) {
164
192
  const item = data?.item ?? data;
165
193
  const id = item?._id || item?.id || item?.areaId;
@@ -116,7 +116,7 @@ const props = defineProps({
116
116
 
117
117
  const startDate = ref("");
118
118
  const endDate = ref("");
119
- const status = ref<TScheduleAreaStatus>("All");
119
+ const status = ref<TScheduleAreaStatus>("all");
120
120
  const statusOptions = [
121
121
  { title: "All", value: "all" },
122
122
  { title: "Open", value: "open" },
@@ -191,7 +191,7 @@ const {
191
191
  site: props.site,
192
192
  startDate: startDate.value,
193
193
  endDate: endDate.value,
194
- status: status.value === "All" ? undefined : status.value,
194
+ status: status.value === "all" ? undefined : status.value,
195
195
  serviceType: props.serviceType,
196
196
  }),
197
197
  {
@@ -1,13 +1,13 @@
1
1
  <template>
2
2
  <div class="d-flex flex-column">
3
3
  <v-text-field v-bind="$attrs" ref="dateTimePickerRef" :model-value="dateTimeFormattedReadOnly" autocomplete="off"
4
- :placeholder="placeholder" :rules="rules" style="z-index: 10" @click="openDatePicker">
4
+ :placeholder="inputPlaceholder" :rules="rules" style="z-index: 10" @click="openDatePicker">
5
5
  <template #append-inner>
6
6
  <v-icon icon="mdi-calendar" @click.stop="openDatePicker" />
7
7
  </template>
8
8
  </v-text-field>
9
9
  <div class="w-100 d-flex align-end ga-3 hidden-input">
10
- <input ref="dateInput" type="datetime-local" v-model="dateTime" />
10
+ <input ref="dateInput" :type="inputType" v-model="dateTime" />
11
11
  </div>
12
12
  </div>
13
13
  </template>
@@ -23,6 +23,10 @@ const prop = defineProps({
23
23
  placeholder: {
24
24
  type: String,
25
25
  default: 'DD/MM/YYYY, HH:MM AM/PM'
26
+ },
27
+ dateOnly: {
28
+ type: Boolean,
29
+ default: false
26
30
  }
27
31
  })
28
32
 
@@ -31,6 +35,10 @@ const dateTime = defineModel<string | null>({ default: null }) //2025-10-10T13:0
31
35
  const dateTimeUTC = defineModel<string | null>('utc', { default: null }) // UTC format
32
36
 
33
37
  const dateTimeFormattedReadOnly = ref<string | null>(null)
38
+ const inputType = computed(() => (prop.dateOnly ? 'date' : 'datetime-local'))
39
+ const inputPlaceholder = computed(() => (
40
+ prop.placeholder || (prop.dateOnly ? 'MM/DD/YYYY' : 'DD/MM/YYYY, HH:MM AM/PM')
41
+ ))
34
42
 
35
43
 
36
44
 
@@ -55,11 +63,16 @@ function convertToReadableFormat(dateStr: string): string {
55
63
  if (!dateStr) return "";
56
64
 
57
65
  const date = new Date(dateStr)
66
+ if (Number.isNaN(date.getTime())) return "";
58
67
 
59
- const day = String(date.getDate()).padStart(2, '0')
60
68
  const month = String(date.getMonth() + 1).padStart(2, '0')
69
+ const day = String(date.getDate()).padStart(2, '0')
61
70
  const year = date.getFullYear()
62
71
 
72
+ if (prop.dateOnly) {
73
+ return `${month}/${day}/${year}`
74
+ }
75
+
63
76
  let hours = date.getHours()
64
77
  const minutes = String(date.getMinutes()).padStart(2, '0')
65
78
 
@@ -72,17 +85,36 @@ function convertToReadableFormat(dateStr: string): string {
72
85
  return `${day}/${month}/${year}, ${formattedTime}`
73
86
  }
74
87
 
88
+ function toDateOnlyInputValue(dateStr: string): string {
89
+ if (!dateStr) return ''
90
+ if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) return dateStr
91
+ const date = new Date(dateStr)
92
+ if (Number.isNaN(date.getTime())) return ''
93
+ const year = date.getFullYear()
94
+ const month = String(date.getMonth() + 1).padStart(2, '0')
95
+ const day = String(date.getDate()).padStart(2, '0')
96
+ return `${year}-${month}-${day}`
97
+ }
98
+
75
99
  function handleInitialDate(){
76
100
  const dateDefault = dateTime.value
77
101
  const dateUTC = dateTimeUTC.value
78
102
  if(dateDefault){
79
103
  dateTimeFormattedReadOnly.value = convertToReadableFormat(dateDefault)
80
- const localDate = new Date(dateDefault)
81
- dateTimeUTC.value = localDate.toISOString()
104
+ if (prop.dateOnly) {
105
+ dateTimeUTC.value = toDateOnlyInputValue(dateDefault)
106
+ } else {
107
+ const localDate = new Date(dateDefault)
108
+ dateTimeUTC.value = localDate.toISOString()
109
+ }
82
110
  } else if (dateUTC){
83
111
  dateTimeFormattedReadOnly.value = convertToReadableFormat(dateUTC)
84
- const localDate = new Date(dateUTC)
85
- dateTime.value = formatDateISO8601(localDate)
112
+ if (prop.dateOnly) {
113
+ dateTime.value = toDateOnlyInputValue(dateUTC)
114
+ } else {
115
+ const localDate = new Date(dateUTC)
116
+ dateTime.value = formatDateISO8601(localDate)
117
+ }
86
118
  } else {
87
119
  dateTimeFormattedReadOnly.value = null
88
120
  }
@@ -98,8 +130,12 @@ watch(dateTime, (dateVal) => {
98
130
  }
99
131
 
100
132
  dateTimeFormattedReadOnly.value = convertToReadableFormat(dateVal)
101
- const localDate = new Date(dateVal)
102
- dateTimeUTC.value = localDate.toISOString()
133
+ if (prop.dateOnly) {
134
+ dateTimeUTC.value = toDateOnlyInputValue(dateVal)
135
+ } else {
136
+ const localDate = new Date(dateVal)
137
+ dateTimeUTC.value = localDate.toISOString()
138
+ }
103
139
 
104
140
  }, { immediate: false })
105
141
 
@@ -111,7 +111,8 @@ const isHydrating = ref(true)
111
111
 
112
112
  const { requiredRule } = useUtils()
113
113
  const { generateTimeSlots, generateTimeSlotsFromStart } = useFacilityUtils()
114
- const { updateOvernightParkingAvailability, getOvernightParkingAvailability } = useSiteSettings()
114
+ const { currentUser } = useLocalAuth()
115
+ const { getOvernightParkingAvailabilityV2, createOrUpdateOvernightParkingAvailabilityV2 } = useSiteSettings()
115
116
 
116
117
  type TDay = Exclude<keyof TOvernightParkingAvailability, 'autoApproveOvernightParking'>
117
118
  type TOvernightParkingSlot = TOvernightParkingDay & { day: TDay }
@@ -139,9 +140,11 @@ const applyDialog = reactive({
139
140
 
140
141
 
141
142
 
142
- const { data: availabilityDataReq, refresh: refreshAvailability } = await useLazyAsyncData<TOvernightParkingAvailability>(
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>>(
143
146
  `overnight-parking-availability-${props.site}`,
144
- () => getOvernightParkingAvailability(props.site)
147
+ () => getOvernightParkingAvailabilityV2(props.site, userType.value)
145
148
  )
146
149
 
147
150
  watch(availabilityDataReq, (data) => {
@@ -152,15 +155,16 @@ watch(availabilityDataReq, (data) => {
152
155
  slot.isEnabled = data?.[day]?.isEnabled ?? false
153
156
  slot.startTime = data?.[day]?.startTime ?? null
154
157
  slot.endTime = data?.[day]?.endTime ?? null
155
- setTimeout(() => {
156
- isHydrating.value = false
157
- }, 500)
158
158
  })
159
-
160
- autoApproveOvernightParking.value = data?.autoApproveOvernightParking === true
159
+ autoApproveOvernightParking.value =
160
+ data?.autoApproveOvernightParking === true || data?.isAutoApproved === true
161
+ setTimeout(() => {
162
+ isHydrating.value = false
163
+ }, 500)
161
164
  }, { immediate: true })
162
165
 
163
166
 
167
+
164
168
  const overnightParkingSlotsArray = ref<TOvernightParkingSlot[]>(
165
169
  orderedDays.map((day) => ({
166
170
  day,
@@ -240,23 +244,20 @@ async function handleSave(action: 'toggle' | 'save') {
240
244
 
241
245
  let payload = {}
242
246
 
243
- if(action === 'save') {
244
- payload = {
245
- ...model.value
246
- }
247
- } else if (action === 'toggle') {
248
- payload = {
249
- autoApproveOvernightParking: autoApproveOvernightParking.value,
250
- }
247
+ payload = {
248
+ ...model.value,
249
+ autoApproveOvernightParking: autoApproveOvernightParking.value,
250
+ isAutoApproved: autoApproveOvernightParking.value,
251
+ updatedBy: updatedByValue.value,
251
252
  }
252
253
 
253
254
  try {
254
255
  loading.updating = true
255
- await updateOvernightParkingAvailability(props.site, payload)
256
+ await createOrUpdateOvernightParkingAvailabilityV2(props.site, payload, userType.value)
257
+ refreshAvailability()
256
258
  message.value = 'Overnight parking settings updated successfully.'
257
259
  messageColor.value = 'success'
258
260
  messageSnackbar.value = true
259
- refreshAvailability()
260
261
  } catch (error) {
261
262
  message.value = 'Failed to update overnight parking settings. Please try again.'
262
263
  messageColor.value = 'error'
@@ -208,12 +208,12 @@ const isScheduleClosed = computed(
208
208
 
209
209
  const items = ref<Array<Record<string, any>>>([]);
210
210
 
211
- const statusFilter = ref<TScheduleAreaStatus>("All");
211
+ const statusFilter = ref<TScheduleAreaStatus>("all");
212
212
  const statusOptions = [
213
- { title: "All", value: "All" },
214
- { title: "Open", value: "Open" },
215
- { title: "Ongoing", value: "Ongoing" },
216
- { title: "Completed", value: "Completed" },
213
+ { title: "All", value: "all" },
214
+ { title: "Open", value: "open" },
215
+ { title: "Ongoing", value: "ongoing" },
216
+ { title: "Completed", value: "completed" },
217
217
  ];
218
218
  const areaTypeFilter = ref<TAreaType>("all");
219
219
  const typeOptions = [
@@ -235,7 +235,7 @@ const {
235
235
  getScheduleAreas({
236
236
  page: page.value,
237
237
  scheduleAreaId: props.scheduleAreaId,
238
- status: statusFilter.value === "All" ? undefined : statusFilter.value,
238
+ status: statusFilter.value === "all" ? undefined : statusFilter.value,
239
239
  type: areaTypeFilter.value === "all" ? undefined : areaTypeFilter.value,
240
240
  serviceType: props.serviceType,
241
241
  }),
@@ -20,8 +20,8 @@
20
20
  <span class="text-caption">Not Checked Out</span>
21
21
  </template>
22
22
  </v-checkbox>
23
- <InputDateTimePicker v-model:utc="dateFrom" density="compact" hide-details />
24
- <InputDateTimePicker v-model:utc="dateTo" density="compact" hide-details />
23
+ <InputDatePicker v-model="dateFrom" density="compact" hide-details />
24
+ <InputDatePicker v-model="dateTo" density="compact" hide-details />
25
25
  <v-select v-if="activeTab == 'registered'" v-model="filterTypes" label="Filter by types" item-title="label"
26
26
  item-value="value" :items="visitorSelection" density="compact" clearable multiple max-width="200"
27
27
  hide-details>
@@ -418,6 +418,19 @@ const tabOptions = [
418
418
  { name: "Resident Transactions", value: "resident-transactions" },
419
419
  ];
420
420
 
421
+ function normalizeDateOnly(value: string) {
422
+ if (!value) return "";
423
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
424
+
425
+ const parsedDate = new Date(value);
426
+ if (Number.isNaN(parsedDate.getTime())) return "";
427
+
428
+ const year = parsedDate.getFullYear();
429
+ const month = String(parsedDate.getMonth() + 1).padStart(2, "0");
430
+ const day = String(parsedDate.getDate()).padStart(2, "0");
431
+ return `${year}-${month}-${day}`;
432
+ }
433
+
421
434
  const selectedVisitorDataObject = computed(() => {
422
435
  return items.value.find((x: any) => x?._id === selectedVisitorId.value) || {};
423
436
  });
@@ -486,8 +499,8 @@ const {
486
499
  page: page.value,
487
500
  site: siteId,
488
501
  search: searchInput.value,
489
- dateTo: dateTo.value,
490
- dateFrom: dateFrom.value,
502
+ dateTo: normalizeDateOnly(dateTo.value),
503
+ dateFrom: normalizeDateOnly(dateFrom.value),
491
504
  type: (filterTypes.value.length === 0 && activeTab.value === 'registered' ? "contractor,delivery,walk-in,pick-up,drop-off,guest" : filterTypes.value.filter(Boolean).join(",")),
492
505
  checkedOut: displayNotCheckedOut.value ? false : undefined
493
506
  }
@@ -805,8 +818,13 @@ const updateRouteQuery = debounce(
805
818
  watch(
806
819
  [searchInput, dateFrom, dateTo, filterTypes, activeTab, displayNotCheckedOut],
807
820
  ([search, from, to, types, tab, checkedOut]) => {
821
+ const normalizedFrom = normalizeDateOnly(from);
822
+ const normalizedTo = normalizeDateOnly(to);
823
+ dateFrom.value = normalizedFrom;
824
+ dateTo.value = normalizedTo;
825
+
808
826
  // updateRouteQuery(search, from, to, types, status, checkedOut)
809
- updateRouteQuery({ search, from, to, types, tab, checkedOut })
827
+ updateRouteQuery({ search, from: normalizedFrom, to: normalizedTo, types, tab, checkedOut })
810
828
  getVisitorRefresh();
811
829
  },
812
830
  { deep: true }
@@ -817,8 +835,8 @@ watch(
817
835
  onMounted(() => {
818
836
  activeTab.value = (route.query.tab as string) || "unregistered"
819
837
  searchInput.value = (route.query.search as string) || "";
820
- dateFrom.value = (route.query.dateFrom as string) || "";
821
- dateTo.value = (route.query.dateTo as string) || "";
838
+ dateFrom.value = normalizeDateOnly((route.query.dateFrom as string) || "");
839
+ dateTo.value = normalizeDateOnly((route.query.dateTo as string) || "");
822
840
  filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(Boolean) as TVisitorType[];
823
841
  displayNotCheckedOut.value = (route.query.checkedOut as string) == 'true' || false
824
842
  })
@@ -100,7 +100,7 @@ export default function useFeedback() {
100
100
  status,
101
101
  provider,
102
102
  service,
103
- ...(currentUser.value.type != "site" && {
103
+ ...(currentUser.value?.type != "site" && {
104
104
  userId,
105
105
  }),
106
106
  },
@@ -140,6 +140,33 @@ export default function () {
140
140
  );
141
141
  }
142
142
 
143
+ async function getOvernightParkingAvailabilityV2(
144
+ siteId: string,
145
+ userType = "site"
146
+ ) {
147
+ return await useNuxtApp().$api<Record<string, any>>(
148
+ `/api/overnight-parking-approval-settings2/v1`,
149
+ {
150
+ method: "PATCH",
151
+ body: JSON.stringify({ site: siteId, userType }),
152
+ }
153
+ );
154
+ }
155
+
156
+ async function createOrUpdateOvernightParkingAvailabilityV2(
157
+ siteId: string,
158
+ payload: Record<string, any>,
159
+ userType = "site"
160
+ ) {
161
+ return await useNuxtApp().$api<Record<string, any>>(
162
+ `/api/overnight-parking-approval-settings2/v1`,
163
+ {
164
+ method: "POST",
165
+ body: JSON.stringify({ site: siteId, userType, ...payload }),
166
+ }
167
+ );
168
+ }
169
+
143
170
  return {
144
171
  getSiteById,
145
172
  getSiteLevels,
@@ -153,6 +180,8 @@ export default function () {
153
180
  updateSitebyId,
154
181
  updateSiteInformation,
155
182
  getOvernightParkingAvailability,
156
- updateOvernightParkingAvailability
183
+ updateOvernightParkingAvailability,
184
+ getOvernightParkingAvailabilityV2,
185
+ createOrUpdateOvernightParkingAvailabilityV2,
157
186
  };
158
187
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.11.19",
5
+ "version": "1.11.20",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {