@7365admin1/layer-common 1.10.9 → 1.10.10

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,58 +1,5 @@
1
1
  <template>
2
2
  <v-row no-gutters class="px-5 pt-4">
3
- <!-- Any unit affected -->
4
- <v-col cols="12" class="border-b pb-5 mb-5 mt-1">
5
- <p class="mb-2" style="font-size: 17px; font-weight: 600">
6
- Any unit affected?
7
- </p>
8
- <p
9
- v-if="affectedEntities?.anyUnitAffectedValue == 'no'"
10
- class="mt-2 text-h6 text-capitalize"
11
- >
12
- {{ affectedEntities?.anyUnitAffectedValue }}
13
- </p>
14
- <!-- Block Level Unit Section -->
15
- <v-row v-else no-gutters class="d-flex align-center mt-5">
16
- <v-row
17
- v-if="affectedEntities?.affectedUnit.hasOwnProperty('block')"
18
- no-gutters
19
- >
20
- <v-col cols="12" sm="4">
21
- <InputLabel class="text-capitalize" title="Block" />
22
- <p class="my-1 text-h6">
23
- {{ affectedEntities?.affectedUnit?.block }}
24
- </p>
25
- </v-col>
26
- <v-col cols="12" sm="4">
27
- <InputLabel class="text-capitalize" title="Level" />
28
- <p class="my-1 text-h6">
29
- {{ affectedEntities?.affectedUnit?.level }}
30
- </p>
31
- </v-col>
32
- <v-col cols="12" sm="4">
33
- <InputLabel class="text-capitalize" title="Unit" />
34
- <p class="my-1 text-h6">
35
- {{ affectedEntities?.affectedUnit?.unit }}
36
- </p>
37
- </v-col>
38
- </v-row>
39
-
40
- <v-col v-else cols="12" class="px-1 mb-0">
41
- <InputLabel class="text-capitalize" title="Location" />
42
- <p class="my-1 text-h6 text-capitalize">
43
- {{ affectedEntities?.affectedUnit?.other }}
44
- </p>
45
- </v-col>
46
-
47
- <v-col cols="12" class="px-1 mb-0 mt-5">
48
- <InputLabel class="text-capitalize" title="Remarks" />
49
- <p class="my-1 text-h6 text-capitalize">
50
- {{ affectedEntities?.affectedUnit?.remarks }}
51
- </p>
52
- </v-col>
53
- </v-row>
54
- </v-col>
55
-
56
3
  <!-- Anyone affected/injured -->
57
4
  <v-col cols="12" class="border-b pb-5 mb-5 mt-1">
58
5
  <p class="mb-2" style="font-size: 17px; font-weight: 600">
@@ -161,6 +108,14 @@ const injuredTableHeader = [
161
108
  title: "Contact Number",
162
109
  value: "contact",
163
110
  },
111
+ {
112
+ title: "Block / Level / Unit Location",
113
+ value: "incidentLocation",
114
+ },
115
+ {
116
+ title: "Remarks",
117
+ value: "remarks",
118
+ },
164
119
  ];
165
120
 
166
121
  const damagePropertyTableHeader = [
@@ -180,10 +135,6 @@ const damagePropertyTableHeader = [
180
135
  title: "Contact Number",
181
136
  value: "contact",
182
137
  },
183
- {
184
- title: "Action",
185
- value: "action",
186
- },
187
138
  ];
188
139
 
189
140
  const isShowNRIC = ref(false);
@@ -0,0 +1,285 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <v-expansion-panels multiple v-model="openPanels" >
4
+
5
+ <!-- SITE SETTINGS -->
6
+ <v-expansion-panel color="primary">
7
+ <v-expansion-panel-title>
8
+ <v-icon class="mr-2">mdi-cog</v-icon>
9
+ Site Settings
10
+ </v-expansion-panel-title>
11
+
12
+ <v-expansion-panel-text>
13
+ <v-row no-gutters class="d-flex justify-center">
14
+ <v-col cols="12">
15
+ <v-row no-gutters>
16
+ <v-col cols="12">
17
+ <span class="text-h4 font-weight-bold"> Site Settings </span>
18
+ </v-col>
19
+
20
+ <v-col cols="12">
21
+ <v-row no-gutters>
22
+ <v-col cols="12" lg="4" class="mt-2">
23
+ <NumberSettingField v-model="blocks" title="No. of blocks" type="blocks"
24
+ :read-only="!canManageSiteSettings"
25
+ :existing-block-number="existingBlockNumber" :site-id="siteId"
26
+ :disabled="existingBlockNumber === blocks" @success="refreshSiteData" />
27
+ </v-col>
28
+ </v-row>
29
+ </v-col>
30
+
31
+ <v-col cols="12">
32
+ <v-row no-gutters>
33
+ <v-col cols="12" lg="4" class="mt-2">
34
+ <NumberSettingField v-model="guardPosts" title="No. of guard posts"
35
+ type="guard_posts" :read-only="!canManageSiteSettings"
36
+ :existing-guard-posts-number="existingGuardPostNumber" :site-id="siteId"
37
+ :disabled="existingGuardPostNumber === guardPosts"
38
+ @success="refreshSiteData" />
39
+ </v-col>
40
+ </v-row>
41
+ </v-col>
42
+
43
+ <v-col cols="12">
44
+ <v-row no-gutters>
45
+ <v-col cols="12" lg="4" class="mt-2">
46
+ <v-row no-gutters>
47
+ <v-form v-model="gracePeriodValid">
48
+ <v-row>
49
+ <v-col cols="6">
50
+ <InputLabel class="text-capitalize font-weight-bold"
51
+ title="Grace Period" required />
52
+ <v-text-field v-model="gracePeriod" type="number"
53
+ density="comfortable" :readonly="!canManageSiteSettings"
54
+ :rules="[requiredRule]" />
55
+ </v-col>
56
+
57
+ <v-col cols="6">
58
+ <v-btn v-if="canManageSiteSettings" color="primary"
59
+ class="text-none mt-6" size="large" variant="flat"
60
+ :disabled="!gracePeriodValid ||
61
+ existingGracePeriodNumber === gracePeriod
62
+ " :loading="gracePeriodLoading" text="Save"
63
+ @click="handleSaveGracePeriod" />
64
+ </v-col>
65
+ </v-row>
66
+ </v-form>
67
+ </v-row>
68
+ </v-col>
69
+ </v-row>
70
+ </v-col>
71
+
72
+ </v-row>
73
+ </v-col>
74
+ </v-row>
75
+ </v-expansion-panel-text>
76
+ </v-expansion-panel>
77
+
78
+ <!-- ANPR CAMERA -->
79
+ <v-expansion-panel color="primary">
80
+ <v-expansion-panel-title>
81
+ <v-icon class="mr-2">mdi-camera</v-icon>
82
+ ANPR Camera
83
+ </v-expansion-panel-title>
84
+
85
+ <v-expansion-panel-text>
86
+ <CameraMain :site="siteId" :read-only="!canManageSiteSettings"
87
+ :guard-posts="siteData?.metadata?.guardPosts" />
88
+ </v-expansion-panel-text>
89
+ </v-expansion-panel>
90
+
91
+ <!-- CCTV CAMERA -->
92
+ <v-expansion-panel color="primary">
93
+ <v-expansion-panel-title>
94
+ <v-icon class="mr-2">mdi-cctv</v-icon>
95
+ CCTV Camera
96
+ </v-expansion-panel-title>
97
+
98
+ <v-expansion-panel-text>
99
+ <CameraMain :site="siteId" type="ip" :read-only="!canManageSiteSettings" />
100
+ </v-expansion-panel-text>
101
+ </v-expansion-panel>
102
+
103
+ <v-expansion-panel color="primary">
104
+ <v-expansion-panel-title>
105
+ <v-icon class="mr-2">mdi-format-list-numbered</v-icon>
106
+ Work Order Settings
107
+ </v-expansion-panel-title>
108
+
109
+ <v-expansion-panel-text>
110
+ <v-text-field v-model="prefix" label="Prefix" :disabled="isLoading || !canManageSiteSettings"
111
+ @input="handleInput" />
112
+
113
+ <v-select v-model="noOfDigits" :items="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" label="No. of Digits"
114
+ :disabled="isLoading || !canManageSiteSettings" />
115
+
116
+ <v-text-field v-model="orderNumberPreview" label="Preview" readonly />
117
+
118
+ <v-btn v-if="canManageSiteSettings" class="mt-2" :loading="isLoading" @click="save">
119
+ Save
120
+ </v-btn>
121
+ </v-expansion-panel-text>
122
+ </v-expansion-panel>
123
+
124
+
125
+ <v-expansion-panel color="primary">
126
+ <v-expansion-panel-title>
127
+ <v-icon class="mr-2">mdi-truck</v-icon>
128
+ Delivery Companies
129
+ </v-expansion-panel-title>
130
+ <v-expansion-panel-text class="">
131
+ <DeliveryCompany :site="siteId" v-model:initial="deliveryCompanies" @refresh-site="refreshSiteData" @update:companiesValue="handleUpdateCompanies"
132
+ :read-only="!canManageSiteSettings" />
133
+ </v-expansion-panel-text>
134
+
135
+ </v-expansion-panel>
136
+
137
+ </v-expansion-panels>
138
+
139
+ <Snackbar v-model="toast.show" :text="toast.message" :color="toast.color" />
140
+ </v-row>
141
+ </template>
142
+
143
+ <script setup lang="ts">
144
+ const props = defineProps({
145
+ siteId: { type: String, required: true },
146
+ canManageSiteSettings: { type: Boolean, default: false }
147
+ })
148
+
149
+ const openPanels = ref([0]) // default open first
150
+
151
+ const { getSiteById, updateSitebyId } = useSiteSettings()
152
+ const { requiredRule } = useUtils()
153
+ const { getFileUrl } = useFile()
154
+ const { getWorkOrderSettings, createWorkOrderSettings } = useWorkOrder()
155
+
156
+ const blocks = ref(0)
157
+ const guardPosts = ref(0)
158
+ const gracePeriod = ref(0)
159
+
160
+ const existingBlockNumber = ref(0)
161
+ const existingGuardPostNumber = ref(0)
162
+ const existingGracePeriodNumber = ref(0)
163
+
164
+ const gracePeriodValid = ref(false)
165
+ const gracePeriodLoading = ref(false)
166
+ const deliveryCompanies = ref<string[]>([])
167
+
168
+ const siteLogo = ref<any[]>([])
169
+ const uploadedSiteLogo = ref("")
170
+
171
+ const prefix = ref("")
172
+ const noOfDigits = ref(1)
173
+ const isLoading = ref(false)
174
+
175
+ const toast = reactive({
176
+ show: false,
177
+ message: "",
178
+ color: ""
179
+ })
180
+
181
+ const { data: siteData, refresh: refreshSiteData } = await useLazyAsyncData(
182
+ `site-${props.siteId}`,
183
+ () => getSiteById(props.siteId)
184
+ )
185
+
186
+ watch(siteData, (val: TSite) => {
187
+ if (!val) return
188
+
189
+ blocks.value = val.metadata?.block || 0
190
+ guardPosts.value = val.metadata?.guardPosts || 0
191
+ gracePeriod.value = val.metadata?.gracePeriod || 0
192
+
193
+ existingBlockNumber.value = blocks.value
194
+ existingGuardPostNumber.value = guardPosts.value
195
+ existingGracePeriodNumber.value = gracePeriod.value
196
+ deliveryCompanies.value = Array.isArray(val.deliveryCompanyList) ? [...val.deliveryCompanyList] : []
197
+
198
+ uploadedSiteLogo.value = val.metadata?.incidentLogo || ""
199
+ }, { immediate: true })
200
+
201
+ async function handleSaveGracePeriod() {
202
+ gracePeriodLoading.value = true
203
+ try {
204
+ await updateSitebyId(props.siteId, {
205
+ field: "metadata.gracePeriod",
206
+ value: gracePeriod.value
207
+ })
208
+ show("Grace period updated", "success")
209
+ refreshSiteData()
210
+ } catch (e: any) {
211
+ show(e?.data?.message || "Error updating grace period", "error")
212
+ } finally {
213
+ gracePeriodLoading.value = false
214
+ }
215
+ }
216
+
217
+ async function handleLogo(action: string) {
218
+ try {
219
+ await updateSitebyId(props.siteId, {
220
+ field: "metadata.incidentLogo",
221
+ value: action === "add" ? siteLogo.value[0] : 0
222
+ })
223
+ show("Logo updated", "success")
224
+ refreshSiteData()
225
+ } catch {
226
+ show("Error updating logo", "error")
227
+ } finally {
228
+ siteLogo.value = []
229
+ }
230
+ }
231
+
232
+ const onUploadedLogoPreview = () => {
233
+ if (!uploadedSiteLogo.value) return ""
234
+ return getFileUrl(uploadedSiteLogo.value)
235
+ }
236
+
237
+ const { data: workOrderSetting } = await useLazyAsyncData(
238
+ `work-${props.siteId}`,
239
+ () => getWorkOrderSettings({ site: props.siteId, service: "Security" })
240
+ )
241
+
242
+ watchEffect(() => {
243
+ if (workOrderSetting.value) {
244
+ prefix.value = workOrderSetting.value.prefix
245
+ noOfDigits.value = workOrderSetting.value.noOfDigits
246
+ }
247
+ })
248
+
249
+ function handleInput(e: any) {
250
+ prefix.value = e.target.value
251
+ .replace(/[^A-Z]/g, "")
252
+ .toUpperCase()
253
+ }
254
+
255
+ function handleUpdateCompanies(value: string[]) {
256
+ deliveryCompanies.value = value
257
+ }
258
+
259
+ const orderNumberPreview = computed(() => {
260
+ return `${prefix.value}${"0".repeat(noOfDigits.value - 1)}1`
261
+ })
262
+
263
+ async function save() {
264
+ try {
265
+ isLoading.value = true
266
+ await createWorkOrderSettings({
267
+ site: props.siteId,
268
+ service: "Security",
269
+ prefix: prefix.value,
270
+ noOfDigits: noOfDigits.value
271
+ })
272
+ show("Saved successfully", "success")
273
+ } catch {
274
+ show("Error saving", "error")
275
+ } finally {
276
+ isLoading.value = false
277
+ }
278
+ }
279
+
280
+ function show(message: string, color: string) {
281
+ toast.show = true
282
+ toast.message = message
283
+ toast.color = color
284
+ }
285
+ </script>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <v-tooltip v-bind="$attrs" :text="text" @click.stop>
3
+ <template v-slot:activator="{ props: activatorProps }">
4
+ <v-btn icon="mdi-information-symbol" v-bind="activatorProps" class="p-0 m-0 d-flex align-center"
5
+ :density="density" :size="size" color="primary-button" />
6
+ </template>
7
+ </v-tooltip>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+
12
+ import type { VTooltip} from "vuetify/components"
13
+
14
+ type TTooltipDensity = VTooltip["$props"]["density"]
15
+
16
+ const props = defineProps({
17
+ text: {
18
+ type: String,
19
+ default: ""
20
+ },
21
+ size: {
22
+ type: String,
23
+ default: ""
24
+ },
25
+ density: {
26
+ type: String as PropType<TTooltipDensity>,
27
+ default: "default"
28
+ }
29
+ })
30
+
31
+ </script>
32
+
33
+ <style scoped></style>
@@ -110,6 +110,36 @@
110
110
  </v-col>
111
111
  </template>
112
112
 
113
+ <template v-if="shouldShowField('delivery-company')">
114
+ <v-col cols="12">
115
+ <InputLabel class="text-capitalize" title="Delivery Company" />
116
+ <v-combobox v-model="deliveryCompany" v-model:search="deliveryCompanyInput" ref="companyCombo"
117
+ autocomplete="off" :hide-no-data="false" :items="deliveryCompanyList" item-value="value"
118
+ :loading="siteDataPending" variant="outlined"
119
+ density="comfortable" persistent-hint small-chips>
120
+ <template v-slot:no-data>
121
+ <v-list-item>
122
+ <v-list-item-title v-if="fetchCompanyListPending">
123
+ <v-progress-circular indeterminate size="20" class="mr-3" />
124
+ Searching companies…
125
+ </v-list-item-title>
126
+ <v-list-item-title v-else-if="companyNameInput" @click.stop="handleAddNewCompany"
127
+ class="d-flex align-center ga-1">
128
+ <span><v-icon icon="mdi-plus" /></span>Add "<strong>{{ companyNameInput }}</strong>" as new
129
+ company.
130
+ </v-list-item-title>
131
+ <v-list-item-title v-else-if="!companyNameInput && companyNames.length === 0">
132
+ Start typing to search for companies.
133
+ </v-list-item-title>
134
+ <v-list-item-title v-else>
135
+ No companies available. Start typing to add a new one.
136
+ </v-list-item-title>
137
+ </v-list-item>
138
+ </template>
139
+ </v-combobox>
140
+ </v-col>
141
+ </template>
142
+
113
143
 
