@7365admin1/layer-common 3.0.21 → 3.0.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/DashboardMain.vue +211 -58
  3. package/components/InvitationMain.vue +32 -3
  4. package/components/MemberMain.vue +1 -1
  5. package/components/Nfc/NFCPatrolReportMain.vue +48 -541
  6. package/components/Nfc/PatrolReport/DailyReportTab.vue +59 -0
  7. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +59 -0
  8. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +81 -0
  9. package/components/Nfc/PatrolReport/PatrolReportTab.vue +73 -0
  10. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +63 -0
  11. package/components/Nfc/PatrolReport/RemarksDialog.vue +37 -0
  12. package/components/Nfc/PatrolReport/ReportEmptyState.vue +24 -0
  13. package/components/Nfc/PatrolReport/ReportFilters.vue +88 -0
  14. package/components/Nfc/PatrolReport/ReportLocationHeader.vue +17 -0
  15. package/components/Nfc/PatrolReport/SummaryReportTable.vue +51 -0
  16. package/components/ScheduleTaskMain.vue +47 -12
  17. package/components/ServiceProviderMain.vue +182 -184
  18. package/components/UnitMain.vue +8 -1
  19. package/components/VisitorForm.vue +2 -0
  20. package/components/VisitorManagement.vue +31 -0
  21. package/components/VisitorSocketPopUp.vue +292 -0
  22. package/composables/useNFCPatrolDailyReport.ts +27 -0
  23. package/composables/useNFCPatrolMonthlyReport.ts +27 -0
  24. package/composables/useNFCPatrolReport.ts +38 -0
  25. package/composables/useNFCPatrolReportExport.ts +31 -0
  26. package/composables/useNFCPatrolReportFilters.ts +39 -0
  27. package/composables/useVisitor.ts +3 -0
  28. package/composables/useVisitorSocket.ts +25 -0
  29. package/package.json +2 -1
  30. package/services/nfcPatrolReport.ts +172 -0
  31. package/types/nfc-patrol-report.ts +73 -0
  32. package/types/verification.d.ts +6 -1
@@ -181,7 +181,6 @@
181
181
  <v-card-text v-else class="text-body-2 text-medium-emphasis">
182
182
  You do not have permission to view service providers.
183
183
  </v-card-text>
184
-
185
184
  </v-card>
186
185
  </v-col>
187
186
  </v-row>
@@ -415,151 +414,151 @@
415
414
  </v-card>
416
415
  </v-dialog>
417
416
 
418
- <v-dialog v-model="createInviteDialog" width="580" persistent>
419
- <v-card width="100%" rounded="lg" class="sp-invite-card">
420
- <v-toolbar flat color="grey-lighten-4" height="76">
421
- <v-row no-gutters class="fill-height px-6" align="center">
422
- <span class="font-weight-bold text-h5">
423
- Invite Service Provider
424
- </span>
425
- </v-row>
426
- </v-toolbar>
427
-
428
- <v-card-text class="sp-invite-body">
429
- <v-form
430
- v-model="validServiceProvider"
431
- :disabled="disableServiceProvider"
432
- >
433
- <v-row no-gutters>
434
- <!-- Email -->
435
- <v-col cols="12">
436
- <v-row no-gutters>
437
- <InputLabel class="text-capitalize" title="Email" required />
438
- <v-col cols="12">
439
- <v-text-field
440
- v-model="serviceProviderInvite.email"
441
- variant="outlined"
442
- density="comfortable"
443
- :rules="[requiredRule, emailRule]"
444
- ></v-text-field>
445
- </v-col>
446
- </v-row>
447
- </v-col>
448
-
449
- <!-- Existing account block -->
450
- <v-col cols="12" v-if="existingAccount">
451
- <v-divider class="mb-6" />
452
-
453
- <div class="sp-existing-title">Existing Account Verified</div>
454
-
455
- <div class="sp-existing-copy">
456
- {{ serviceProviderInvite.email }} is already onboarded.
457
- Sending this invite will add this site to the list of
458
- locations this company manages.
459
- </div>
460
-
461
- <v-row no-gutters>
462
- <InputLabel title="Organization" />
463
- <v-col cols="12">
464
- <v-text-field
465
- :model-value="existingAccount.text"
466
- readonly
467
- variant="outlined"
468
- density="comfortable"
469
- />
470
- </v-col>
471
- </v-row>
472
- </v-col>
473
-
474
- <v-col cols="12">
475
- <v-divider class="mb-6" />
476
- </v-col>
417
+ <v-dialog v-model="createInviteDialog" width="580" persistent>
418
+ <v-card width="100%" rounded="lg" class="sp-invite-card">
419
+ <v-toolbar flat color="grey-lighten-4" height="76">
420
+ <v-row no-gutters class="fill-height px-6" align="center">
421
+ <span class="font-weight-bold text-h5">
422
+ Invite Service Provider
423
+ </span>
424
+ </v-row>
425
+ </v-toolbar>
477
426
 
478
- <!-- App & Site: only shown when no existing account is verified -->
479
- <template v-if="!existingAccount">
427
+ <v-card-text class="sp-invite-body">
428
+ <v-form
429
+ v-model="validServiceProvider"
430
+ :disabled="disableServiceProvider"
431
+ >
432
+ <v-row no-gutters>
433
+ <!-- Email -->
480
434
  <v-col cols="12">
481
435
  <v-row no-gutters>
482
- <InputLabel class="text-capitalize" title="App" required />
436
+ <InputLabel class="text-capitalize" title="Email" required />
483
437
  <v-col cols="12">
484
- <v-select
485
- v-model="serviceProviderInvite.app"
486
- :items="serviceProviderInviteApps"
487
- item-title="title"
488
- item-value="value"
438
+ <v-text-field
439
+ v-model="serviceProviderInvite.email"
489
440
  variant="outlined"
490
441
  density="comfortable"
491
- :rules="[requiredRule]"
492
- ></v-select>
442
+ :rules="[requiredRule, emailRule]"
443
+ ></v-text-field>
493
444
  </v-col>
494
445
  </v-row>
495
446
  </v-col>
496
447
 
497
- <v-col cols="12">
448
+ <!-- Existing account block -->
449
+ <v-col cols="12" v-if="existingAccount">
450
+ <v-divider class="mb-6" />
451
+
452
+ <div class="sp-existing-title">Existing Account Verified</div>
453
+
454
+ <div class="sp-existing-copy">
455
+ {{ serviceProviderInvite.email }} is already onboarded. Sending
456
+ this invite will add this site to the list of locations this
457
+ company manages.
458
+ </div>
459
+
498
460
  <v-row no-gutters>
499
- <InputLabel class="text-capitalize" title="Site" required />
461
+ <InputLabel title="Organization" />
500
462
  <v-col cols="12">
501
- <v-select
502
- v-model="serviceProviderInvite.siteId"
503
- :items="serviceProviderInviteSites"
504
- item-title="title"
505
- item-value="value"
463
+ <v-text-field
464
+ :model-value="existingAccount.text"
465
+ readonly
506
466
  variant="outlined"
507
467
  density="comfortable"
508
- :rules="[requiredRule]"
509
- @update:model-value="setInviteSiteName"
510
- ></v-select>
468
+ />
511
469
  </v-col>
512
470
  </v-row>
513
471
  </v-col>
514
- </template>
515
472
 
516
- <!-- Error message -->
517
- <v-col cols="12" class="my-2">
518
- <v-row no-gutters>
519
- <v-col cols="12" class="text-center">
520
- <span
521
- class="text-none text-subtitle-2 font-weight-medium text-error"
522
- >
523
- {{ messageServiceProvider }}
524
- </span>
473
+ <v-col cols="12">
474
+ <v-divider class="mb-6" />
475
+ </v-col>
476
+
477
+ <!-- App & Site: only shown when no existing account is verified -->
478
+ <template v-if="!existingAccount">
479
+ <v-col cols="12">
480
+ <v-row no-gutters>
481
+ <InputLabel class="text-capitalize" title="App" required />
482
+ <v-col cols="12">
483
+ <v-select
484
+ v-model="serviceProviderInvite.app"
485
+ :items="serviceProviderInviteApps"
486
+ item-title="title"
487
+ item-value="value"
488
+ variant="outlined"
489
+ density="comfortable"
490
+ :rules="[requiredRule]"
491
+ ></v-select>
492
+ </v-col>
493
+ </v-row>
525
494
  </v-col>
526
- </v-row>
527
- </v-col>
528
- </v-row>
529
- </v-form>
530
- </v-card-text>
531
495
 
