@7365admin1/layer-common 1.11.15 → 1.11.16

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,12 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.11.16
4
+
5
+ ### Patch Changes
6
+
7
+ - 59db5ff: Update Layer common register
8
+ - acbb54d: Update Visitor Management
9
+
3
10
  ## 1.11.15
4
11
 
5
12
  ### Patch Changes
@@ -249,6 +249,7 @@ import useEquipment from "../composables/useEquipment";
249
249
  import useStock from "../composables/useStock";
250
250
  import { useEquipmentPermission } from "../composables/useEquipmentPermission";
251
251
  import useUtils from "../composables/useUtils";
252
+ import useEquipmentManagement from "../composables/useEquipmentManagement";
252
253
 
253
254
  const props = defineProps({
254
255
  orgId: { type: String, default: "" },
@@ -497,7 +498,8 @@ async function _deleteEquipment() {
497
498
  }
498
499
  }
499
500
 
500
- const { createStock, requestItem } = useStock();
501
+ const { createStock } = useStock();
502
+ const { checkOutItemSingle } = useEquipmentManagement();
501
503
 
502
504
  const stockActionQuantity = ref<number>(0);
503
505
  const stockActionRemarks = ref("");
@@ -535,9 +537,10 @@ async function _requestItem() {
535
537
 
536
538
  const payload: TStockCreate = {
537
539
  qty: stockActionQuantity.value,
540
+ serviceType: props.serviceType,
538
541
  };
539
542
 
540
- const response = await requestItem(props.site, id, payload);
543
+ const response = await checkOutItemSingle(props.site, id, payload);
541
544
  showMessage(response?.message, "success");
542
545
  dialogStockAction.value = false;
543
546
  stockActionQuantity.value = 0;
@@ -0,0 +1,121 @@
1
+ <template>
2
+ <v-row no-gutters class="w-100">
3
+ <v-card class="w-100">
4
+ <v-card-text>
5
+ <v-btn block color="primary-button" :height="40" text="Scan QR Code" class="text-capitalize" disabled
6
+ prepend-icon="mdi-qrcode" />
7
+ <v-form ref="formRef" v-model="validForm" :disabled="processing" @click="errorMessage = ''">
8
+ <v-row no-gutters class="pt-5 ga-2">
9
+ <v-col cols="12">
10
+ <InputLabel class="text-capitalize" title="Full Name" required />
11
+ <v-text-field v-model.trim="memberForm.name" density="compact" hide-details
12
+ :rules="[requiredRule]" />
13
+ </v-col>
14
+
15
+ <v-col cols="12">
16
+ <InputLabel class="text-capitalize" title="NRIC" required />
17
+ <InputNRICNumber v-model.trim="memberForm.nric" density="compact" hide-details
18
+ :rules="[requiredRule]" />
19
+ </v-col>
20
+
21
+ <v-col cols="12">
22
+ <v-combobox v-model="pass" v-model:search="passInput" :hide-no-data="false" class="mt-3"
23
+ @update:focused="handleFocusedPass" :items="passItems" item-value="value"
24
+ @update:model-value="handleSelectPass" label="Pass (optional)" variant="outlined"
25
+ hide-details density="compact" persistent-hint small-chips>
26
+ <template v-slot:no-data>
27
+ <v-list-item density="compact">
28
+ <v-list-item-title v-if="passInput">
29
+ No results matching "<strong>{{ passInput }}</strong>". This value will be
30
+ added as new
31
+ option.
32
+ </v-list-item-title>
33
+ <v-list-item v-else>No data available</v-list-item>
34
+ </v-list-item>
35
+ </template>
36
+ </v-combobox>
37
+ </v-col>
38
+
39
+ <v-col cols="12">
40
+ <InputLabel class="text-capitalize" title="Phone Number" required />
41
+ <InputPhoneNumberV2 v-model="memberForm.contact" density="compact" hide-details/>
42
+ </v-col>
43
+
44
+ <v-row class="pt-3" justify="space-between">
45
+ <v-col cols="6">
46
+ <v-btn text="Add Member" :disabled="!validForm" prepend-icon="mdi-plus"
47
+ color="primary-button" class="text-capitalize" @click="handleAddMember" />
48
+ </v-col>
49
+ <v-col cols="6" align="end">
50
+ <v-btn text="Clear Form" prepend-icon="mdi-refresh" color="primary-button"
51
+ class="text-capitalize" @click.stop="handleClearForm" />
52
+ </v-col>
53
+ </v-row>
54
+ </v-row>
55
+ </v-form>
56
+ </v-card-text>
57
+ </v-card>
58
+ <v-divider class="my-3" />
59
+ <v-row v-if="members.length > 0" no-gutters class="w-100 mt-5 ga-3">
60
+ <template v-for="member, index in members" :key="member.nric">
61
+ <CardMemberInfoSummary :member="member" @remove="handleRemoveMember(index)" />
62
+ </template>
63
+ </v-row>
64
+ </v-row>
65
+ </template>
66
+
67
+ <script setup lang="ts">
68
+
69
+ const props = defineProps({
70
+ })
71
+
72
+ const { requiredRule } = useUtils()
73
+
74
+
75
+ const memberForm = reactive<TMemberInfo>({
76
+ name: "",
77
+ nric: "",
78
+ visitorPass: "",
79
+ contact: ""
80
+ })
81
+
82
+ const validForm = ref(false)
83
+ const formRef = ref<HTMLFormElement>()
84
+ const processing = ref(false);
85
+ const errorMessage = ref('')
86
+
87
+ // pass
88
+ const pass = ref()
89
+ const passInput = ref('')
90
+ const passItems = ref(['Test'])
91
+
92
+ const members = defineModel<TMemberInfo[]>({required: true, default: []})
93
+
94
+
95
+ function handleFocusedPass() {
96
+
97
+ }
98
+
99
+ function handleSelectPass(val: any) {
100
+ memberForm.visitorPass = val
101
+ }
102
+
103
+ function handleClearForm() {
104
+ formRef.value?.reset()
105
+ }
106
+
107
+ async function handleAddMember() {
108
+ members.value.push({...memberForm})
109
+ handleClearForm()
110
+ }
111
+
112
+ function handleRemoveMember(index: number){
113
+ const filtered = members.value.filter((item, qIndex) => qIndex !== index)
114
+ members.value = filtered
115
+ }
116
+
117
+
118
+
119
+ </script>
120
+
121
+ <style scoped></style>
@@ -0,0 +1,126 @@
1
+ <template>
2
+ <v-row no-gutters class="w-100">
3
+ <v-card class="w-100">
4
+ <v-card-text>
5
+ <v-btn block color="primary-button" :height="40" text="Scan QR Code" class="text-capitalize" disabled
6
+ prepend-icon="mdi-qrcode" />
7
+ <v-combobox v-model="pass" v-model:search="passInput" :hide-no-data="false" class="mt-3"
8
+ @update:focused="handleFocusedPass" :items="passItems" :rules="props.passRules" item-title="prefixAndName" item-value="_id"
9
+ @update:model-value="handleSelectPass" label="Pass" variant="outlined" hide-details
10
+ density="compact" persistent-hint small-chips>
11
+ <template v-slot:no-data>
12
+ <v-list-item density="compact">
13
+ <v-list-item-title v-if="passInput">
14
+ No results matching "<strong>{{ passInput }}</strong>". This value will be
15
+ added as new
16
+ option.
17
+ </v-list-item-title>
18
+ <v-list-item v-else>No data available</v-list-item>
19
+ </v-list-item>
20
+ </template>
21
+ </v-combobox>
22
+
23
+ <v-divider class="my-4 w-100" />
24
+
25
+
26
+ <template v-if="selectedType">
27
+ <v-number-input v-model="count" variant="outlined" :min="1" :precision="0" density="compact" :rules="countRules" />
28
+ </template>
29
+ </v-card-text>
30
+ </v-card>
31
+ </v-row>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import type { PropType } from 'vue'
36
+ import type { ValidationRule } from 'vuetify/lib/types.mjs'
37
+ import usePassKey from '../composables/usePassKey'
38
+
39
+ const props = defineProps({
40
+ passRules: {
41
+ type: Array as PropType<ValidationRule[]>,
42
+ default: []
43
+ },
44
+ countRules: {
45
+ type: Array as PropType<ValidationRule[]>,
46
+ default: []
47
+ },
48
+ site: {
49
+ type: String,
50
+ required: true
51
+ },
52
+ type: {
53
+ type: String as PropType<TVisitorType>,
54
+ required: true
55
+ },
56
+ contractorType: {
57
+ type: String,
58
+ default: ""
59
+ }
60
+ })
61
+
62
+ const pass = ref("")
63
+ const passInput = ref('')
64
+ const passItems = ref([])
65
+ const selectedType = ref<'qr-pass' | 'nfc-card'>()
66
+ const count = ref(1)
67
+
68
+ const { getPassKeysByPageSearch } = usePassKey()
69
+
70
+ const typeItems = [
71
+ {
72
+ label: "QR Pass",
73
+ value: "qr-pass"
74
+ },
75
+ {
76
+ label: "NFC Card",
77
+ value: "nfc-card"
78
+ }
79
+ ]
80
+
81
+ const passTypesComputed = computed(() => {
82
+ if(props.type === 'contractor'){
83
+ if(props.contractorType === 'property-agent'){
84
+ return ["agent-pass"]
85
+ } else {
86
+ return ["contractor-pass"]
87
+ }
88
+ } else {
89
+ return ["visitor-pass"]
90
+ }
91
+ })
92
+
93
+ const { data: passesData, refresh: refreshPassesData, pending: fetchPassesPending } = await useLazyAsyncData('get-pass-keys', () => {
94
+ return getPassKeysByPageSearch({
95
+ search: passInput.value,
96
+ page: 1,
97
+ limit: 500,
98
+ passTypes: passTypesComputed.value,
99
+ sites: [props.site],
100
+ statuses: ["Available"]
101
+ })
102
+ })
103
+
104
+ watch(passesData, (data: any) => {
105
+ passItems.value = data?.items || []
106
+ })
107
+
108
+
109
+ function handleFocusedPass() {
110
+
111
+ }
112
+
113
+ function handleSelectPass() {
114
+
115
+ }
116
+
117
+ //prevent negative value;
118
+ watch(count, (newCount) => {
119
+ if(newCount < 1){
120
+ count.value = 1
121
+ }
122
+ })
123
+
124
+ </script>
125
+
126
+ <style scoped></style>
@@ -184,7 +184,7 @@
184
184
  </v-col>
185
185
 
186
186
  <v-col v-if="prop.type === 'contractor' && contractorStep === 2" cols="12">
187
- <PassInformation />
187
+ <PassInformation :site="prop.site" :type="prop.type" :contractor-type="visitor.contractorType" />
188
188
  <EntryPassInformation
189
189
  v-if="entryPassSettings?.data?.settings?.nfcPass"
190
190
  v-model="passType"
@@ -866,7 +866,7 @@ async function submit() {
866
866
  site: prop.site,
867
867
  };
868
868
 
869
- if (prop.mode === "add") {
869
+ if (prop.mode === "add" || prop.mode === "register") {
870
870
  const allowedFields = typeFieldMap[prop.type];
871
871
  for (const key of allowedFields as (keyof TVisitorPayload)[]) {
872
872
  if (visitor[key] !== undefined) {
@@ -876,11 +876,6 @@ async function submit() {
876
876
  };
877
877
  }
878
878
  }
879
- } else if (prop.mode === 'register') {
880
- payload = {
881
- ...payload,
882
- status: "registered",
883
- }
884
879
  }
885
880
 
886
881
 
@@ -975,7 +970,7 @@ watch(
975
970
  );
976
971
 
977
972
  onMounted(() => {
978
- contractorStep.value = 1;
973
+ contractorStep.value = 2;
979
974
  currentAutofillSource.value = null;
980
975
 
981
976
  if (prop.mode === 'register' && prop.visitorData) {
@@ -22,7 +22,7 @@
22
22
  </v-checkbox>
23
23
  <InputDateTimePicker v-model:utc="dateFrom" density="compact" hide-details />
24
24
  <InputDateTimePicker v-model:utc="dateTo" density="compact" hide-details />
25
- <v-select v-if="activeTab !== 'guests'" v-model="filterTypes" label="Filter by types" item-title="label"
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>
28
28
  <template v-slot:selection="{ item, index }">
@@ -108,7 +108,7 @@
108
108
  <TooltipInfo text="Manual Checkout" density="compact" size="x-small" />
109
109
  </span>
110
110
  </template>
111
- <span v-else-if="!item?.checkOut && item?.status === 'registered'">
111
+ <span v-else-if="!item?.checkOut && (item?.status === 'registered' || item?.status === 'unregistered')">
112
112
  <v-btn size="x-small" class="text-capitalize" color="red" text="Checkout"
113
113
  :loading="loading.checkingOut && item?._id === selectedVisitorId" @click.stop="handleCheckout(item._id)"
114
114
  v-if="canCheckoutVisitor" />
@@ -282,6 +282,7 @@ const tabOptions = [
282
282
  { name: "Registered", value: "registered" },
283
283
  { name: "Unregistered", value: "unregistered" },
284
284
  { name: "Guests", value: "guests" },
285
+ { name: "Resident Transactions", value: "resident-transactions" },
285
286
  ];
286
287
 
287
288
  const selectedVisitorDataObject = computed(() => {
@@ -350,11 +351,21 @@ const {
350
351
  checkedOut: displayNotCheckedOut.value ? false : undefined
351
352
  }
352
353
 
353
- if (activeTab.value !== "guests") {
354
- params.status = activeTab.value
355
- } else if (activeTab.value === "guests") {
354
+ // if (activeTab.value !== "guests") {
355
+ // params.status = activeTab.value
356
+ // } else if (activeTab.value === "guests") {
357
+ // params.type = "guest"
358
+ // params.status = "pending"
359
+ // }
360
+
361
+ if(activeTab.value === "guests"){
356
362
  params.type = "guest"
357
363
  params.status = "pending"
364
+ } else if (activeTab.value === "resident-transactions") {
365
+ params.status = "registered"
366
+ params.type = "resident,tenant"
367
+ } else {
368
+ params.status = activeTab.value
358
369
  }
359
370
 
360
371
  return await getVisitors(params)
@@ -1,5 +1,11 @@
1
1
  export default function useEquipmentManagement() {
2
- function getSupplies({ page = 1, search = "", limit = 10, site = "", serviceType = "" } = {}) {
2
+ function getSupplies({
3
+ page = 1,
4
+ search = "",
5
+ limit = 10,
6
+ site = "",
7
+ serviceType = "",
8
+ } = {}) {
3
9
  return useNuxtApp().$api<Record<string, any>>(
4
10
  `/api/hygiene-supplies/site/${site}`,
5
11
  {
@@ -53,11 +59,26 @@ export default function useEquipmentManagement() {
53
59
  );
54
60
  }
55
61
 
62
+ function checkOutItemSingle(site: string, id: string, payload: TStockCreate) {
63
+ return useNuxtApp().$api<Record<string, any>>(
64
+ `/api/hygiene-checkout-items/site/${site}/supply/${id}`,
65
+ {
66
+ method: "POST",
67
+ body: {
68
+ qty: payload.qty,
69
+ serviceType: payload.serviceType,
70
+ },
71
+ }
72
+ );
73
+ }
74
+
56
75
  return {
57
76
  getSupplies,
58
77
  getSupplyById,
59
78
  createSupply,
60
79
  updateSupply,
61
80
  deleteSupply,
81
+ checkOutItemSingle,
82
+
62
83
  };
63
84
  }
@@ -27,21 +27,10 @@ export default function useStock() {
27
27
  );
28
28
  }
29
29
 
30
- function requestItem(site: string, id: string, payload: TStockCreate) {
31
- return useNuxtApp().$api<Record<string, any>>(
32
- `/api/hygiene-request-items/site/${site}/supply/${id}`,
33
- {
34
- method: "POST",
35
- body: {
36
- qty: payload.qty,
37
- },
38
- }
39
- );
40
- }
30
+
41
31
 
42
32
  return {
43
33
  createStock,
44
34
  getStockBySupply,
45
- requestItem,
46
35
  };
47
36
  }
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.15",
5
+ "version": "1.11.16",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {