@7365admin1/layer-common 1.11.21 → 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.
@@ -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>
@@ -32,7 +32,7 @@
32
32
  <!-- This is where the QR code scanner will be displayed -->
33
33
  </div>
34
34
  <!-- Show Error Messages -->
35
- <div v-if="showNotFoundMessage" class="text-error mt-8">
35
+ <div v-if="showNotFoundMessage" class="text-error mt-4">
36
36
  <p>{{ errorMessage }}</p>
37
37
  </div>
38
38
  <!-- Switch camera button inside the reader box -->
@@ -52,9 +52,18 @@ const props = defineProps({
52
52
  type: Boolean,
53
53
  default: false,
54
54
  },
55
+ site: {
56
+ type: String,
57
+ default: null,
58
+ },
55
59
  });
56
60
 
57
- const emit = defineEmits(["closeDialog"]);
61
+ const { validateVisitorQrCode } = useVisitor();
62
+
63
+ const emits = defineEmits([
64
+ "closeDialog",
65
+ "openVisitorDataFromScannedQrCodeDialog",
66
+ ]);
58
67
 
59
68
  const message = ref("");
60
69
  const messageColor = ref("");
@@ -86,31 +95,57 @@ const handleScanVisitorQRCode = async () => {
86
95
  showNotFoundMessage.value = false;
87
96
  // Automatically navigate to the scanned URL if it's a valid link
88
97
  if (isValidUrl(qrCodeMessage)) {
89
- console.log(`QR Code value: ${qrCodeMessage}`);
90
- window.location.href = qrCodeMessage;
98
+ console.log("QR Code value", qrCodeMessage);
99
+ console.log("Last _id", qrCodeMessage.split("/").pop());
100
+ // get the last _id in the url
101
+ const id = qrCodeMessage.split("/").pop();
102
+ // check if it's a valid mongodb _id
103
+ if (/^[0-9a-fA-F]{24}$/.test(id as string)) {
104
+ // validate visitor's QR code
105
+ const response = await validateVisitorQrCode(
106
+ props.site as string,
107
+ id as string
108
+ );
109
+ console.log("validateVisitorQrCode", response);
110
+ if (response.errorMessage) {
111
+ showNotFoundMessage.value = true;
112
+ errorMessage.value = response.errorMessage;
113
+ showMessage(errorMessage.value, "error");
114
+ } else {
115
+ emits("openVisitorDataFromScannedQrCodeDialog", response);
116
+ }
117
+ } else {
118
+ showNotFoundMessage.value = true;
119
+ errorMessage.value = "Invalid mongodb _id.";
120
+ showMessage(errorMessage.value, "error");
121
+ }
122
+
123
+ // window.location.href = qrCodeMessage;
91
124
  } else {
92
125
  showNotFoundMessage.value = true;
93
- errorMessage.value = "Invalid url";
94
- showMessage("Invalid url", "error");
126
+ errorMessage.value = "Invalid url.";
127
+ showMessage(errorMessage.value, "error");
95
128
  }
96
129
  },
97
130
  (msg: string) => {
98
- if (msg === "QR Code no longer in front") {
131
+ if (msg === "QR Code no longer in front.") {
99
132
  showNotFoundMessage.value = true;
100
133
  errorMessage.value = msg;
101
- showMessage(msg, "error");
134
+ showMessage(errorMessage.value, "error");
102
135
  }
103
136
  }
104
137
  )
105
138
  .catch((err: any) => {
139
+ closeDialog();
106
140
  showMessage(`Unable to start scanning, error: ${err}`, "error");
107
141
  });
108
142
  };
109
143
 
110
- const isValidUrl = (string: string) => {
144
+ const isValidUrl = (url: string) => {
111
145
  try {
112
- new URL(string);
113
- return true;
146
+ if (typeof url == "string") {
147
+ return true;
148
+ }
114
149
  } catch (_) {
115
150
  return false;
116
151
  }
@@ -130,7 +165,7 @@ const switchCamera = async () => {
130
165
  };
131
166
 
132
167
  const closeDialog = () => {
133
- emit("closeDialog");
168
+ emits("closeDialog");
134
169
  stopScanner();
135
170
  };
136
171
 
@@ -163,7 +163,6 @@
163
163
  </template>
164
164
 
165
165
  <script setup lang="ts">
166
- import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
167
166
  import useCleaningSchedules from "../composables/useCleaningSchedules";
168
167
  import useUtils from "../composables/useUtils";
169
168
 
@@ -174,6 +173,9 @@ const props = defineProps({
174
173
  type: { type: String, required: true, default: "cleaner" },
175
174
  serviceType: { type: String, default: "" },
176
175
  scheduleRoute: { type: String, default: "cleaning-schedule" },
176
+ canGenerateChecklist: { type: Boolean, default: false },
177
+ canViewHistory: { type: Boolean, default: true },
178
+ canManageScheduleTasks: { type: Boolean, default: false },
177
179
  });
178
180
 
179
181
  const submitting = ref(false);
@@ -195,8 +197,9 @@ const headers = [
195
197
 
196
198
  const { formatDate, back } = useUtils();
197
199
 
198
- const { canGenerateChecklist, canViewHistory, canManageScheduleTasks } =
199
- useCleaningSchedulePermission();
200
+ const canGenerateChecklist = computed(() => props.canGenerateChecklist);
201
+ const canViewHistory = computed(() => props.canViewHistory);
202
+ const canManageScheduleTasks = computed(() => props.canManageScheduleTasks);
200
203
 
201
204
  const selectedScheduleStatus = useState<string>(
202
205
  "selectedScheduleStatus",
@@ -257,24 +257,27 @@
257
257
 
258
258
  <script lang="ts" setup>
259
259
  import useScheduleTask from "../composables/useScheduleTask";
260
- import useScheduleTaskPermission from "../composables/useScheduleTaskPermission";
261
260
 
262
261
  const props = defineProps({
263
262
  orgId: { type: String, required: true },
264
263
  site: { type: String, required: true },
265
264
  type: { type: String, default: "toilet" },
266
265
  serviceType: { type: String, default: "" },
266
+ canViewScheduleTasks: { type: Boolean, default: true },
267
+ canCreateScheduleTask: { type: Boolean, default: false },
268
+ canUpdateScheduleTask: { type: Boolean, default: false },
269
+ canDeleteScheduleTask: { type: Boolean, default: false },
270
+ canViewScheduleTaskDetails: { type: Boolean, default: true },
267
271
  });
268
272
 
269
273
  const { formatDate, debounce } = useUtils();
270
274
  const { getScheduleTasks, getScheduleTaskById, deleteScheduleTask } = useScheduleTask();
271
- const {
272
- canViewScheduleTasks,
273
- canCreateScheduleTask,
274
- canUpdateScheduleTask,
275
- canDeleteScheduleTask,
276
- canViewScheduleTaskDetails,
277
- } = useScheduleTaskPermission();
275
+
276
+ const canViewScheduleTasks = computed(() => props.canViewScheduleTasks);
277
+ const canCreateScheduleTask = computed(() => props.canCreateScheduleTask);
278
+ const canUpdateScheduleTask = computed(() => props.canUpdateScheduleTask);
279
+ const canDeleteScheduleTask = computed(() => props.canDeleteScheduleTask);
280
+ const canViewScheduleTaskDetails = computed(() => props.canViewScheduleTaskDetails);
278
281
 
279
282
  const page = ref(1);
280
283
  const pages = ref(0);
@@ -209,7 +209,6 @@
209
209
  </template>
210
210
 
211
211
  <script setup lang="ts">
212
- import { useUnitPermission } from "../composables/useUnitPermission";
213
212
  import useUnits from "../composables/useUnits";
214
213
  import useUtils from "../composables/useUtils";
215
214
 
@@ -217,6 +216,11 @@ const props = defineProps({
217
216
  orgId: { type: String, default: "" },
218
217
  site: { type: String, default: "" },
219
218
  serviceType: { type: String, default: "", required: true },
219
+ canViewUnits: { type: Boolean, default: true },
220
+ canCreateUnit: { type: Boolean, default: false },
221
+ canUpdateUnit: { type: Boolean, default: false },
222
+ canDeleteUnit: { type: Boolean, default: false },
223
+ canImportUnit: { type: Boolean, default: false },
220
224
  });
221
225
 
222
226
  const items = ref<Array<Record<string, any>>>([]);
@@ -299,13 +303,11 @@ function showMessage(msg: string, color: string = "error") {
299
303
  messageSnackbar.value = true;
300
304
  }
301
305
 
302
- const {
303
- canCreateUnit,
304
- canUpdateUnit,
305
- canDeleteUnit,
306
- canViewUnits,
307
- canImportUnit,
308
- } = useUnitPermission();
306
+ const canViewUnits = computed(() => props.canViewUnits);
307
+ const canCreateUnit = computed(() => props.canCreateUnit);
308
+ const canUpdateUnit = computed(() => props.canUpdateUnit);
309
+ const canDeleteUnit = computed(() => props.canDeleteUnit);
310
+ const canImportUnit = computed(() => props.canImportUnit);
309
311
 
310
312
  async function handleDownloadExcel() {
311
313
  try {