532
- <v-toolbar flat height="76" class="sp-invite-footer">
533
- <v-row no-gutters class="fill-height">
534
- <v-col cols="6" class="fill-height">
535
- <v-btn
536
- block
537
- variant="flat"
538
- class="text-none sp-invite-footer-btn"
539
- height="76"
540
- @click="setServiceProvider({ mode: 'invite', dialog: false })"
541
- >
542
- Back
543
- </v-btn>
544
- </v-col>
496
+ <v-col cols="12">
497
+ <v-row no-gutters>
498
+ <InputLabel class="text-capitalize" title="Site" required />
499
+ <v-col cols="12">
500
+ <v-select
501
+ v-model="serviceProviderInvite.siteId"
502
+ :items="serviceProviderInviteSites"
503
+ item-title="title"
504
+ item-value="value"
505
+ variant="outlined"
506
+ density="comfortable"
507
+ :rules="[requiredRule]"
508
+ @update:model-value="setInviteSiteName"
509
+ ></v-select>
510
+ </v-col>
511
+ </v-row>
512
+ </v-col>
513
+ </template>
545
514
 
546
- <v-col cols="6" class="fill-height">
547
- <v-btn
548
- block
549
- variant="flat"
550
- class="text-none sp-invite-submit-btn"
551
- height="76"
552
- :disabled="!validServiceProvider"
553
- :loading="disableServiceProvider"
554
- @click="submitServiceProviderInvite()"
555
- >
556
- Submit
557
- </v-btn>
558
- </v-col>
559
- </v-row>
560
- </v-toolbar>
561
- </v-card>
562
- </v-dialog>
515
+ <!-- Error message -->
516
+ <v-col cols="12" class="my-2">
517
+ <v-row no-gutters>
518
+ <v-col cols="12" class="text-center">
519
+ <span
520
+ class="text-none text-subtitle-2 font-weight-medium text-error"
521
+ >
522
+ {{ messageServiceProvider }}
523
+ </span>
524
+ </v-col>
525
+ </v-row>
526
+ </v-col>
527
+ </v-row>
528
+ </v-form>
529
+ </v-card-text>
530
+
531
+ <v-toolbar flat height="76" class="sp-invite-footer">
532
+ <v-row no-gutters class="fill-height">
533
+ <v-col cols="6" class="fill-height">
534
+ <v-btn
535
+ block
536
+ variant="flat"
537
+ class="text-none sp-invite-footer-btn"
538
+ height="76"
539
+ @click="setServiceProvider({ mode: 'invite', dialog: false })"
540
+ >
541
+ Back
542
+ </v-btn>
543
+ </v-col>
544
+
545
+ <v-col cols="6" class="fill-height">
546
+ <v-btn
547
+ block
548
+ variant="flat"
549
+ class="text-none sp-invite-submit-btn"
550
+ height="76"
551
+ :disabled="!validServiceProvider"
552
+ :loading="disableServiceProvider"
553
+ @click="submitServiceProviderInvite()"
554
+ >
555
+ Submit
556
+ </v-btn>
557
+ </v-col>
558
+ </v-row>
559
+ </v-toolbar>
560
+ </v-card>
561
+ </v-dialog>
563
562
 
564
563
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
565
564
  </template>
