@7365admin1/layer-common 1.11.15 → 1.11.17
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 +13 -0
- package/components/EquipmentManagementMain.vue +5 -2
- package/components/MemberInformation.vue +121 -0
- package/components/PassInformation.vue +126 -0
- package/components/VisitorForm.vue +3 -8
- package/components/VisitorManagement.vue +17 -6
- package/composables/useEquipmentManagement.ts +22 -1
- package/composables/useStock.ts +1 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 1.11.17
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 785495f: Update layer-common
|
|
8
|
+
|
|
9
|
+
## 1.11.16
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 59db5ff: Update Layer common register
|
|
14
|
+
- acbb54d: Update Visitor Management
|
|
15
|
+
|
|
3
16
|
## 1.11.15
|
|
4
17
|
|
|
5
18
|
### 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
|
|
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
|
|
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 =
|
|
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
|
|
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(() => {
|
|
@@ -346,15 +347,25 @@ const {
|
|
|
346
347
|
search: searchInput.value,
|
|
347
348
|
dateTo: dateTo.value,
|
|
348
349
|
dateFrom: dateFrom.value,
|
|
349
|
-
type: filterTypes.value.filter(Boolean).join(","),
|
|
350
|
+
type: (filterTypes.value.length === 0 && activeTab.value === 'registered' ? "contractor,delivery,walk-in,pick-up,drop-off,guest" : filterTypes.value.filter(Boolean).join(",")),
|
|
350
351
|
checkedOut: displayNotCheckedOut.value ? false : undefined
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
if (activeTab.value !== "guests") {
|
|
354
|
-
|
|
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({
|
|
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
|
}
|
package/composables/useStock.ts
CHANGED
|
@@ -27,21 +27,10 @@ export default function useStock() {
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
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
|
}
|