@7365admin1/layer-common 1.8.0

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.
Files changed (198) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.editorconfig +12 -0
  4. package/.github/workflows/main.yml +17 -0
  5. package/.github/workflows/publish.yml +39 -0
  6. package/.nuxtrc +1 -0
  7. package/.playground/app.vue +41 -0
  8. package/.playground/eslint.config.mjs +6 -0
  9. package/.playground/nuxt.config.ts +22 -0
  10. package/.playground/pages/feedback.vue +30 -0
  11. package/CHANGELOG.md +263 -0
  12. package/README.md +73 -0
  13. package/app.vue +3 -0
  14. package/components/AccessCardAddForm.vue +363 -0
  15. package/components/AccessManagement.vue +420 -0
  16. package/components/Avatar/Main.vue +68 -0
  17. package/components/BillingMain.vue +66 -0
  18. package/components/BtnUploadFile.vue +139 -0
  19. package/components/BuildingForm.vue +303 -0
  20. package/components/BuildingManagement/buildings.vue +335 -0
  21. package/components/BuildingManagement/units.vue +350 -0
  22. package/components/BuildingUnitFormAdd.vue +441 -0
  23. package/components/BuildingUnitFormEdit.vue +429 -0
  24. package/components/CameraForm.vue +264 -0
  25. package/components/CameraMain.vue +352 -0
  26. package/components/Card/DeleteConfirmation.vue +51 -0
  27. package/components/Card/MemberInfoSummary.vue +44 -0
  28. package/components/Card/Toggle.vue +25 -0
  29. package/components/Chat/Bubbles.vue +53 -0
  30. package/components/Chat/Information.vue +416 -0
  31. package/components/Chat/ListCard.vue +62 -0
  32. package/components/Chat/Message.vue +158 -0
  33. package/components/Chat/Navigation.vue +150 -0
  34. package/components/ConfirmDialog.vue +66 -0
  35. package/components/Container/Standard.vue +33 -0
  36. package/components/DashboardPlaceholder.vue +1524 -0
  37. package/components/Dialog/DeleteConfirmation.vue +51 -0
  38. package/components/Dialog/ReplaceAutofillPrompt.vue +49 -0
  39. package/components/Dialog/UpdateMoreAction.vue +103 -0
  40. package/components/DocumentForm.vue +187 -0
  41. package/components/DocumentManagement.vue +376 -0
  42. package/components/Editor.vue +95 -0
  43. package/components/EntryPassMain.vue +518 -0
  44. package/components/Feedback/Form.vue +173 -0
  45. package/components/FeedbackDetail.vue +599 -0
  46. package/components/FeedbackMain.vue +588 -0
  47. package/components/FormDialog.vue +65 -0
  48. package/components/ImageCarousel.vue +138 -0
  49. package/components/Input/Date.vue +177 -0
  50. package/components/Input/DateTimePicker.vue +131 -0
  51. package/components/Input/File.vue +236 -0
  52. package/components/Input/FileV2.vue +234 -0
  53. package/components/Input/InputPhoneNumberV2.vue +164 -0
  54. package/components/Input/ListGroupSelection.vue +96 -0
  55. package/components/Input/NRICNumber.vue +53 -0
  56. package/components/Input/NewDate.vue +123 -0
  57. package/components/Input/Number.vue +124 -0
  58. package/components/Input/Password.vue +22 -0
  59. package/components/Input/PhoneNumber.vue +188 -0
  60. package/components/Input/VehicleNumber.vue +49 -0
  61. package/components/InputLabel.vue +22 -0
  62. package/components/InvitationForm.vue +359 -0
  63. package/components/InvitationMain.vue +310 -0
  64. package/components/Layout/Header.vue +129 -0
  65. package/components/Layout/NavigationDrawer.vue +44 -0
  66. package/components/ListItem.vue +35 -0
  67. package/components/ListView.vue +87 -0
  68. package/components/LocalPagination.vue +31 -0
  69. package/components/MemberMain.vue +459 -0
  70. package/components/NFC/NFCPatrolReportMain.vue +591 -0
  71. package/components/NFC/NFCPatrolRouteForm.vue +596 -0
  72. package/components/NFC/NFCPatrolRouteMain.vue +539 -0
  73. package/components/NFC/NFCTagForm.vue +236 -0
  74. package/components/NFC/NFCTagMain.vue +337 -0
  75. package/components/NFC/PatrolSettings.vue +130 -0
  76. package/components/NavigationItem.vue +83 -0
  77. package/components/NumberSettingField.vue +107 -0
  78. package/components/OnlineFormConfigurationForm.vue +290 -0
  79. package/components/OnlineFormsConfiguration.vue +429 -0
  80. package/components/PeopleForm.vue +452 -0
  81. package/components/PlaceholderComponent.vue +34 -0
  82. package/components/RolePermissionFormCreate.vue +161 -0
  83. package/components/RolePermissionFormPreviewUpdate.vue +183 -0
  84. package/components/RolePermissionMain.vue +361 -0
  85. package/components/SearchVehicleNumberUser.vue +91 -0
  86. package/components/ServiceProviderFormCreate.vue +154 -0
  87. package/components/ServiceProviderMain.vue +547 -0
  88. package/components/SignaturePad.vue +73 -0
  89. package/components/Snackbar.vue +23 -0
  90. package/components/SpecificAttr.vue +53 -0
  91. package/components/SupplyManagement.vue +292 -0
  92. package/components/SwitchContext.vue +108 -0
  93. package/components/TableList.vue +150 -0
  94. package/components/TableListSecondary.vue +164 -0
  95. package/components/TableMain.vue +142 -0
  96. package/components/TableWithButton.vue +94 -0
  97. package/components/VehicleUpdateMoreAction.vue +84 -0
  98. package/components/VideoPlayer.vue +125 -0
  99. package/components/VisitorForm.vue +659 -0
  100. package/components/VisitorFormSelection.vue +53 -0
  101. package/components/VisitorManagement.vue +490 -0
  102. package/components/WorkOrder/Create.vue +284 -0
  103. package/components/WorkOrder/Detail.vue +71 -0
  104. package/components/WorkOrder/ListView.vue +96 -0
  105. package/components/WorkOrder/Main.vue +489 -0
  106. package/components/Workorder.vue +1 -0
  107. package/composables/useAddress.ts +107 -0
  108. package/composables/useBuilding.ts +250 -0
  109. package/composables/useBuildingUnit.ts +116 -0
  110. package/composables/useCard.ts +46 -0
  111. package/composables/useCommonPermission.ts +207 -0
  112. package/composables/useCustomer.ts +113 -0
  113. package/composables/useCustomerSite.ts +56 -0
  114. package/composables/useDashboard.ts +31 -0
  115. package/composables/useDashboardData.ts +425 -0
  116. package/composables/useDocument.ts +57 -0
  117. package/composables/useFacility.ts +246 -0
  118. package/composables/useFeedback.ts +119 -0
  119. package/composables/useFile.ts +55 -0
  120. package/composables/useInvoice.ts +18 -0
  121. package/composables/useLocal.ts +131 -0
  122. package/composables/useLocalAuth.ts +137 -0
  123. package/composables/useLocalSetup.ts +13 -0
  124. package/composables/useMember.ts +111 -0
  125. package/composables/useNFCPatrolRoute.ts +77 -0
  126. package/composables/useNFCPatrolSettings.ts +19 -0
  127. package/composables/useNFCPatrolTag.ts +53 -0
  128. package/composables/useOnlineForm.ts +67 -0
  129. package/composables/useOrg.ts +129 -0
  130. package/composables/usePDFDownload.ts +25 -0
  131. package/composables/usePaymentMethod.ts +101 -0
  132. package/composables/usePeople.ts +81 -0
  133. package/composables/usePermission.ts +54 -0
  134. package/composables/usePhoneCountries.ts +561 -0
  135. package/composables/usePrice.ts +15 -0
  136. package/composables/usePromoCode.ts +36 -0
  137. package/composables/useRecapPermission.ts +26 -0
  138. package/composables/useRole.ts +104 -0
  139. package/composables/useSecurityUtils.ts +18 -0
  140. package/composables/useServiceProvider.ts +224 -0
  141. package/composables/useSite.ts +109 -0
  142. package/composables/useSiteEntryPassSettings.ts +46 -0
  143. package/composables/useSiteSettings.ts +123 -0
  144. package/composables/useSubscription.ts +150 -0
  145. package/composables/useUser.ts +132 -0
  146. package/composables/useUtils.ts +445 -0
  147. package/composables/useVerification.ts +34 -0
  148. package/composables/useVisitor.ts +120 -0
  149. package/composables/useWorkOrder.ts +85 -0
  150. package/error.vue +41 -0
  151. package/layouts/plain.vue +7 -0
  152. package/middleware/01.auth.ts +20 -0
  153. package/middleware/02.org.ts +21 -0
  154. package/middleware/03.customer.ts +13 -0
  155. package/middleware/member.ts +4 -0
  156. package/nuxt.config.ts +54 -0
  157. package/package.json +39 -0
  158. package/pages/index.vue +3 -0
  159. package/pages/payment-method-linked.vue +31 -0
  160. package/pages/require-customer.vue +56 -0
  161. package/pages/require-organization-membership.vue +47 -0
  162. package/pages/unauthorized.vue +29 -0
  163. package/plugins/API.ts +21 -0
  164. package/plugins/iconify.client.ts +5 -0
  165. package/plugins/secure-member.client.ts +86 -0
  166. package/plugins/vuetify.ts +62 -0
  167. package/public/bg-camera.jpg +0 -0
  168. package/public/bg-city.jpg +0 -0
  169. package/public/bg-condo.jpg +0 -0
  170. package/public/images/icons/delete-icon.png +0 -0
  171. package/public/sprite.svg +1 -0
  172. package/tsconfig.json +3 -0
  173. package/types/address.d.ts +13 -0
  174. package/types/building.d.ts +27 -0
  175. package/types/camera.d.ts +31 -0
  176. package/types/card.d.ts +22 -0
  177. package/types/customer.d.ts +27 -0
  178. package/types/document.d.ts +6 -0
  179. package/types/feedback.d.ts +68 -0
  180. package/types/local.d.ts +74 -0
  181. package/types/member.d.ts +21 -0
  182. package/types/online-form.d.ts +15 -0
  183. package/types/org.d.ts +13 -0
  184. package/types/people.d.ts +24 -0
  185. package/types/permission.d.ts +25 -0
  186. package/types/phone-number.d.ts +10 -0
  187. package/types/price.d.ts +17 -0
  188. package/types/promo-code.d.ts +19 -0
  189. package/types/role.d.ts +11 -0
  190. package/types/select.d.ts +4 -0
  191. package/types/service-provider.d.ts +15 -0
  192. package/types/site.d.ts +20 -0
  193. package/types/subscription.d.ts +23 -0
  194. package/types/user.d.ts +19 -0
  195. package/types/verification.d.ts +20 -0
  196. package/types/visitor.d.ts +42 -0
  197. package/types/work-order.d.ts +42 -0
  198. package/utils/phoneMasks.ts +1703 -0
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6" align="center">
5
+ <span class="font-weight-bold text-h5 text-capitalize">
6
+ Add Visitor
7
+ </span>
8
+ </v-row>
9
+ </v-toolbar>
10
+
11
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-5">
12
+ <span class="text-subtitle-2 w-100 font-weight-medium d-flex justify-center mb-3">Please Select Visitor Type</span>
13
+ <template v-for="item in selection" :key="item.value">
14
+ <v-btn color="primary-button" block variant="flat" rounded="md" size="48" :text="item.label" @click="select(item.value)" class="my-2 text-capitalize text-subtitle-2" />
15
+ </template>
16
+ </v-card-text>
17
+
18
+ <v-toolbar density="compact">
19
+ <v-row no-gutters>
20
+ <v-col cols="12">
21
+ <v-btn
22
+ tile
23
+ block
24
+ variant="text"
25
+ class="text-none"
26
+ size="48"
27
+ @click="cancel"
28
+ >
29
+ Cancel
30
+ </v-btn>
31
+ </v-col>
32
+ </v-row>
33
+ </v-toolbar>
34
+ </v-card>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ const prop = defineProps({
39
+ });
40
+
41
+ const emit = defineEmits(['cancel', 'select']);
42
+ const { visitorSelection: selection} = useVisitor();
43
+
44
+
45
+
46
+ function cancel() {
47
+ emit("cancel");
48
+ }
49
+
50
+ function select(value: string) {
51
+ emit("select", value);
52
+ }
53
+ </script>
@@ -0,0 +1,490 @@
1
+ <template>
2
+ <v-row no-gutters>
3
+ <TableMain :headers="headers" :items="items" :loading="getVisitorPending" :page="page" :pages="pages"
4
+ :extension-height="110" :offset="300" :pageRange="pageRange" :canCreate="canAddVisitor"
5
+ @refresh="getVisitorRefresh" @update:page="handleUpdatePage" createLabel="Add Visitor" show-header
6
+ @row-click="handleRowClick" @create="dialog.showSelection = true">
7
+ <template #extension>
8
+ <v-row no-gutters class="w-100 d-flex flex-column">
9
+ <v-tabs v-model="activeTab" color="primary" :height="40" @update:model-value="toRoute" class="w-100">
10
+ <v-tab v-for="tab in tabOptions" :value="tab.status" :key="tab.status" class="text-capitalize">
11
+ {{ tab.name }}
12
+ </v-tab>
13
+ </v-tabs>
14
+
15
+ <v-card class="w-100 px-3 d-flex align-center ga-5 py-2" flat :height="60">
16
+ <v-text-field v-model="searchInput" density="compact" placeholder="Search" clearable max-width="300"
17
+ append-inner-icon="mdi-magnify" hide-details />
18
+ <v-checkbox v-model="displayNotCheckedOut" class="text-subtitle-2" hide-details>
19
+ <template #label>
20
+ <span class="text-caption">Not Checked Out</span>
21
+ </template>
22
+ </v-checkbox>
23
+ <InputDateTimePicker v-model:utc="dateFrom" density="compact" hide-details />
24
+ <InputDateTimePicker v-model:utc="dateTo" density="compact" hide-details />
25
+ <v-select v-model="filterTypes" label="Filter by types" item-title="label" item-value="value"
26
+ :items="visitorSelection" density="compact" clearable multiple max-width="200" hide-details>
27
+ <template v-slot:selection="{ item, index }">
28
+ <div class="d-flex align-center text-caption text-nowrap">
29
+ <v-chip v-if="index === 0" color="error" :text="filterTypeSelectionLabel()" size="x-small"
30
+ variant="tonal" class="ml-2" />
31
+ </div>
32
+ </template>
33
+ </v-select>
34
+ </v-card>
35
+ </v-row>
36
+ </template>
37
+
38
+ <template v-slot:item.name="{ item }">
39
+ <span class="d-flex align-center ga-2">
40
+ <span>
41
+ <AvatarMain :name="item?.name" :size="20" :id="item?._id" />
42
+ </span>
43
+ <span class="text-capitalize">{{ item?.name }}</span>
44
+ </span>
45
+ </template>
46
+
47
+ <template v-slot:item.type-company="{ item }">
48
+ <span class="d-flex align-center ga-2">
49
+ <v-icon icon="mdi-user" size="15" />
50
+ <span v-if="item.type === 'contractor'" class="text-capitalize">{{
51
+ formatCamelCaseToWords(item.contractorType)
52
+ }}</span>
53
+ <span v-else class="text-capitalize">{{ formatType(item) }}</span>
54
+ </span>
55
+ <span class="d-flex align-center ga-2">
56
+ <v-icon icon="mdi-domain" size="15" />
57
+ <span class="text-capitalize">{{ item?.company || "N/A" }}</span>
58
+ </span>
59
+ </template>
60
+
61
+ <template v-slot:item.location="{ item }">
62
+ <span class="d-flex align-center ga-2">
63
+ <v-icon icon="mdi-storefront-outline" size="15" />
64
+ <span class="text-capitalize">{{
65
+ formatLocation({
66
+ block: item.block,
67
+ level: item.level,
68
+ unit: item.unitName,
69
+ })
70
+ }}</span>
71
+ </span>
72
+ </template>
73
+
74
+ <template v-slot:item.contact-vehicleNumber="{ item }">
75
+ <span class="d-flex align-center ga-2">
76
+ <v-icon icon="mdi-phone" size="15" />
77
+ <span class="text-capitalize">{{ item?.contact || "N/A" }}</span>
78
+ </span>
79
+ <span class="d-flex align-center ga-2">
80
+ <v-icon icon="mdi-car-back" size="15" />
81
+ <span class="text-capitalize">{{ item?.plateNumber || "N/A" }}</span>
82
+ </span>
83
+ </template>
84
+
85
+ <template v-slot:item.checkin-out="{ item }">
86
+ <span class="d-flex align-center ga-2">
87
+ <v-icon icon="mdi-clock-time-four-outline" color="green" size="20" />
88
+ <span class="text-capitalize">{{
89
+ UTCToLocalTIme(item.checkIn) || "-"
90
+ }}</span>
91
+ </span>
92
+ <span class="d-flex align-center ga-2">
93
+ <v-icon icon="mdi-clock-time-eight-outline" color="red" size="20" v-if="item.checkOut" />
94
+ <template v-if="item.checkOut">
95
+ <span class="text-capitalize">{{
96
+ UTCToLocalTIme(item.checkOut) || "_"
97
+ }}</span>
98
+ <span v-if="item?.manualCheckout">
99
+ <TooltipInfo text="Manual Checkout" density="compact" size="x-small" />
100
+ </span>
101
+ </template>
102
+ <span v-else>
103
+ <v-btn size="x-small" class="text-capitalize" color="red" text="Checkout"
104
+ :loading="loading.checkingOut && item?._id === selectedVisitorId" @click.stop="handleCheckout(item._id)"
105
+ v-if="canCheckoutVisitor" />
106
+ </span>
107
+ </span>
108
+ </template>
109
+ </TableMain>
110
+
111
+ <v-dialog v-model="dialog.showSelection" width="450" persistent>
112
+ <VisitorFormSelection @cancel="dialog.showSelection = false" @select="handleSelectVisitorType" />
113
+ </v-dialog>
114
+
115
+ <v-dialog v-model="dialog.addVisitor" v-if="activeVisitorFormType" width="450" persistent>
116
+ <VisitorForm mode="add" :org="orgId" :site="siteId" :type="activeVisitorFormType" @back="handleClickBack"
117
+ @done="handleVisitorFormDone" @done:more="handleVisitorFormCreateMore" />
118
+ </v-dialog>
119
+
120
+ <v-dialog v-model="dialog.viewVisitor" width="450" persistent>
121
+ <VehicleUpdateMoreAction title="Preview" :can-update="false" :can-delete="canDeleteVisitor"
122
+ @close="dialog.viewVisitor = false" edit-button-label="Edit Visitor" delete-button-label="Delete Visitor"
123
+ @delete="handleDeleteVisitor">
124
+ <template v-slot:content>
125
+ <v-row no-gutters class="mb-4">
126
+ <v-col v-for="(label, key) in formattedFields" :key="key" cols="12">
127
+ <span v-if="
128
+ key === 'checkOut' &&
129
+ !selectedVisitorObject[key] &&
130
+ canCheckoutVisitor
131
+ " class="d-flex align-center">
132
+ <strong>{{ label }}:</strong>
133
+ <v-btn size="x-small" class="ml-3 text-capitalize" color="red" text="Checkout"
134
+ :disabled="loading.checkingOut" @click="handleCheckout(selectedVisitorId as string)" />
135
+ </span>
136
+
137
+ <span v-else-if="selectedVisitorObject[key]" class="d-flex ga-3 align-center"><strong>{{ label
138
+ }}:</strong>
139
+ {{ formatValues(key, selectedVisitorObject[key]) }}
140
+ <TooltipInfo v-if="key === 'checkOut'" text="Manual Checkout" density="compact" size="x-small" />
141
+ </span>
142
+ </v-col>
143
+ </v-row>
144
+ </template>
145
+ </VehicleUpdateMoreAction>
146
+ </v-dialog>
147
+
148
+ <v-dialog v-model="dialog.vehicleNumberUsersList" persistent max-width="600">
149
+ <SearchVehicleNumberUser :vehicle-number="'123123'" @close="dialog.vehicleNumberUsersList = false" @update:people="handleUpdateAutofillDetails"/>
150
+ </v-dialog>
151
+
152
+ <v-dialog v-model="dialog.deleteConfirmation" width="450" persistent>
153
+ <CardDeleteConfirmation prompt-title="Are you sure want to delete this visitor?"
154
+ :loading="loading.deletingVisitor" @close="dialog.deleteConfirmation = false"
155
+ @delete="handleProceedDeleteVisitor" />
156
+ </v-dialog>
157
+
158
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
159
+ </v-row>
160
+ </template>
161
+
162
+ <script setup lang="ts">
163
+ definePageMeta({
164
+ middleware: ["01-auth", "02-org"],
165
+ });
166
+
167
+ const props = defineProps({
168
+ canAddVisitor: {
169
+ type: Boolean,
170
+ default: true,
171
+ },
172
+ canViewVisitor: {
173
+ type: Boolean,
174
+ default: true,
175
+ },
176
+ canUpdateVisitor: {
177
+ type: Boolean,
178
+ default: true,
179
+ },
180
+ canDeleteVisitor: {
181
+ type: Boolean,
182
+ default: true,
183
+ },
184
+ canCheckoutVisitor: {
185
+ type: Boolean,
186
+ default: true,
187
+ },
188
+ });
189
+
190
+ const headers = [
191
+ { title: "Name", value: "name" },
192
+ { title: "Type/Company", value: "type-company" },
193
+ { title: "Location", value: "location" },
194
+ { title: "Contact/Vehicle No.", value: "contact-vehicleNumber" },
195
+ { title: "Check In/Out", value: "checkin-out" },
196
+ ];
197
+
198
+ const {
199
+ getVisitors,
200
+ visitorSelection,
201
+ typeFieldMap,
202
+ deleteVisitor,
203
+ updateVisitor,
204
+ } = useVisitor();
205
+ const { debounce, formatCamelCaseToWords, formatDate, UTCToLocalTIme } =
206
+ useUtils();
207
+ const { formatLocation } = useSecurityUtils();
208
+ // const { status: visitorStatus, search } = useRoute().query as { status: string, search: string};
209
+
210
+ const route = useRoute()
211
+ const router = useRouter()
212
+ const { org: orgId, site: siteId } = route.params as {
213
+ org: string;
214
+ site: string;
215
+ };
216
+ const routeName = route.name;
217
+
218
+ const items = ref<Array<Record<string, any>>>([]);
219
+ const page = ref(1);
220
+ const pages = ref(0);
221
+ const pageRange = ref("-- - -- of --");
222
+ const activeTab = ref("registered");
223
+ const activeVisitorFormType = ref<TVisitorType | null>(null);
224
+ const selectedVisitorId = ref<string | null>(""); // selected visitor for viewing/actions
225
+
226
+ //filter states
227
+ const searchInput = ref("");
228
+ const dateFrom = ref("");
229
+ const dateTo = ref("");
230
+ const filterTypes = ref<TVisitorType[]>([]);
231
+ const displayNotCheckedOut = ref<boolean>(false);
232
+
233
+ const message = ref("");
234
+ const messageColor = ref("");
235
+ const messageSnackbar = ref(false);
236
+
237
+ const loading = reactive({
238
+ deletingVisitor: false,
239
+ fetchingVisitors: false,
240
+ checkingOut: false,
241
+ });
242
+
243
+ const dialog = reactive({
244
+ showSelection: false,
245
+ addVisitor: false,
246
+ viewVisitor: false,
247
+ deleteConfirmation: false,
248
+ vehicleNumberUsersList: false
249
+ });
250
+
251
+ const tabOptions = [
252
+ { name: "Registered", status: "registered" },
253
+ { name: "Unregistered", status: "unregistered" },
254
+ ];
255
+
256
+ const formatType = (item: any) =>
257
+ (item.deliveryType ? item.deliveryType + "-" : "") + item.type;
258
+
259
+ const formattedFields = {
260
+ name: "Name",
261
+ nric: "NRIC",
262
+ contact: "Phone Number",
263
+ plateNumber: "Vehicle Number",
264
+ company: "Company",
265
+ block: "Block",
266
+ level: "Level",
267
+ unitName: "Unit",
268
+ checkIn: "Check In",
269
+ checkOut: "Check Out",
270
+ remarks: "Remarks",
271
+ } as const;
272
+
273
+ function filterTypeSelectionLabel() {
274
+ const length = filterTypes.value.length;
275
+ return `${length} selected ${length === 1 ? "type" : "types"}` as string;
276
+ }
277
+
278
+ function toRoute(status: any) {
279
+ const obj = tabOptions.find((x) => x.status === status);
280
+ if (!obj) return;
281
+ page.value = 1
282
+ navigateTo({
283
+ name: routeName,
284
+ params: {
285
+ org: orgId,
286
+ },
287
+ query: {
288
+ status: obj.status,
289
+ },
290
+ });
291
+ }
292
+
293
+ const {
294
+ data: getVisitorReq,
295
+ refresh: getVisitorRefresh,
296
+ pending: getVisitorPending,
297
+ } = await useLazyAsyncData(
298
+ `get-all-visitors-${activeTab.value}-${page.value}-${siteId}`
299
+ ,
300
+ () =>
301
+ getVisitors({
302
+ page: page.value,
303
+ site: siteId,
304
+ search: searchInput.value,
305
+ dateTo: dateTo.value,
306
+ dateFrom: dateFrom.value,
307
+ type: filterTypes.value.filter(Boolean).join(","),
308
+ status: activeTab.value as string,
309
+ checkedOut: displayNotCheckedOut.value ? false : undefined
310
+ }),
311
+ {
312
+ watch: [page, () => route.query],
313
+ }
314
+ );
315
+
316
+ watch(getVisitorReq, (newData: any) => {
317
+ if (newData) {
318
+ items.value = newData.items ?? [];
319
+ pages.value = newData.pages ?? 0;
320
+ pageRange.value = newData?.pageRange ?? "-- - -- of --";
321
+ }
322
+ });
323
+
324
+ const selectedVisitorObject = computed(() => {
325
+ const obj = items.value.find((x: any) => x?._id === selectedVisitorId.value);
326
+ if (!obj) return {};
327
+ const type = obj?.type as TVisitorType | undefined;
328
+ if (!type) return {};
329
+ let includedKeys: string[] = ["checkIn", "checkOut"];
330
+ includedKeys.unshift(...(typeFieldMap[type] ?? []));
331
+ return Object.fromEntries(
332
+ Object.entries(obj).filter(([key]) => includedKeys.includes(key))
333
+ );
334
+ });
335
+
336
+ function formatValues(key: string, value: any) {
337
+ if (!value) return "";
338
+ switch (key) {
339
+ case "unit":
340
+ return value?.name;
341
+ case "checkIn":
342
+ return formatDate(value);
343
+ case "checkOut":
344
+ return formatDate(value);
345
+ }
346
+
347
+ return value;
348
+ }
349
+
350
+ function handleRowClick(data: any) {
351
+ selectedVisitorId.value = data?.item?._id;
352
+ dialog.viewVisitor = true;
353
+ }
354
+
355
+ function handleUpdatePage(newPageNum: number) {
356
+ page.value = newPageNum;
357
+ }
358
+
359
+ function handleSelectVisitorType(type: TVisitorType) {
360
+ dialog.showSelection = false;
361
+ dialog.addVisitor = true;
362
+ activeVisitorFormType.value = type;
363
+ }
364
+
365
+ function handleClickBack() {
366
+ dialog.showSelection = true;
367
+ dialog.addVisitor = false;
368
+ }
369
+
370
+ function handleVisitorFormDone() {
371
+ getVisitorRefresh();
372
+ dialog.showSelection = false;
373
+ dialog.addVisitor = false;
374
+ }
375
+
376
+ function handleVisitorFormCreateMore() {
377
+ getVisitorRefresh();
378
+ dialog.showSelection = true;
379
+ dialog.addVisitor = false;
380
+ }
381
+
382
+ function handleDeleteVisitor() {
383
+ dialog.deleteConfirmation = true;
384
+ dialog.viewVisitor = false;
385
+ }
386
+
387
+ function showMessage(msg: string, color: string) {
388
+ message.value = msg;
389
+ messageColor.value = color;
390
+ messageSnackbar.value = true;
391
+ }
392
+
393
+ function handleUpdateAutofillDetails(people: TPeople){
394
+ dialog.vehicleNumberUsersList = false
395
+ console.log('people', people)
396
+ }
397
+
398
+ async function handleProceedDeleteVisitor() {
399
+ try {
400
+ loading.deletingVisitor = true;
401
+ const userId = selectedVisitorId.value;
402
+ const res = await deleteVisitor(userId as string);
403
+ if (res) {
404
+ showMessage("Visitor successfully deleted!", "info");
405
+ await getVisitorRefresh();
406
+ dialog.deleteConfirmation = false;
407
+ }
408
+ } catch (error: any) {
409
+ const errorMessage = error?.response?._data?.message;
410
+ console.log("[ERROR]", error);
411
+ showMessage(
412
+ errorMessage || "Something went wrong. Please try again later.",
413
+ "error"
414
+ );
415
+ } finally {
416
+ loading.deletingVisitor = false;
417
+ }
418
+ }
419
+
420
+ async function handleCheckout(userId: string) {
421
+ if (!userId) {
422
+ showMessage("Invalid userId", "error");
423
+ return;
424
+ }
425
+ selectedVisitorId.value = userId;
426
+
427
+ try {
428
+ loading.checkingOut = true;
429
+ const res = await updateVisitor(userId as string, {
430
+ checkOut: new Date().toISOString(),
431
+ });
432
+ if (res) {
433
+ showMessage("Visitor successfully checked-out!", "info");
434
+ await getVisitorRefresh();
435
+ dialog.viewVisitor = false;
436
+ }
437
+ } catch (error: any) {
438
+ const errorMessage = error?.response?._data?.message;
439
+ console.log("[ERROR]", error);
440
+ showMessage(
441
+ errorMessage || "Something went wrong. Please try again later.",
442
+ "error"
443
+ );
444
+ } finally {
445
+ loading.checkingOut = false;
446
+ }
447
+ }
448
+
449
+
450
+
451
+ const updateRouteQuery = debounce(
452
+ (search: string, from: string, to: string, types: string[], status, checkedOut) => {
453
+ router.replace({
454
+ query: {
455
+ ...route.query,
456
+ search: search || undefined,
457
+ dateFrom: from || undefined,
458
+ dateTo: to || undefined,
459
+ type: types.filter(Boolean).join(",") || undefined,
460
+ status: status || undefined,
461
+ ...(checkedOut === false ? { checkedOut: "false" } : { checkedOut: undefined })
462
+ },
463
+ });
464
+ },
465
+ 500 // wait 500ms after last input
466
+ );
467
+
468
+ watch(
469
+ [searchInput, dateFrom, dateTo, filterTypes, activeTab, displayNotCheckedOut],
470
+ ([search, from, to, types, status, checkedOut]) => {
471
+ updateRouteQuery(search, from, to, types, status, checkedOut)
472
+
473
+ },
474
+ { deep: true }
475
+ );
476
+
477
+
478
+
479
+ onMounted(() => {
480
+ activeTab.value = (route.query.status as string) || "unregistered"
481
+ searchInput.value = (route.query.search as string) || "";
482
+ dateFrom.value = (route.query.dateFrom as string) || "";
483
+ dateTo.value = (route.query.dateTo as string) || "";
484
+ filterTypes.value = ((route.query.type as string)?.split(",") || []).filter(Boolean) as TVisitorType[];
485
+ displayNotCheckedOut.value = !!(route.query.checkedOut as string) || false
486
+
487
+ })
488
+ </script>
489
+
490
+ <style scoped></style>