114
144
  <v-col v-if="shouldShowField('plateNumber')" cols="12">
115
145
  <v-row>
@@ -356,13 +386,17 @@ const blocksArray = ref<TDefaultOptionObj[]>([]);
356
386
  const levelsArray = ref<TDefaultOptionObj[]>([]);
357
387
  const unitsArray = ref<TDefaultOptionObj[]>([]);
358
388
 
389
+ const deliveryCompany = ref("");
390
+ const deliveryCompanyInput = ref("");
391
+ const deliveryCompanyList = ref<string[]>([]);
392
+
359
393
  const matchingPlateNumberNonCheckedOutArr = ref<TVisitor[]>([])
360
394
 
361
395
 
362
396
  const vehicleNumberUserItems = ref<TPeople[]>([])
363
397
 
364
398
 
365
- const shouldShowField = (fieldKey: keyof TVisitorPayload) => {
399
+ const shouldShowField = (fieldKey: keyof TVisitorPayload | 'delivery-company') => {
366
400
  if (prop.type !== "contractor" || contractorStep.value === 1) {
367
401
  const visibleFields = typeFieldMap[prop.type];
368
402
  return visibleFields?.includes(fieldKey);
@@ -634,6 +668,7 @@ const {
634
668
  data: siteData,
635
669
  refresh: refreshSiteData,
636
670
  status: blockStatus,
671
+ pending: siteDataPending,
637
672
  } = useLazyAsyncData(`fetch-site-data-${prop.site}`, () =>
638
673
  getSiteById(prop.site)
639
674
  );
@@ -665,7 +700,7 @@ const {
665
700
  watch(
666
701
  siteData,
667
702
  (newVal) => {
668
- const siteDataValue = newVal as any;
703
+ const siteDataValue = newVal as TSite;
669
704
  if (siteDataValue) {
670
705
  const numberOfBlocks = siteDataValue.metadata?.block || 0;
671
706
  for (let i = 1; i <= numberOfBlocks; i++) {
@@ -674,6 +709,9 @@ watch(
674
709
  value: i,
675
710
  });
676
711
  }
712
+
713
+ deliveryCompanyList.value = Array.isArray(siteDataValue?.deliveryCompanyList) ? siteDataValue.deliveryCompanyList : [];
714
+
677
715
  } else {
678
716
  blocksArray.value = [];
679
717
  }
@@ -857,6 +895,14 @@ async function submit() {
857
895
  members: visitor.members,
858
896
  };
859
897
  }
898
+
899
+ if(prop.type === "delivery"){
900
+ payload = {
901
+ ...payload,
902
+ company: deliveryCompany.value
903
+ }
904
+ }
905
+
860
906
  try {
861
907
  const res = await createVisitor(payload);
862
908
  if (res) {
@@ -89,13 +89,19 @@
89
89
  <span class="text-capitalize">{{
90
90
  UTCToLocalTIme(item.checkIn) || "-"
91
91
  }}</span>
92
+ <span>
93
+ <v-icon v-if="item?.snapshotEntryImage" size="17" icon="mdi-image" @click.stop="handleViewImage(item.snapshotEntryImage)" />
94
+ </span>
92
95
  </span>
93
96
  <span class="d-flex align-center ga-2">
94
97
  <v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" />
95
98
  <template v-if="item.checkOut">
96
99
  <span class="text-capitalize">{{
97
- UTCToLocalTIme(item.checkOut) || "_"
100
+ UTCToLocalTIme(item.checkOut) || "-"
98
101
  }}</span>
102
+ <span>
103
+ <v-icon v-if="item?.snapshotExitImage" size="17" icon="mdi-image" @click.stop="handleViewImage(item.snapshotExitImage)" />
104
+ </span>
99
105
  <span v-if="item?.manualCheckout">
100
106
  <TooltipInfo text="Manual Checkout" density="compact" size="x-small" />
101
107
  </span>
@@ -120,8 +126,9 @@
120
126
  </v-dialog>
121
127
 
122
128
  <v-dialog v-model="dialog.showForm" v-if="activeVisitorFormType" width="450" persistent>
123
- <VisitorForm :mode="mode" :org="orgId" :site="siteId" :visitor-data="selectedVisitorDataObject" :type="activeVisitorFormType" @back="handleClickBack"
124
- @done="handleVisitorFormDone" @done:more="handleVisitorFormCreateMore" @close:all="handleCloseAll" />
129
+ <VisitorForm :mode="mode" :org="orgId" :site="siteId" :visitor-data="selectedVisitorDataObject"
130
+ :type="activeVisitorFormType" @back="handleClickBack" @done="handleVisitorFormDone"
131
+ @done:more="handleVisitorFormCreateMore" @close:all="handleCloseAll" />
125
132
  </v-dialog>
126
133
 
127
134
  <v-dialog v-model="dialog.viewVisitor" width="450" persistent>
@@ -200,6 +207,7 @@ const {
200
207
  const { debounce, formatCamelCaseToWords, formatDate, UTCToLocalTIme } =
201
208
  useUtils();
202
209
  const { formatLocation } = useSecurityUtils();
210
+ const { getFileUrlAnpr } = useFile();
203
211
  // const { status: visitorStatus, search } = useRoute().query as { status: string, search: string};
204
212
 
205
213
  const route = useRoute()
@@ -276,8 +284,11 @@ const formattedFields = {
276
284
  level: "Level",
277
285
  unitName: "Unit",
278
286
  checkIn: "Check In",
287
+ snapshotEntryImage: "Entry Image",
279
288
  checkOut: "Check Out",
289
+ snapshotExitImage: "Exit Image",
280
290
  remarks: "Remarks",
291
+
281
292
  } as const;
282
293
 
283
294
  function filterTypeSelectionLabel() {
@@ -326,7 +337,7 @@ const {
326
337
  params.status = activeTab.value
327
338
  } else if (activeTab.value === "guests") {
328
339
  params.type = "guest"
329
- params.status = undefined
340
+ params.status = "pending"
330
341
  }
331
342
 
332
343
  return await getVisitors(params)
@@ -348,11 +359,11 @@ watch(getVisitorReq, (newData: any) => {
348
359
  });
349
360
 
350
361
  const selectedVisitorObject = computed(() => {
362
+
351
363
  const obj = items.value.find((x: any) => x?._id === selectedVisitorId.value);
352
364
  if (!obj) return {};
353
365
  const type = obj?.type as TVisitorType | undefined;
354
- if (!type) return {};
355
- let includedKeys: string[] = ["checkIn", "checkOut"];
366
+ let includedKeys: string[] = ["checkIn", "checkOut", "plateNumber", "snapshotEntryImage", "snapshotExitImage"];
356
367
  includedKeys.unshift(...(typeFieldMap[type] ?? []));
357
368
  return Object.fromEntries(
358
369
  Object.entries(obj).filter(([key]) => includedKeys.includes(key))
@@ -434,6 +445,12 @@ function handleRegistrationUnregisteredVisitor(item: Partial<TVisitor>) {
434
445
  mode.value = "register";
435
446
  }
436
447
 
448
+ function handleViewImage(imageId: string) {
449
+ const imageEndpoint = `${siteId}/${imageId}`;
450
+ const imageUrl = getFileUrlAnpr(imageEndpoint);
451
+ window.open(imageUrl, "_blank");
452
+ }
453
+
437
454
 
438
455
  async function handleProceedDeleteVisitor() {
439
456
  try {
@@ -243,6 +243,24 @@ export default function useAccessManagement() {
243
243
  );
244
244
  }
245
245
 
246
+ function generateQrVms(params: {
247
+ site: string;
248
+ unitId: string;
249
+ quantity: number;
250
+ }) {
251
+ return useNuxtApp().$api<Record<string, any>>(
252
+ `/api/access-management/generate-qr-vms`,
253
+ {
254
+ method: "POST",
255
+ body: {
256
+ site: params.site,
257
+ unitId: params.unitId,
258
+ quantity: params.quantity,
259
+ },
260
+ }
261
+ );
262
+ }
263
+
246
264
  return {
247
265
  getDoorAccessLevels,
248
266
  getLiftAccessLevels,
@@ -261,5 +279,6 @@ export default function useAccessManagement() {
261
279
  saveVisitorAccessCardQrTag,
262
280
  getAllVisitorAccessCardsQrTags,
263
281
  getAvailableContractorCards,
282
+ generateQrVms,
264
283
  };
265
284
  }
@@ -1,12 +1,17 @@
1
+ import type { APP_CONSTANTS } from "../constants/app";
2
+
1
3
  export default function(){
2
4
 
3
- const recipientList: { title: string, value: TAnnouncementRecipients }[] = [
4
- // resident | security_agency | cleaning_services | mechanical_electrical | property_management_agency
5
+ const recipientList: { title: string, value: typeof APP_CONSTANTS[keyof typeof APP_CONSTANTS] }[] = [
5
6
  { title: "Security Agency", value: "security_agency" },
6
7
  { title: "Cleaning Services", value: "cleaning_services" },
7
8
  { title: "Mechanical & Electrical", value: "mechanical_electrical" },
8
9
  { title: "Property Management Agency", value: "property_management_agency" },
9
- { title: "Resident", value: "resident" }
10
+ { title: "Resident", value: "resident" },
11
+ { title: "Pest Control", value: "pest_control_services" },
12
+ { title: "Landscaping", value: "landscaping_services" },
13
+ { title: "Pool Maintenance", value: "pool_maintenance_services" },
14
+
10
15
  ]
11
16
 
12
17
  async function add(payload: Partial<TCreateAnnouncementPayload>) {