@iservice365/layer-common 1.2.0 → 1.3.0

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,20 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 88b8014: ## What's Changed
8
+
9
+ - 20089 add edit guest feature by @aguirremac17 in https://github.com/Seven365-Pte-Ltd/iservice365-layer-common/pull/156
10
+ - chore: remove deltefile integration in frontend by @aguirremac17 in https://github.com/Seven365-Pte-Ltd/iservice365-layer-common/pull/157
11
+ - Work Order subject form update by @Jerome7365 in https://github.com/Seven365-Pte-Ltd/iservice365-layer-common/pull/158
12
+ - chore: update query key to status by @aguirremac17 in https://github.com/Seven365-Pte-Ltd/iservice365-layer-common/pull/159
13
+ - 20114 update key type to status in visitor management by @aguirremac17 in https://github.com/Seven365-Pte-Ltd/iservice365-layer-common/pull/160
14
+ - Update Work Order Component And Composable by @Jerome7365 in https://github.com/Seven365-Pte-Ltd/iservice365-layer-common/pull/161
15
+
16
+ **Full Changelog**: https://github.com/Seven365-Pte-Ltd/iservice365-layer-common/compare/v1.2.0...1.3.0
17
+
3
18
  ## 1.2.0
4
19
 
5
20
  ### Minor Changes
@@ -97,15 +97,16 @@ watch(dateTime, (dateVal) => {
97
97
 
98
98
  }, { immediate: false })
99
99
 
100
+ watch(dateTimeUTC, () => {
101
+ handleInitialDate()
102
+ }, { immediate: true})
100
103
 