@@ -689,7 +688,7 @@ const headers = [
689
688
 
690
689
  const { natureOfBusinessV3 } = useLocal();
691
690
  const { requiredRule, emailRule, debounce } = useUtils();
692
- const {getUserByEmail} = useUser()
691
+ const { getUserByEmail } = useUser();
693
692
 
694
693
  const listTab = ref<ListTab>("active");
695
694
  const page = ref(1);
@@ -732,14 +731,13 @@ const {
732
731
  add: addServiceProvider,
733
732
  createServiceProviderInvite,
734
733
  updateStatus: updateServiceProviderStatus,
735
- invite
734
+ invite,
736
735
  } = useServiceProvider();
737
736
  const { getAll: getAllMembers } = useMember();
738
737
 
739
- const { getVerifications, cancelUserInvitation, getVerificationByEmail } = useVerification();
740
- const {
741
- getOrgsByMembership,
742
- } = useMember();
738
+ const { getVerifications, cancelUserInvitation, getVerificationByEmail } =
739
+ useVerification();
740
+ const { getOrgsByMembership } = useMember();
743
741
 
744
742
  const existingAccount = ref<any>(null);
745
743
  const checkingExistingAccount = ref(false);
@@ -774,22 +772,21 @@ async function checkExistingAccount(email: string) {
774
772
 
775
773
  if (!user?._id) return;
776
774
 
777
- const orgs = await getOrgs({
778
- user: user._id,
779
- });
780
-
781
- const matchedOrg = orgs.items?.find(
782
- (org: any) => org.type === serviceProviderInvite.value.app
783
- );
775
+ const orgs = await getOrgs({
776
+ user: user._id,
777
+ });
784
778
 
785
- if (matchedOrg) {
786
- existingAccount.value = {
787
- _id: matchedOrg.value,
788
- text: matchedOrg.text,
789
- ...matchedOrg,
790
- };
791
- }
779
+ const matchedOrg = orgs.items?.find(
780
+ (org: any) => org.type === serviceProviderInvite.value.app
781
+ );
792
782
 
783
+ if (matchedOrg) {
784
+ existingAccount.value = {
785
+ _id: matchedOrg.value,
786
+ text: matchedOrg.text,
787
+ ...matchedOrg,
788
+ };
789
+ }
793
790
  } catch (error) {
794
791
  existingAccount.value = null;
795
792
  } finally {
@@ -797,7 +794,6 @@ if (matchedOrg) {
797
794
  }
798
795
  }
799
796
 
800
-
801
797
  watch(
802
798
  () => serviceProviderInvite.value.email,
803
799
  async (value) => {
@@ -876,7 +872,10 @@ function rowActionLabel(row: TableRow) {
876
872
 
877
873
  function typeDisplay(type: string, nature?: string) {
878
874
  if (nature && String(nature).trim()) return String(nature);
879
- const list = natureOfBusinessV3.value as Array<{ title?: string; value?: string }>;
875
+ const list = natureOfBusinessV3.value as Array<{
876
+ title?: string;
877
+ value?: string;
878
+ }>;
880
879
  const hit = list?.find((x) => x.value === type);
881
880
  return hit?.title ?? type ?? "-";
882
881
  }
@@ -975,12 +974,12 @@ function mapInviteRows(raw: Record<string, any>[]): TableRow[] {
975
974
  }
976
975
 
977
976
  async function loadList() {
978
- if (!props.siteId || !props.canViewServiceProviders) {
979
- items.value = [];
980
- pages.value = 0;
981
- pageRange.value = "-- - -- of --";
982
- return;
983
- }
977
+ // if (!props.siteId || !props.canViewServiceProviders) {
978
+ // items.value = [];
979
+ // pages.value = 0;
980
+ // pageRange.value = "-- - -- of --";
981
+ // return;
982
+ // }
984
983
 
985
984
  loading.value = true;
986
985
  try {
@@ -1077,7 +1076,8 @@ async function confirmStatusChange() {
1077
1076
  if (!statusActionRow.value) return;
1078
1077
 
1079
1078
  statusUpdating.value = true;
1080
- const nextStatus = statusAction.value === "reactivate" ? "active" : "inactive";
1079
+ const nextStatus =
1080
+ statusAction.value === "reactivate" ? "active" : "inactive";
1081
1081
  try {
1082
1082
  await updateServiceProviderStatus(statusActionRow.value._id, nextStatus);
1083
1083
  showMessage(
@@ -1142,38 +1142,38 @@ function setServiceProvider({
1142
1142
  createDialog.value = dialog;
1143
1143
  }
1144
1144
 
1145
- if (dialog) {
1146
- serviceProvider.value = {
1147
- name: "",
1148
- type: "",
1149
- email: "",
1150
- description: "",
1151
- orgId: props.orgId,
1152
- siteId: props.siteId,
1153
- siteName: site.value?.name ?? "",
1154
- orgName: "Org Name",
1155
- category: mode === "add" ? "internal" : "external",
1156
- };
1157
-
1158
- if (mode === "invite") {
1159
- existingAccount.value = null;
1160
-
1161
- serviceProviderInvite.value = {
1145
+ if (dialog) {
1146
+ serviceProvider.value = {
1147
+ name: "",
1148
+ type: "",
1162
1149
  email: "",
1163
- app: defaultInviteApp,
1150
+ description: "",
1151
+ orgId: props.orgId,
1164
1152
  siteId: props.siteId,
1165
- siteName: site.value?.name || props.siteName,
1153
+ siteName: site.value?.name ?? "",
1154
+ orgName: "Org Name",
1155
+ category: mode === "add" ? "internal" : "external",
1166
1156
  };
1167
- }
1168
1157
 
1169
- createMoreServiceProvider.value = false;
1170
- return;
1171
- }
1158
+ if (mode === "invite") {
1159
+ existingAccount.value = null;
1160
+
1161
+ serviceProviderInvite.value = {
1162
+ email: "",
1163
+ app: defaultInviteApp,
1164
+ siteId: props.siteId,
1165
+ siteName: site.value?.name || props.siteName,
1166
+ };
1167
+ }
1168
+
1169
+ createMoreServiceProvider.value = false;
1170
+ return;
1171
+ }
1172
1172
 
1173
- serviceProvider.value.description = data.description ?? "";
1174
- serviceProvider.value.email = data.email ?? "";
1175
- serviceProvider.value.name = data.name ?? "";
1176
- serviceProvider.value.type = data.type ?? "";
1173
+ serviceProvider.value.description = data.description ?? "";
1174
+ serviceProvider.value.email = data.email ?? "";
1175
+ serviceProvider.value.name = data.name ?? "";
1176
+ serviceProvider.value.type = data.type ?? "";
1177
1177
  }
1178
1178
 
1179
1179
  async function submitServiceProviderAdd() {
@@ -1211,9 +1211,7 @@ async function submitServiceProviderInvite() {
1211
1211
  site.value?.name ||
1212
1212
  props.siteName,
1213
1213
 
1214
- inviteType: existingAccount.value
1215
- ? "create-org"
1216
- : "organization-invite",
1214
+ inviteType: existingAccount.value ? "create-org" : "organization-invite",
1217
1215
  });
1218
1216
 
1219
1217
  showMessage("Invitation sent.", "success");
@@ -197,7 +197,11 @@
197
197
  </v-card>
198
198
  </v-dialog>
199
199
 
200
- <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
200
+ <Snackbar
201
+ v-model="messageSnackbar"
202
+ :text="snackBarMessage"
203
+ :color="messageColor"
204
+ />
201
205
 
202
206
  <input
203
207
  ref="fileInput"
@@ -297,8 +301,11 @@ const dialogMode = ref<"add" | "edit">("add");
297
301
  const editingUnit = ref<Record<string, any>>({});
298
302
  const areaName = ref("");
299
303
 
304
+ const snackBarMessage = ref("");
305
+
300
306
  function showMessage(msg: string, color: string = "error") {
301
307
  message.value = msg;
308
+ snackBarMessage.value = msg;
302
309
  messageColor.value = color;
303
310
  messageSnackbar.value = true;
304
311
  }
@@ -1269,6 +1269,8 @@ async function submit() {
1269
1269
  payload.visitorPass = visitor.visitorPass;
1270
1270
  payload.passKeys = visitor.passKeys;
1271
1271
  }
1272
+
1273
+ delete payload.createdBy;
1272
1274
  }
1273
1275
 
1274
1276
  if (prop.type === "contractor") {
@@ -1604,6 +1604,10 @@ function handleRegistrationUnregisteredVisitor(item: Partial<TVisitor>) {
1604
1604
  selectedVisitorId.value = id;
1605
1605
  dialog.showSelection = true;
1606
1606
  mode.value = "register";
1607
+
1608
+ if(!selectedVisitorDataObject.value){
1609
+ getVisitorRefresh();
1610
+ }
1607
1611
  }
1608
1612
 
1609
1613
  function handleViewImage(imageId: string) {
@@ -2034,6 +2038,33 @@ onMounted(() => {
2034
2038
  (route.query.checkedOut as string) == "true" || false;
2035
2039
  }
2036
2040
  });
2041
+
2042
+ const { socketUnregisteredVisitorTrigger, visitorSocketData } =
2043
+ useVisitorSocket();
2044
+
2045
+ watchEffect(async () => {
2046
+ if (visitorSocketData.value && visitorSocketData.value.reload && activeTab.value === "unregistered") {
2047
+ await getVisitorRefresh();
2048
+ visitorSocketData.value = null;
2049
+ socketUnregisteredVisitorTrigger.value = false;
2050
+ }
2051
+
2052
+ if(visitorSocketData.value && visitorSocketData.value.plateNumber && socketUnregisteredVisitorTrigger.value && activeTab.value === "unregistered" && dialog.showSelection === false) {
2053
+ // await getVisitorRefresh();
2054
+ // visitorSocketData.value = null;
2055
+ // socketUnregisteredVisitorTrigger.value = false;
2056
+ setTimeout(async() => {
2057
+ const { _id } = visitorSocketData.value;
2058
+ await handleRegistrationUnregisteredVisitor({ _id });
2059
+ socketUnregisteredVisitorTrigger.value = false;
2060
+
2061
+ setTimeout(() => {
2062
+ visitorSocketData.value = null;
2063
+ }, 1000)
2064
+ }, 1000)
2065
+ }
2066
+ })
2067
+
2037
2068
  </script>
2038
2069
 
2039
2070
  <style scoped></style>