@7365admin1/layer-common 1.11.22 → 1.11.23

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.23
4
+
5
+ ### Patch Changes
6
+
7
+ - 6d68511: Update layer-comon qr code package
8
+
3
9
  ## 1.11.22
4
10
 
5
11
  ### Patch Changes
@@ -61,20 +61,20 @@
61
61
 
62
62
  <script setup lang="ts">
63
63
  import useAttendance from "../composables/useAttendance";
64
- import { useAttendancePermission } from "../composables/useAttendancePermission";
65
64
  import useUtils from "../composables/useUtils";
66
65
 
67
66
  const props = defineProps({
68
67
  orgId: { type: String, default: "" },
69
68
  site: { type: String, default: "" },
70
69
  serviceType: { type: String, default: "" },
70
+ canViewAllAttendance: { type: Boolean, default: true },
71
+ canViewAttendanceDetails: { type: Boolean, default: true },
72
+ canManageAttendanceSettings: { type: Boolean, default: false },
71
73
  });
72
74
 
73
- const {
74
- canViewAllAttendance,
75
- canViewAttendanceDetails,
76
- canManageAttendanceSettings,
77
- } = useAttendancePermission();
75
+ const canViewAllAttendance = computed(() => props.canViewAllAttendance);
76
+ const canViewAttendanceDetails = computed(() => props.canViewAttendanceDetails);
77
+ const canManageAttendanceSettings = computed(() => props.canManageAttendanceSettings);
78
78
 
79
79
  const submitting = ref(false);
80
80
  const page = ref(1);
@@ -102,7 +102,6 @@
102
102
  </template>
103
103
 
104
104
  <script lang="ts" setup>
105
- import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
106
105
  import useCleaningSchedules from "../composables/useCleaningSchedules";
107
106
  import useUtils from "../composables/useUtils";
108
107
 
@@ -112,6 +111,10 @@ const props = defineProps({
112
111
  type: { type: String, required: true },
113
112
  serviceType: { type: String, default: "" },
114
113
  scheduleRoute: { type: String, default: "cleaning-schedule" },
114
+ canViewSchedules: { type: Boolean, default: true },
115
+ canViewScheduleDetails: { type: Boolean, default: true },
116
+ canDownloadSchedule: { type: Boolean, default: false },
117
+ canManageScheduleTasks: { type: Boolean, default: false },
115
118
  });
116
119
 
117
120
  const startDate = ref("");
@@ -172,12 +175,10 @@ const getCloseInColor = (id: string): string => {
172
175
  return "primary";
173
176
  };
174
177
 
175
- const {
176
- canDownloadSchedule,
177
- canViewSchedules,
178
- canViewScheduleDetails,
179
- canManageScheduleTasks,
180
- } = useCleaningSchedulePermission();
178
+ const canDownloadSchedule = computed(() => props.canDownloadSchedule);
179
+ const canViewSchedules = computed(() => props.canViewSchedules);
180
+ const canViewScheduleDetails = computed(() => props.canViewScheduleDetails);
181
+ const canManageScheduleTasks = computed(() => props.canManageScheduleTasks);
181
182
 