101
- onMounted(async () => {
102
104
 
103
- handleInitialDate()
105
+ onMounted(async () => {
104
106
  await nextTick()
105
107
  isInitialLoad.value = false
106
-
107
108
  // Wait until Vuetify renders its internal input
108
- const nativeInput = dateTimePickerRef.value?.$el?.querySelector('input')
109
+ const nativeInput = (dateTimePickerRef.value as any)?.$el?.querySelector('input')
109
110
  if (nativeInput) {
110
111
  nativeInput.addEventListener('click', (e: MouseEvent) => {
111
112
  e.stopPropagation()
@@ -74,11 +74,6 @@ async function handleRemove(removedFile: File) {
74
74
 
75
75
  idsArray.value = filesCollection.value.map((x) => x.id)
76
76
 
77
- try {
78
- await deleteFile(removedItem.id)
79
- } catch (err) {
80
- console.warn('Failed to delete file', err)
81
- }
82
77
  }
83
78
 
84
79
  async function handleUpdateValue(value: File[]) {
@@ -69,7 +69,6 @@ const currentMask = computed(() => {
69
69
 
70
70
 
71
71
  const validatePhone = (value: string): boolean | string => {
72
- console.log('value', value)
73
72
  if (!value) {
74
73
  errorMessage.value = ''
75
74
  return true;
@@ -7,6 +7,8 @@
7
7
  :counter="maxlength"
8
8
  @input="onInput"
9
9
  outlined
10
+ :disabled="disabled"
11
+ :readonly="readonly"
10
12
  clearable
11
13
  />
12
14
  </template>
@@ -25,6 +27,12 @@ const props = defineProps({
25
27
  maxlength: {
26
28
  type: [Number, String],
27
29
  default: false
30
+ },
31
+ disabled: {
32
+ type: Boolean,
33
+ },
34
+ readonly: {
35
+ type: Boolean,
28
36
  }
29
37
  })
30
38
 
@@ -3,7 +3,7 @@
3
3
  <v-toolbar>
4
4
  <v-row no-gutters class="fill-height px-6" align="center">
5
5
  <span class="font-weight-bold text-h5 text-capitalize">
6
- {{ prop.mode }} {{ prop.type === 'visitor' ? 'Guest' : prop.type }}
6
+ {{ prop.mode }} {{ prop.type === 'visitor' ? 'Guest' : prop.type }}
7
7
  </span>
8
8
  </v-row>
9
9
  </v-toolbar>
@@ -34,15 +34,14 @@
34
34
  <v-row>
35
35
  <v-col cols="12">
36
36
  <InputLabel class="text-capitalize" title="NRIC/Passport/ID No." />
37
- <InputNRICNumber v-model.trim="form.nric" density="comfortable" />
37
+ <InputNRICNumber v-model.trim="form.nric" density="comfortable" />
38
38
  </v-col>
39
39
  </v-row>
40
40
  </v-col>
41
41
 
42
42
  <v-col cols="12">
43
43
  <InputLabel class="text-capitalize" title="Phone Number" required />
44
- <InputPhoneNumberV2 v-model="form.contact"
45
- density="comfortable" :rules="[requiredRule]" />
44
+ <InputPhoneNumberV2 v-model="form.contact" density="comfortable" :rules="[requiredRule]" />
46
45
  </v-col>
47
46
 
48
47
  <v-col cols="12">
@@ -65,19 +64,18 @@
65
64
 
66
65
  <v-col cols="12">
67
66
  <InputLabel class="text-capitalize" title="Unit" required />
68
- <v-select v-model="form.unit" :items="unitsArray" @update:model-value="handleUpdateUnit" density="comfortable" :disabled="!form.level"
69
- :loading="unitsStatus === 'pending'" :rules="[requiredRule]" />
67
+ <v-select v-model="form.unit" :items="unitsArray" @update:model-value="handleUpdateUnit"
68
+ density="comfortable" :disabled="!form.level" :loading="unitsStatus === 'pending'"
69
+ :rules="[requiredRule]" />
70
70
  </v-col>
71
71
 
72
72
  <v-col cols="12">
73
73
  <InputLabel class="text-capitalize" title="Start Date" required />
74
- <InputDateTimePicker v-model:utc="form.start" ref="startDateRef"
75
- :rules="[validStartDateRule]" />
74
+ <InputDateTimePicker v-model:utc="form.start" ref="startDateRef" name="start_date" :rules="[validStartDateRule]" />
76
75
  </v-col>
77
76
  <v-col cols="12">
78
77
  <InputLabel class="text-capitalize" title="End Date" required />
79
- <InputDateTimePicker v-model:utc="form.end" ref="endDateRef"
80
- :rules="[validExpiryDateRule]" />
78
+ <InputDateTimePicker v-model:utc="form.end" ref="endDateRef" name="end_date" :rules="[validExpiryDateRule]" />
81
79
  </v-col>
82
80
 
83
81
  <v-col cols="12">
@@ -144,12 +142,20 @@ const prop = defineProps({
144
142
  type: {
145
143
  type: String as PropType<TPeopleType>,
146
144
  required: true
145
+ },
146
+ activeId: {
147
+ type: String as PropType<string | null>, // id of person you are trying to update
148
+ default: null
149
+ },
150
+ people: {
151
+ type: Object as PropType<TPeople>,
152
+ required: false
147
153
  }
148
154
  });
149
155
 
150
156
  const { requiredRule, formatDateISO8601 } = useUtils();
151
157
  const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
152
- const { create } = usePeople();
158
+ const { create, updateById } = usePeople();
153
159
 
154
160
  const emit = defineEmits(['back', 'select', 'done', 'done:more', 'error', 'close']);
155
161
 
@@ -164,7 +170,7 @@ const form = reactive<Partial<TPeoplePayload>>({
164
170
  unit: "",
165
171
  unitName: "",
166
172
  remarks: "",
167
- start: dateToday() || "",
173
+ start: "",
168
174
  end: "",
169
175
  type: prop.type
170
176
  })
@@ -199,21 +205,21 @@ const contractorTypes = [
199
205
 
200
206
  const { data: siteData, refresh: refreshSiteData, status: blockStatus } = useLazyAsyncData(
201
207
  `fetch-site-data-${prop.site}`,
202
- () => getSiteById(prop.site));
208
+ () => getSiteById(prop.site),);
203
209
 
204
210
  const { data: levelsData, refresh: refreshLevelsData, status: levelsStatus } = useLazyAsyncData(
205
211
  `fetch-levels-data-${prop.site}-${form.block}`,
206
212
  async () => {
207
213
  if (!form.block) return Promise.resolve(null);
208
214
  return await getSiteLevels(prop.site, { block: Number(form.block) })
209
- });
215
+ }, { watch: [() => form.block],});
210
216
 
211
217
  const { data: unitsData, refresh: refreshUnitsData, status: unitsStatus } = useLazyAsyncData(
212
218
  `fetch-units-data-${prop.site}-${form.level}`,
213
219
  async () => {
214
220
  if (!form.level) return Promise.resolve(null);
215
221
  return await getSiteUnits(prop.site, Number(form.block), form.level)
216
- });
222
+ }, { watch: [()=> form.level]});
217
223
 
218
224
  watch(
219
225
  siteData,
@@ -267,12 +273,10 @@ watch(
267
273
  function handleChangeBlock(value: any) {
268
274
  form.level = '';
269
275
  form.unit = '';
270
- refreshLevelsData();
271
276
  }
272
277
 
273
278
  function handleChangeLevel(value: any) {
274
279
  form.unit = '';
275
- refreshUnitsData();
276
280
  }
277
281
 
278
282
  function handleUpdateUnit(value: any) {
@@ -280,7 +284,7 @@ function handleUpdateUnit(value: any) {
280
284
  form.unitName = selectedUnit?.title || ''
281
285
  }
282
286
 
283
- function dateToday(){
287
+ function dateToday() {
284
288
  const today = new Date()
285
289
  return today.toISOString()
286
290
  }
@@ -362,33 +366,44 @@ async function submit() {
362
366
  errorMessage.value = '';
363
367
  processing.value = true;
364
368
 
365
- let payload: Partial<TPeoplePayload> = {
366
- org: prop.org,
367
- site: prop.site
368
- }
369
+ let payload: Partial<TPeoplePayload> = {}
369
370
 
370
371
  if (prop.mode === 'add') {
371
372
  payload = {
372
373
  ...payload,
374
+ org: prop.org,
375
+ site: prop.site,
373
376
  ...form,
374
377
  }
375
378
 
376
379
  } else if (prop.mode === 'edit') {
380
+ const { type, ...rest } = form
381
+ payload = {
382
+ ...payload,
383
+ ...rest
384
+ }
377
385
 
378
386
  }
379
387
  try {
388
+ if (prop.mode === 'add') {
389
+ await create(payload)
390
+ } else if (prop.mode === 'edit'){
391
+ const userId = prop.activeId
392
+ if(!userId) {
393
+ throw new Error('User Id prop is not defined')
394
+ }
380
395
 
381
- const res = await create(payload)
382
- if (res) {
383
- if (createMore.value) {
384
- resetForm()
385
- step.value = 1;
386
- errorMessage.value = ""
387
- emit("done:more")
388
- createMore.value = false
389
- } else emit("done")
396
+ await updateById(userId, payload)
390
397
  }
391
398
 
399
+ if (createMore.value) {
400
+ resetForm()
401
+ step.value = 1;
402
+ errorMessage.value = ""
403
+ emit("done:more")
404
+ createMore.value = false
405
+ } else emit("done")
406
+
392
407
  } catch (error: any) {
393
408
  const err = error?.data?.message
394
409
  errorMessage.value = err || `Failed to ${prop.mode === 'add' ? 'add' : 'update'} ${prop.type}. Please try again.`;
@@ -406,9 +421,27 @@ watch(() => [form.end, form.start], () => {
406
421
  (startDateRef.value as any)?.validate();
407
422
  });
408
423
 
424
+ async function mountExistingData() {
425
+ setTimeout(() => {
426
+ const people = prop.people
427
+ if (!people) return
428
+
429
+ (Object.keys(form) as (keyof typeof form)[]).forEach((key) => {
430
+ if (key in people) {
431
+ form[key] = people[key as keyof TPeople]
432
+ }
433
+ })
434
+ }, 100)
435
+ }
436
+
409
437
  onMounted(() => {
410
438
  step.value = 1;
411
439
  createMore.value = false;
440
+ if(prop.mode === 'edit'){
441
+ mountExistingData()
442
+ } else {
443
+ form.start = dateToday()
444
+ }
412
445
  })
413
446
 
414
447
  </script>
@@ -416,5 +449,4 @@ onMounted(() => {
416
449
  .button-outline-class {
417
450
  border: 1px solid rgba(var(--v-theme-primary));
418
451
  }
419
-
420
452
  </style>
@@ -27,8 +27,8 @@
27
27
  >
28
28
  <v-tab
29
29
  v-for="tab in tabOptions"
30
- :value="tab.type"
31
- :key="tab.type"
30
+ :value="tab.status"
31
+ :key="tab.status"
32
32
  class="text-capitalize"
33
33
  >
34
34
  {{ tab.name }}
@@ -309,7 +309,7 @@ const {
309
309
  const { debounce, formatCamelCaseToWords, formatDate, UTCToLocalTIme } =
310
310
  useUtils();
311
311
  const { formatLocation } = useSecurityUtils();
312
- const { type: visitorType } = useRoute().query;
312
+ const { status: visitorStatus } = useRoute().query;
313
313
  const { org: orgId, site: siteId } = useRoute().params as {
314
314
  org: string;
315
315
  site: string;
@@ -320,13 +320,13 @@ const items = ref<Array<Record<string, any>>>([]);
320
320
  const page = ref(1);
321
321
  const pages = ref(0);
322
322
  const pageRange = ref("-- - -- of --");
323
- const activeTab = ref(visitorType ?? "registered");
323
+ const activeTab = ref(visitorStatus ?? "registered");
324
324
  const activeVisitorFormType = ref<TVisitorType | null>(null);
325
325
  const selectedVisitorId = ref<string | null>(""); // selected visitor for viewing/actions
326
326
 
327
327
  //filter states
328
328
  const searchInput = ref("");
329
- const dateFrom = ref(getUTCDates().dateYesterday);
329
+ const dateFrom = ref("");
330
330
  const dateTo = ref("");
331
331
  const filterTypes = ref([]);
332
332
  const displayCheckedOutOnly = ref<boolean>(false);
@@ -349,8 +349,8 @@ const dialog = reactive({
349
349
  });
350
350
 
351
351
  const tabOptions = [
352
- { name: "Registered", type: "registered" },
353
- { name: "Unregistered", type: "unregistered" },
352
+ { name: "Registered", status: "registered" },
353
+ { name: "Unregistered", status: "unregistered" },
354
354
  ];
355
355
 
356
356
  const formatType = (item: any) =>
@@ -374,8 +374,8 @@ function filterTypeSelectionLabel() {
374
374
  return `${length} selected ${length === 1 ? "type" : "types"}` as string;
375
375
  }
376
376
 
377
- function toRoute(type: any) {
378
- const obj = tabOptions.find((x) => x.type === type);
377
+ function toRoute(status: any) {
378
+ const obj = tabOptions.find((x) => x.status === status);
379
379
  if (!obj) return;
380
380
  navigateTo({
381
381
  name: routeName,
@@ -383,7 +383,7 @@ function toRoute(type: any) {
383
383
  org: orgId,
384
384
  },
385
385
  query: {
386
- type: obj.type,
386
+ status: obj.status,
387
387
  },
388
388
  });
389
389
  }
@@ -393,7 +393,7 @@ const {
393
393
  refresh: getVisitorRefresh,
394
394
  pending: getVisitorPending,
395
395
  } = await useLazyAsyncData(
396
- `get-all-visitors-${visitorType}`,
396
+ `get-all-visitors-${visitorStatus}`,
397
397
  () =>
398
398
  getVisitors({
399
399
  page: page.value,
@@ -404,9 +404,10 @@ const {
404
404
  dateFrom: dateFrom.value,
405
405
  type: filterTypes.value.filter(Boolean).join(","),
406
406
  displayNoCheckOut: displayCheckedOutOnly.value,
407
+ status: activeTab.value as string
407
408
  }),
408
409
  {
409
- watch: [page],
410
+ watch: [page, activeTab],
410
411
  }
411
412
  );
412
413
 
@@ -16,18 +16,23 @@
16
16
  @errored="onImageError"
17
17
  />
18
18
 
19
- <v-text-field
19
+ <v-autocomplete
20
+ v-model="workOrder.subject"
21
+ :items="subjects"
22
+ item-title="title"
23
+ item-value="value"
24
+ item-props
20
25
  label="Subject"
21
26
  variant="outlined"
22
27
  density="compact"
23
- v-model="workOrder.subject"
24
- class="mb-1"
25
- dense
28
+ class="mb-2"
29
+ clearable
26
30
  :readonly="
27
31
  props.createdFrom === 'feedback' && workOrder.subject !== ''
28
32
  ? true
29
33
  : false
30
34
  "
35
+ :custom-filter="customFilter"
31
36
  />
32
37
 
33
38
  <v-autocomplete
@@ -239,4 +244,13 @@ watch(workOrderUnit, (val: string) => {
239
244
  }
240
245
  }
241
246
  });
247
+
248
+ const { subjects } = useLocal();
249
+
250
+ const customFilter = (value: string, query: string, item: any) => {
251
+ const title = item?.raw?.title?.toLowerCase() || "";
252
+ const subtitle = item?.raw?.subtitle?.toLowerCase() || "";
253
+ const search = query.toLowerCase();
254
+ return title.includes(search) || subtitle.includes(search);
255
+ };
242
256
  </script>
@@ -133,6 +133,10 @@ const props = defineProps({
133
133
  type: Boolean,
134
134
  default: false,
135
135
  },
136
+ category: {
137
+ type: String,
138
+ default: "",
139
+ },
136
140
  });
137
141
 
138
142
  const theme = useTheme().name;
@@ -193,11 +197,11 @@ const {
193
197
  data: getAllWorkOrderReq,
194
198
  refresh: getAllReqRefresh,
195
199
  status: getAllReqStatus,
196
- } = useLazyAsyncData("get-all-work-orders", () =>
200
+ } = useLazyAsyncData("get-all-work-orders-" + props.category, () =>
197
201
  _getWorkOrders({
198
202
  page: page.value,
199
- organization: route.params.org as string,
200
203
  site: route.params.site as string,
204
+ category: props.category,
201
205
  })
202
206
  );
203
207
 
@@ -286,7 +290,7 @@ watchEffect(() => {
286
290
  serviceProviders.value = getAllReq.value.map((i: any) => ({
287
291
  title: i.nature.replace(/_/g, " "),
288
292
  subtitle: i.title,
289
- value: i._id.org,
293
+ value: i.nature
290
294
  }));
291
295
  }
292
296
  });
@@ -13,28 +13,28 @@ export default function(){
13
13
  type="",
14
14
  displayNoCheckOut=false
15
15
  } = {}) {
16
- return await useNuxtApp().$api<Record<string, any>>("/api/people", {
16
+ return await $fetch<Record<string, any>>("/api/people", {
17
17
  method: "GET",
18
18
  query: { page, limit, sort, search, org, site, dateTo, dateFrom, type }
19
19
  });
20
20
  }
21
21
 
22
22
  async function create(payload: Partial<TPeoplePayload>){
23
- return await useNuxtApp().$api<Record<string, any>>("/api/people", {
23
+ return await $fetch<Record<string, any>>("/api/people", {
24
24
  method: "POST",
25
25
  body: payload,
26
26
  });
27
27
  }
28
28
 
29
29
  async function updateById(_id: string, payload: Partial<TPeoplePayload>){
30
- return await useNuxtApp().$api<Record<string, any>>(`/api/people/id/${_id}`, {
30
+ return await $fetch<Record<string, any>>(`/api/people/id/${_id}`, {
31
31
  method: "PUT",
32
32
  body: payload,
33
33
  });
34
34
  }
35
35
 
36
36
  async function deleteById(_id: string){
37
- return await useNuxtApp().$api<Record<string, any>>(`/api/people/id/${_id}`, {
37
+ return await $fetch<Record<string, any>>(`/api/people/id/${_id}`, {
38
38
  method: "DELETE",
39
39
  });
40
40
  }
@@ -39,7 +39,8 @@ export default function(){
39
39
  dateTo = "",
40
40
  dateFrom = "",
41
41
  type="",
42
- displayNoCheckOut=false
42
+ displayNoCheckOut=false,
43
+ // status = "registered"
43
44
  } = {}) {
44
45
  return await useNuxtApp().$api<Record<string, any>>("/api/visitor-transactions", {
45
46
  method: "GET",
@@ -32,18 +32,18 @@ export default function useWorkOrder() {
32
32
 
33
33
  async function getWorkOrders({
34
34
  page = 1,
35
- organization = "",
36
35
  site = "",
37
36
  status = "to-do",
38
37
  search = "",
39
38
  limit = 10,
39
+ category = "",
40
40
  } = {}) {
41
41
  try {
42
42
  return useNuxtApp().$api<Record<string, any>>(
43
- `/api/work-orders/organization/${organization}/site/${site}/status/${status}`,
43
+ `/api/work-orders/site/${site}/status/${status}`,
44
44
  {
45
45
  method: "GET",
46
- query: { page, search, limit },
46
+ query: { page, search, limit, category },
47
47
  }
48
48
  );
49
49
  } catch (err) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@iservice365/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.2.0",
5
+ "version": "1.3.0",
6
6
  "main": "./nuxt.config.ts",
7
7
  "scripts": {
8
8
  "dev": "nuxi dev .playground",
package/types/people.d.ts CHANGED
@@ -4,15 +4,15 @@ declare type TPeople = {
4
4
  level?: string;
5
5
  unit?: string | Partial<TBuildingUnit>;
6
6
  unitName?: string;
7
- contact: string;
8
- plateNumber: string;
7
+ contact?: string;
8
+ plateNumber?: string;
9
9
  nric?: string;
10
10
  contact?: string;
11
11
  remarks?: string;
12
- start: string;
13
- end: string;
14
- org: string;
15
- site: string,
12
+ start?: string;
13
+ end?: string;
14
+ org?: string;
15
+ site?: string,
16
16
  type?: TPeoplePayload;
17
17
  };
18
18