182
183
  const {
183
184
  data: getCleanerChecklistReq,
@@ -300,7 +300,6 @@
300
300
  </template>
301
301
 
302
302
  <script setup lang="ts">
303
- import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
304
303
  import useCleaningSchedules from "../composables/useCleaningSchedules";
305
304
  import useCustomerSite from "../composables/useCustomerSite";
306
305
  import useFile from "../composables/useFile";
@@ -313,13 +312,15 @@ const props = defineProps({
313
312
  scheduleAreaId: { type: String, default: "" },
314
313
  type: { type: String, default: "cleaner" },
315
314
  serviceType: { type: String, default: "" },
315
+ canAddRemarks: { type: Boolean, default: false },
316
+ canManageScheduleTasks: { type: Boolean, default: false },
316
317
  });
317
318
 
318
319
  const { getUnitCleanerChecklist, updateUnitChecklist } = useCleaningSchedules();
319
320
  const { getBySiteAsServiceProvider } = useCustomerSite();
320
321
  const { back } = useUtils();
321
- const { canAddRemarks, canManageScheduleTasks } =
322
- useCleaningSchedulePermission();
322
+ const canAddRemarks = computed(() => props.canAddRemarks);
323
+ const canManageScheduleTasks = computed(() => props.canManageScheduleTasks);
323
324
 
324
325
  const selectedScheduleStatus = useState<string>(
325
326
  "selectedScheduleStatus",
@@ -2,9 +2,9 @@
2
2
  <v-row no-gutters class="w-100">
3
3
  <v-card class="w-100">
4
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 = ''">
5
+ <v-btn block color="primary-button" :height="40" text="Scan QR Code" class="text-capitalize"
6
+ prepend-icon="mdi-qrcode" @click="handleScanQRPass" />
7
+ <v-form ref="formRef" v-model="validForm" :disabled="processing" @click="errorScanPassMessage = ''">
8
8
  <v-row no-gutters class="pt-5 ga-2">
9
9
  <v-col cols="12">
10
10
  <InputLabel class="text-capitalize" title="Full Name" required />
@@ -19,10 +19,10 @@
19
19
  </v-col>
20
20
 
21
21
  <v-col cols="12">
22
- <v-autocomplete v-model="selectedPass" v-model:search="passInput"
23
- :hide-no-data="false" class="mt-3" :items="passItemsFilteredFinal"
24
- item-title="prefixAndName" item-value="_id" label="Pass (optional)" variant="outlined"
25
- hide-details density="compact" persistent-hint small-chips>
22
+ <v-autocomplete v-model="selectedPass" v-model:search="passInput" :hide-no-data="false"
23
+ class="mt-3" :items="passItemsFilteredFinal" item-title="prefixAndName" item-value="_id"
24
+ label="Pass (optional)" variant="outlined" density="compact" :error-messages="errorScanPassMessage"
25
+ persistent-hint small-chips>
26
26
  <template v-slot:no-data>
27
27
  <v-list-item density="compact">
28
28
  <v-list-item-title v-if="passInput">
@@ -35,8 +35,7 @@
35
35
  </template>
36
36
 
37
37
  <template v-slot:chip="{ props, item }">
38
- <v-chip v-if="selectedPass" v-bind="props"
39
- prepend-icon="mdi-card-bulleted-outline"
38
+ <v-chip v-if="selectedPass" v-bind="props" prepend-icon="mdi-card-bulleted-outline"
40
39
  :text="item.raw?.prefixAndName"></v-chip>
41
40
  </template>
42
41
  </v-autocomplete>
@@ -62,12 +61,15 @@
62
61
  </v-form>
63
62
  </v-card-text>
64
63
  </v-card>
64
+ <visitor-pass-key-q-r-scanner :dialog="dialog.scanQRCode" v-model:scannedValue="scannedPassValue"
65
+ @close-dialog="dialog.scanQRCode = false" />
65
66
  <v-divider class="my-3" />
66
67
  <v-row v-if="committedMembers.length > 0" no-gutters class="w-100 mt-5 ga-3">
67
68
  <template v-for="member, index in committedMembers" :key="member.nric">
68
69
  <CardMemberInfoSummary :member="membersDisplayed[index]" @remove="handleRemoveMember(index)" />
69
70
  </template>
70
71
  </v-row>
72
+ <!-- <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" /> -->
71
73
  </v-row>
72
74
  </template>
73
75
 
@@ -103,18 +105,33 @@ const { getPassKeysByPageSearch } = usePassKey()
103
105
  const memberForm = reactive<TMemberInfo>({
104
106
  name: "",
105
107
  nric: "",
106
- visitorPass: "",
108
+ visitorPass: [] as { keyId: string }[],
107
109
  contact: ""
108
110
  })
109
111
 
110
112
  const validForm = ref(false)
111
113
  const formRef = ref<HTMLFormElement>()
112
114
  const processing = ref(false);
113
- const errorMessage = ref('')
115
+ const errorScanPassMessage = ref('')
116
+
117
+ const message = ref("");
118
+ const messageColor = ref("");
119
+ const messageSnackbar = ref(false);
120
+
121
+ function showMessage(msg: string, color: string) {
122
+ message.value = msg;
123
+ messageColor.value = color;
124
+ messageSnackbar.value = true;
125
+ }
114
126
 
115
127
  // pass
116
128
  const passInput = ref('')
117
129
  const passItems = ref<TPassKey[]>([])
130
+ const scannedPassValue = ref<string>("")
131
+
132
+ const dialog = reactive({
133
+ scanQRCode: false,
134
+ })
118
135
 
119
136
  const selectedPass = ref<string>('')
120
137
 
@@ -160,7 +177,7 @@ watch(passesData, (data: any) => {
160
177
 
161
178
  const passItemsFilteredFinal = computed(() => {
162
179
  return passItems.value.filter((item: TPassKey) => {
163
- return !props.selectedVisitorPass.some((pass: TPassKeyPayload) => pass.keyId === item._id) && !committedMembers.value.some((member: TMemberInfo) => member.visitorPass === item._id)
180
+ return !props.selectedVisitorPass.some((pass: TPassKeyPayload) => pass.keyId === item._id) && !committedMembers.value.some((member: TMemberInfo) => member.visitorPass?.some((pass) => pass.keyId === item._id))
164
181
  })
165
182
  })
166
183
 
@@ -168,15 +185,34 @@ const membersDisplayed = computed(() => {
168
185
  return committedMembers.value.map((member: TMemberInfo) => {
169
186
  return {
170
187
  ...member,
171
- visitorPass: passItems.value.find((item: TPassKey) => item._id === member.visitorPass)?.prefixAndName || ""
188
+ visitorPass: passItems.value.find((item: TPassKey) => item._id === member.visitorPass?.[0]?.keyId)?.prefixAndName || ""
172
189
  }
173
190
  })
174
191
  })
175
192
 
193
+ function handleScanQRPass() {
194
+ errorScanPassMessage.value = ""
195
+ dialog.scanQRCode = true
196
+ }
197
+
176
198
  watch(selectedPass, (newVal) => {
177
199
  memberForm.visitorPass = [{ keyId: newVal }]
178
200
  })
179
201
 
202
+ watch(scannedPassValue, (newVal) => {
203
+ if (!newVal) return
204
+
205
+ const matchedPass = passItemsFilteredFinal.value.find((p) => p.prefixAndName === newVal)
206
+
207
+ if (matchedPass) {
208
+ selectedPass.value = matchedPass._id ?? ""
209
+ return
210
+ }
211
+
212
+ errorScanPassMessage.value = "Scanned pass not available"
213
+ showMessage("Scanned pass not available", "error")
214
+ })
215
+
180
216
 
181
217
 
182
218
  function handleClearForm() {
@@ -79,16 +79,19 @@
79
79
 
80
80
  <script setup lang="ts">
81
81
  import useAttendance from "../composables/useAttendance";
82
- import { useAttendancePermission } from "../composables/useAttendancePermission";
83
82
 
84
83
  const props = defineProps({
85
84
  orgId: { type: String, default: "" },
86
85
  site: { type: String, default: "" },
87
86
  serviceType: { type: String, default: "" },
87
+ canViewOwnAttendance: { type: Boolean, default: true },
88
+ canCheckInOut: { type: Boolean, default: false },
89
+ canViewAttendanceDetails: { type: Boolean, default: true },
88
90
  });
89
91
 
90
- const { canViewOwnAttendance, canCheckInOut, canViewAttendanceDetails } =
91
- useAttendancePermission();
92
+ const canViewOwnAttendance = computed(() => props.canViewOwnAttendance);
93
+ const canCheckInOut = computed(() => props.canCheckInOut);
94
+ const canViewAttendanceDetails = computed(() => props.canViewAttendanceDetails);
92
95
 
93
96
  const searchInput = ref("");
94
97
  const dialogShowForm = ref(false);
@@ -1,171 +1,224 @@
1
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-autocomplete v-model="selectedPass" v-model:search="passInput" :hide-no-data="false" class="mt-3"
8
- :items="passItems" :rules="props.passRules" item-title="prefixAndName" item-value="_id" label="Pass"
9
- variant="outlined" hide-details density="compact" persistent-hint small-chips>
10
-
11
- <template v-slot:chip="{ props, item }">
12
- <v-chip v-if="selectedPass" v-bind="props" prepend-icon="mdi-card-bulleted-outline"
13
- :text="item.raw?.prefixAndName"></v-chip>
14
- </template>
15
- </v-autocomplete>
16
-
17
-
18
- <template v-if="!props.hideKeys">
19
- <v-autocomplete v-model="selectedKeys" v-model:search="keyInput" :hide-no-data="false" class="mt-3"
20
- :items="keyItems" :rules="props.passRules" item-title="prefixAndName" item-value="_id"
21
- label="Keys" multiple variant="outlined" hide-details density="compact" persistent-hint
22
- small-chips>
23
-
24
- <template v-slot:chip="{ props, item }">
25
- <v-chip v-if="selectedKeys.length > 0" v-bind="props" prepend-icon="mdi-key"
26
- :text="item.raw?.prefixAndName"></v-chip>
27
- </template>
28
- </v-autocomplete>
29
- </template>
30
-
31
- <v-divider class="my-4 w-100" />
32
-
33
-
34
- <template v-if="selectedType">
35
- <v-number-input v-model="count" variant="outlined" :min="1" :precision="0" density="compact"
36
- :rules="countRules" />
37
- </template>
38
- </v-card-text>
39
- </v-card>
40
- </v-row>
2
+ <v-row no-gutters class="w-100">
3
+ <v-card class="w-100">
4
+ <v-card-text>
5
+ <v-btn
6
+ block
7
+ color="primary-button"
8
+ :height="40"
9
+ text="Scan QR Code"
10
+ class="text-capitalize"
11
+ disabled
12
+ prepend-icon="mdi-qrcode"
13
+ />
14
+ <v-autocomplete
15
+ v-model="selectedPass"
16
+ v-model:search="passInput"
17
+ :hide-no-data="false"
18
+ class="mt-3"
19
+ :items="passItems"
20
+ :rules="props.passRules"
21
+ item-title="prefixAndName"
22
+ item-value="_id"
23
+ label="Pass"
24
+ variant="outlined"
25
+ hide-details
26
+ density="compact"
27
+ persistent-hint
28
+ small-chips
29
+ :clearable="props.clearable"
30
+ >
31
+ <template v-slot:chip="{ props, item }">
32
+ <v-chip
33
+ v-if="selectedPass"
34
+ v-bind="props"
35
+ prepend-icon="mdi-card-bulleted-outline"
36
+ :text="item.raw?.prefixAndName"
37
+ ></v-chip>
38
+ </template>
39
+ </v-autocomplete>
40
+
41
+ <template v-if="!props.hideKeys">
42
+ <v-autocomplete
43
+ v-model="selectedKeys"
44
+ v-model:search="keyInput"
45
+ :hide-no-data="false"
46
+ class="mt-3"
47
+ :items="keyItems"
48
+ :rules="props.passRules"
49
+ item-title="prefixAndName"
50
+ item-value="_id"
51
+ label="Keys"
52
+ multiple
53
+ variant="outlined"
54
+ hide-details
55
+ density="compact"
56
+ persistent-hint
57
+ small-chips
58
+ :clearable="props.clearable"
59
+ >
60
+ <template v-slot:chip="{ props, item }">
61
+ <v-chip
62
+ v-if="selectedKeys.length > 0"
63
+ v-bind="props"
64
+ prepend-icon="mdi-key"
65
+ :text="item.raw?.prefixAndName"
66
+ ></v-chip>
67
+ </template>
68
+ </v-autocomplete>
69
+ </template>
70
+
71
+ <v-divider class="my-4 w-100" />
72
+
73
+ <template v-if="selectedType">
74
+ <v-number-input
75
+ v-model="count"
76
+ variant="outlined"
77
+ :min="1"
78
+ :precision="0"
79
+ density="compact"
80
+ :rules="countRules"
81
+ />
82
+ </template>
83
+ </v-card-text>
84
+ </v-card>
85
+ </v-row>
41
86
  </template>
42
87
 
43
88
  <script setup lang="ts">
44
- import type { PropType } from 'vue'
45
- import type { ValidationRule } from 'vuetify/lib/types.mjs'
46
- import usePassKey from '../composables/usePassKey'
47
- import useKey from '../composables/useKey'
89
+ import type { PropType } from "vue";
90
+ import type { ValidationRule } from "vuetify/lib/types.mjs";
91
+ import usePassKey from "../composables/usePassKey";
92
+ import useKey from "../composables/useKey";
48
93
 
49
94
  const props = defineProps({
50
- passRules: {
51
- type: Array as PropType<ValidationRule[]>,
52
- default: []
53
- },
54
- countRules: {
55
- type: Array as PropType<ValidationRule[]>,
56
- default: []
57
- },
58
- site: {
59
- type: String,
60
- required: true
61
- },
62
- type: {
63
- type: String as PropType<TVisitorType>,
64
- required: true
65
- },
66
- contractorType: {
67
- type: String,
68
- default: ""
69
- },
70
- hideKeys: {
71
- type: Boolean,
72
- default: false
73
- }
74
- })
75
-
76
- const pass = defineModel<TPassKeyPayload[]>("pass", { default: [] })
77
- const keys = defineModel<TPassKeyPayload[]>("keys", { default: [] })
78
- const selectedPass = ref<string>('')
79
- const selectedKeys = ref<string[]>([])
80
- const passInput = ref('')
81
- const keyInput = ref('')
82
- const passItems = ref<TPassKey[]>([])
83
- const keyItems = ref<any[]>([])
84
- const selectedType = ref<'qr-pass' | 'nfc-card'>()
85
- const count = ref(1)
86
-
87
- const { getPassKeysByPageSearch } = usePassKey()
95
+ passRules: {
96
+ type: Array as PropType<ValidationRule[]>,
97
+ default: [],
98
+ },
99
+ countRules: {
100
+ type: Array as PropType<ValidationRule[]>,
101
+ default: [],
102
+ },
103
+ site: {
104
+ type: String,
105
+ required: true,
106
+ },
107
+ type: {
108
+ type: String as PropType<TVisitorType>,
109
+ required: true,
110
+ },
111
+ contractorType: {
112
+ type: String,
113
+ default: "",
114
+ },
115
+ hideKeys: {
116
+ type: Boolean,
117
+ default: false,
118
+ },
119
+ clearable: {
120
+ type: Boolean,
121
+ default: false,
122
+ },
123
+ });
124
+
125
+ const pass = defineModel<TPassKeyPayload[]>("pass", { default: [] });
126
+ const keys = defineModel<TPassKeyPayload[]>("keys", { default: [] });
127
+ const selectedPass = ref<string>("");
128
+ const selectedKeys = ref<string[]>([]);
129
+ const passInput = ref("");
130
+ const keyInput = ref("");
131
+ const passItems = ref<TPassKey[]>([]);
132
+ const keyItems = ref<any[]>([]);
133
+ const selectedType = ref<"qr-pass" | "nfc-card">();
134
+ const count = ref(1);
135
+
136
+ const { getPassKeysByPageSearch } = usePassKey();
88
137
 
89
138
  const typeItems = [
90
- {
91
- label: "QR Pass",
92
- value: "qr-pass"
93
- },
94
- {
95
- label: "NFC Card",
96
- value: "nfc-card"
97
- }
98
- ]
139
+ {
140
+ label: "QR Pass",
141
+ value: "qr-pass",
142
+ },
143
+ {
144
+ label: "NFC Card",
145
+ value: "nfc-card",
146
+ },
147
+ ];
99
148
 
100
149
  const passTypesComputed = computed(() => {
101
- if (props.type === 'contractor') {
102
- if (props.contractorType === 'property-agent') {
103
- return ["agent-pass"]
104
- } else {
105
- return ["contractor-pass"]
106
- }
150
+ if (props.type === "contractor") {
151
+ if (props.contractorType === "property-agent") {
152
+ return ["agent-pass"];
107
153
  } else {
108
- return ["visitor-pass"]
154
+ return ["contractor-pass"];
109
155
  }
110
- })
111
-
112
- const { data: passesData, refresh: refreshPassesData, pending: fetchPassesPending } = await useLazyAsyncData('get-pass-keys', () => {
113
- return getPassKeysByPageSearch({
114
- search: passInput.value,
115
- page: 1,
116
- limit: 500,
117
- passTypes: passTypesComputed.value,
118
- sites: [props.site],
119
- statuses: ["Available"]
120
- })
121
- })
156
+ } else {
157
+ return ["visitor-pass"];
158
+ }
159
+ });
160
+
161
+ const {
162
+ data: passesData,
163
+ refresh: refreshPassesData,
164
+ pending: fetchPassesPending,
165
+ } = await useLazyAsyncData("get-pass-keys", () => {
166
+ return getPassKeysByPageSearch({
167
+ search: passInput.value,
168
+ page: 1,
169
+ limit: 500,
170
+ passTypes: passTypesComputed.value,
171
+ sites: [props.site],
172
+ statuses: ["Available"],
173
+ });
174
+ });
122
175
 
123
176
  watch(passesData, (data: any) => {
124
- passItems.value = data?.items || []
125
- })
126
-
127
- const { data: keysData, refresh: refreshKeysData, pending: fetchKeysPending } = await useLazyAsyncData('get-keys', () => {
128
- return getPassKeysByPageSearch({
129
- search: keyInput.value,
130
- statuses: ["Available"],
131
- passTypes: ['pass-key'],
132
- page: 1,
133
- limit: 500,
134
- sites: [props.site],
135
- })
136
- })
177
+ passItems.value = data?.items || [];
178
+ });
179
+
180
+ const {
181
+ data: keysData,
182
+ refresh: refreshKeysData,
183
+ pending: fetchKeysPending,
184
+ } = await useLazyAsyncData("get-keys", () => {
185
+ return getPassKeysByPageSearch({
186
+ search: keyInput.value,
187
+ statuses: ["Available"],
188
+ passTypes: ["pass-key"],
189
+ page: 1,
190
+ limit: 500,
191
+ sites: [props.site],
192
+ });
193
+ });
137
194
 
138
195
  watch(keysData, (data: any) => {
139
- keyItems.value = data?.items || []
140
- })
196
+ keyItems.value = data?.items || [];
197
+ });
141
198
 
142
199
  watch(selectedPass, (newVal) => {
143
- pass.value = [{ keyId: newVal }]
144
- })
200
+ pass.value = [{ keyId: newVal }];
201
+ });
145
202
 
146
203
  watch(selectedKeys, (newVal) => {
147
- keys.value = newVal.map(key => ({ keyId: key }))
148
- })
149
-
150
-
151
-
204
+ keys.value = newVal.map((key) => ({ keyId: key }));
205
+ });
152
206
 
153
207
  //prevent negative value;
154
208
  watch(count, (newCount) => {
155
- if (newCount < 1) {
156
- count.value = 1
157
- }
158
- })
209
+ if (newCount < 1) {
210
+ count.value = 1;
211
+ }
212
+ });
159
213
 
160
214
  onMounted(() => {
161
- if (pass.value.length > 0) {
162
- selectedPass.value = pass.value[0].keyId
163
- }
164
- if (keys.value.length > 0) {
165
- selectedKeys.value = keys.value.map(k => k.keyId)
166
- }
167
- })
168
-
215
+ if (pass.value.length > 0) {
216
+ selectedPass.value = pass.value[0].keyId;
217
+ }
218
+ if (keys.value.length > 0) {
219
+ selectedKeys.value = keys.value.map((k) => k.keyId);
220
+ }
221
+ });
169
222
  </script>
170
223
 
171
- <style scoped></style>
224
+ <style scoped></style>