@7365admin1/layer-common 3.0.6 → 3.0.8

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.
@@ -32,6 +32,9 @@
32
32
  @click:row="handleRowClick"
33
33
  style="max-height: 300px"
34
34
  >
35
+ <template #item.host="{ value }">
36
+ {{ maskHostUrl(value) }}
37
+ </template>
35
38
  </v-data-table>
36
39
  </v-card-text>
37
40
 
@@ -103,7 +106,7 @@
103
106
  <v-col cols="12" class="mb-2">
104
107
  Host:
105
108
  <span class="font-weight-bold">
106
- {{ camera.host }}
109
+ {{ maskHostUrl(camera.host) }}
107
110
  </span>
108
111
  </v-col>
109
112
 
@@ -367,6 +370,19 @@ function handleSuccess() {
367
370
  getCameraRefresh();
368
371
  }
369
372
 
373
+ function maskHostUrl(value?: string | null) {
374
+ if (!value) return "";
375
+
376
+ const text = String(value);
377
+ const protocolMatch = text.match(/^([a-z][a-z\d+\-.]*:\/\/)(.*)$/i);
378
+ const prefix = protocolMatch?.[1] ?? "";
379
+ const rest = protocolMatch?.[2] ?? text;
380
+
381
+ if (rest.length <= 8) return `${prefix}${"*".repeat(rest.length)}`;
382
+
383
+ return `${prefix}${rest.slice(0, 3)}****${rest.slice(-3)}`;
384
+ }
385
+
370
386
  const camera = ref<TSiteCamera>({
371
387
  site: "",
372
388
  host: "",
@@ -52,7 +52,8 @@
52
52
  density="comfortable"
53
53
  divided
54
54
  variant="outlined"
55
- color="blue-grey-darken-4"
55
+ class="bg-white"
56
+ style="border-radius: 8px; border-color: #d9e1e8;"
56
57
  >
57
58
  <v-btn
58
59
  v-for="range in rangeOptions"
@@ -60,6 +61,11 @@
60
61
  :value="range.value"
61
62
  class="text-none"
62
63
  size="small"
64
+ :style="
65
+ selectedRange === range.value
66
+ ? { backgroundColor: '#e2e8f0', color: '#0b2f47', fontWeight: '600' }
67
+ : { backgroundColor: 'transparent', color: '#64748b' }
68
+ "
63
69
  >
64
70
  {{ range.label }}
65
71
  </v-btn>
@@ -74,9 +80,16 @@
74
80
  class="mb-4"
75
81
  />
76
82
 
77
- <v-row dense class="mb-1">
78
- <v-col v-for="card in visibleKpiCards" :key="card.key" cols="12" sm="6">
79
- <v-card flat border rounded="lg" class="pa-4" style="min-height: 158px">
83
+ <v-row dense class="mb-1 align-stretch">
84
+ <v-col
85
+ v-for="card in visibleKpiCards"
86
+ :key="card.key"
87
+ cols="12"
88
+ sm="6"
89
+ md="4"
90
+ class="d-flex"
91
+ >
92
+ <v-card flat border rounded="lg" class="pa-4 flex-grow-1" style="min-height: 158px">
80
93
  <div class="d-flex align-start justify-space-between">
81
94
  <div
82
95
  class="d-inline-flex align-center justify-center rounded-lg"
@@ -121,39 +134,58 @@
121
134
  </v-menu>
122
135
  </div>
123
136
 
124
- <div class="mt-6">
137
+ <div class="mt-6 w-100">
125
138
  <p
126
139
  class="text-caption font-weight-black text-uppercase text-blue-grey-darken-2 mb-3"
127
140
  >
128
141
  {{ card.title }}
129
142
  </p>
130
- <div class="d-flex align-center mb-3" style="min-height: 31px">
131
- <span class="text-h5 font-weight-black text-blue-grey-darken-4">{{
132
- card.value
133
- }}</span>
134
- </div>
135
- <div class="d-flex align-center flex-wrap ga-2">
136
- <span v-if="card.meta" class="text-caption text-blue-grey">{{
137
- card.meta
138
- }}</span>
139
- <v-chip
140
- :color="card.percentage >= 0 ? 'success' : 'error'"
141
- size="x-small"
142
- variant="flat"
143
- class="font-weight-bold"
144
- >
145
- <v-icon
146
- :icon="
147
- card.percentage >= 0
148
- ? 'mdi-arrow-up-right'
149
- : 'mdi-arrow-down-right'
150
- "
151
- size="13"
152
- start
153
- />
154
- {{ Math.abs(card.percentage) }}%
155
- </v-chip>
156
- </div>
143
+ <template
144
+ v-if="
145
+ card.key === 'supplyAlert' && Array.isArray(card.raw?.items)
146
+ "
147
+ >
148
+ <div class="d-flex flex-column ga-2 mt-2">
149
+ <div
150
+ v-for="item in card.raw.items"
151
+ :key="item._id || item.name"
152
+ class="d-flex justify-space-between text-body-2 text-blue-grey-darken-4 font-weight-medium"
153
+ >
154
+ <span>{{ item.name }}</span>
155
+ <span class="text-blue-grey">Balance: {{ item.qty }}</span>
156
+ </div>
157
+ </div>
158
+ </template>
159
+ <template v-else>
160
+ <div class="d-flex align-center mb-3" style="min-height: 31px">
161
+ <span
162
+ class="text-h5 font-weight-black text-blue-grey-darken-4"
163
+ >{{ card.value }}</span
164
+ >
165
+ </div>
166
+ <div class="d-flex align-center flex-wrap ga-2">
167
+ <span v-if="card.meta" class="text-caption text-blue-grey">{{
168
+ card.meta
169
+ }}</span>
170
+ <v-chip
171
+ :color="card.percentage >= 0 ? 'success' : 'error'"
172
+ size="x-small"
173
+ variant="flat"
174
+ class="font-weight-bold"
175
+ >
176
+ <v-icon
177
+ :icon="
178
+ card.percentage >= 0
179
+ ? 'mdi-arrow-up-right'
180
+ : 'mdi-arrow-down-right'
181
+ "
182
+ size="13"
183
+ start
184
+ />
185
+ {{ Math.abs(card.percentage) }}%
186
+ </v-chip>
187
+ </div>
188
+ </template>
157
189
  </div>
158
190
  </v-card>
159
191
  </v-col>
@@ -172,19 +204,18 @@
172
204
  </h2>
173
205
  </div>
174
206
 
175
- <div v-if="hasChartData" class="mt-4">
207
+ <div v-if="hasChartData" class="mt-4" ref="chartContainerRef">
176
208
  <svg
177
- viewBox="0 0 920 260"
178
- preserveAspectRatio="none"
209
+ :viewBox="`0 0 ${chartWidth} 260`"
179
210
  class="d-block w-100"
180
- style="height: 260px"
211
+ style="height: auto; max-height: 260px"
181
212
  >
182
213
  <line
183
214
  v-for="tick in chartTicks"
184
215
  :key="tick"
185
216
  x1="48"
186
217
  :y1="tick"
187
- x2="900"
218
+ :x2="chartWidth - 20"
188
219
  :y2="tick"
189
220
  style="stroke: #e9eef2; stroke-width: 1"
190
221
  />
@@ -320,13 +351,7 @@
320
351
  <div
321
352
  class="d-flex align-center ga-2 text-caption text-blue-grey"
322
353
  >
323
- <v-avatar
324
- size="30"
325
- color="blue-grey-lighten-4"
326
- class="text-caption font-weight-bold text-blue-grey-darken-2"
327
- >
328
- {{ getInitials(item.person) }}
329
- </v-avatar>
354
+ <AvatarMain :name="item.person" :size="30" />
330
355
  <span class="text-truncate" style="max-width: 100px">{{
331
356
  item.person
332
357
  }}</span>
@@ -375,14 +400,8 @@
375
400
  rounded="lg"
376
401
  class="pa-3"
377
402
  >
378
- <div class="d-flex align-center ga-3">
379
- <v-avatar
380
- size="34"
381
- color="blue-grey-lighten-4"
382
- class="text-caption font-weight-bold text-blue-grey-darken-2 flex-shrink-0"
383
- >
384
- {{ getInitials(item.name) }}
385
- </v-avatar>
403
+ <div class="d-flex align-start ga-3">
404
+ <AvatarMain :name="item.name" :size="32" />
386
405
  <div class="flex-grow-1" style="min-width: 0">
387
406
  <div
388
407
  class="text-body-2 font-weight-bold text-blue-grey-darken-4 text-truncate"
@@ -415,59 +434,37 @@
415
434
  action-label="View All"
416
435
  @action="goToDashboardRoute('feedbacks')"
417
436
  >
418
- <div v-if="feedbackItems.length" class="d-flex flex-column ga-3">
419
- <v-card
420
- v-for="item in feedbackItems"
421
- :key="item.id"
422
- flat
423
- border
424
- rounded="lg"
425
- class="pa-3"
426
- >
427
- <div class="d-flex align-center flex-wrap ga-3">
428
- <div class="flex-grow-1" style="min-width: 0">
429
- <div
430
- class="text-body-2 font-weight-bold text-blue-grey-darken-4 text-truncate"
431
- >
432
- {{ item.title }}
433
- </div>
434
- <div class="text-caption text-blue-grey mt-1 text-truncate">
435
- {{ item.subtitle }}
436
- </div>
437
+ <div v-if="feedbackItems.length" class="figma-list mt-2">
438
+ <div v-for="item in feedbackItems" :key="item.id" class="d-flex align-center justify-space-between" style="padding: 16px 20px; border: 1px solid #e2e8f0; border-radius: 12px; margin-bottom: 12px; background-color: #ffffff; box-shadow: 0 1px 2px rgba(0,0,0,0.02);">
439
+ <div style="flex: 1; min-width: 140px;">
440
+ <strong style="display: block; font-size: 15px; font-weight: 700; color: #0b2f47; margin-bottom: 4px;">{{ item.title }}</strong>
441
+ <span style="font-size: 13px; color: #94a3b8;">{{ item.subtitle }}</span>
442
+ </div>
443
+
444
+ <div style="flex: 1; display: flex; align-items: center; gap: 12px;">
445
+ <AvatarMain :name="item.person" :size="36" />
446
+ <span style="font-size: 14px; color: #64748b;">{{ item.person }}</span>
447
+ </div>
448
+
449
+ <div style="flex: 1; display: flex; justify-content: center;">
450
+ <div v-if="getStatusTone(item.status) === 'success'" style="background-color: #72e3a1; color: #0f5132; padding: 6px 16px; border-radius: 20px; font-size: 13px; font-weight: 500; text-transform: capitalize;">
451
+ {{ item.status }}
437
452
  </div>
438
- <div
439
- class="d-flex align-center ga-2 text-caption text-blue-grey"
440
- >
441
- <v-avatar
442
- size="30"
443
- color="blue-grey-lighten-4"
444
- class="text-caption font-weight-bold text-blue-grey-darken-2"
445
- >
446
- {{ getInitials(item.person) }}
447
- </v-avatar>
448
- <span class="text-truncate" style="max-width: 100px">{{
449
- item.person
450
- }}</span>
453
+ <div v-else-if="getStatusTone(item.status) === 'warning'" style="background-color: #fde68a; color: #92400e; padding: 6px 16px; border-radius: 20px; font-size: 13px; font-weight: 500; text-transform: capitalize;">
454
+ {{ item.status }}
451
455
  </div>
452
- <v-chip
453
- size="small"
454
- :color="getStatusTone(item.status)"
455
- variant="tonal"
456
- label
457
- >
456
+ <div v-else-if="getStatusTone(item.status) === 'error'" style="background-color: #fecaca; color: #b91c1c; padding: 6px 16px; border-radius: 20px; font-size: 13px; font-weight: 500; text-transform: capitalize;">
458
457
  {{ item.status }}
459
- </v-chip>
460
- <v-btn
461
- variant="outlined"
462
- color="primary"
463
- size="small"
464
- class="text-none font-weight-bold"
465
- @click="goToListItem(item, 'feedbacks')"
466
- >
467
- View
468
- </v-btn>
458
+ </div>
459
+ <div v-else style="background-color: #e2e8f0; color: #64748b; padding: 6px 16px; border-radius: 20px; font-size: 13px; font-weight: 500; text-transform: capitalize;">
460
+ {{ item.status || 'To-Do' }}
461
+ </div>
469
462
  </div>
470
- </v-card>
463
+
464
+ <div style="flex: 0 0 auto; text-align: right;">
465
+ <v-btn variant="outlined" class="text-none font-weight-medium" size="small" color="#3b82f6" style="border-color: #e2e8f0; border-radius: 8px; padding: 0 16px; height: 32px;" @click="goToListItem(item, 'feedbacks')">View</v-btn>
466
+ </div>
467
+ </div>
471
468
  </div>
472
469
  <DashboardEmptyState v-else />
473
470
  </DashboardPanel>
@@ -598,6 +595,7 @@ type DashboardMetric = {
598
595
  outOfStock?: number;
599
596
  completed?: number;
600
597
  total?: number;
598
+ items?: { name: string; qty: number }[];
601
599
  };
602
600
 
603
601
  type DashboardListItem = {
@@ -693,13 +691,6 @@ const defaultCardValues: TDashboardCardValue[] = [
693
691
  icon: "mdi-briefcase",
694
692
  color: "#0b2f47",
695
693
  },
696
- {
697
- key: "supplyAlert",
698
- periodKey: "period",
699
- title: "Supply Alert",
700
- icon: "mdi-alert",
701
- color: "#d32f2f",
702
- },
703
694
  {
704
695
  key: "taskCompleted",
705
696
  periodKey: "period",
@@ -707,6 +698,13 @@ const defaultCardValues: TDashboardCardValue[] = [
707
698
  icon: "mdi-clipboard-check-outline",
708
699
  color: "#f4a340",
709
700
  },
701
+ {
702
+ key: "supplyAlert",
703
+ periodKey: "period",
704
+ title: "Supply Alert",
705
+ icon: "mdi-alert",
706
+ color: "#d32f2f",
707
+ },
710
708
  ];
711
709
 
712
710
  const landscapeCardValues: TDashboardCardValue[] = [
@@ -717,13 +715,6 @@ const landscapeCardValues: TDashboardCardValue[] = [
717
715
  icon: "mdi-briefcase",
718
716
  color: "#0b2f47",
719
717
  },
720
- {
721
- key: "supplyAlert",
722
- periodKey: "period",
723
- title: "Supply Alert",
724
- icon: "mdi-alert",
725
- color: "#d32f2f",
726
- },
727
718
  {
728
719
  key: "taskCompleted",
729
720
  periodKey: "period",
@@ -732,11 +723,11 @@ const landscapeCardValues: TDashboardCardValue[] = [
732
723
  color: "#f4a340",
733
724
  },
734
725
  {
735
- key: "landscapeSchedule",
726
+ key: "supplyAlert",
736
727
  periodKey: "period",
737
- title: "Landscape Schedule",
738
- icon: "mdi-sprinkler-variant",
739
- color: "#4caf50",
728
+ title: "Supply Alert",
729
+ icon: "mdi-alert",
730
+ color: "#d32f2f",
740
731
  },
741
732
  ];
742
733
 
@@ -799,10 +790,14 @@ const { data: weeklyActivityReq, pending: loadingWeeklyActivity } =
799
790
  `get-dashboard-weekly-activity-${props.site}-${props.serviceType}`,
800
791
  () =>
801
792
  props.serviceType
802
- ? getDashboardHygieneWeeklyActivity(props.site, props.serviceType)
793
+ ? getDashboardHygieneWeeklyActivity(
794
+ props.site,
795
+ props.serviceType,
796
+ currentPeriod.value
797
+ )
803
798
  : Promise.resolve(null),
804
799
  {
805
- watch: [() => props.site, () => props.serviceType],
800
+ watch: [() => props.site, () => props.serviceType, currentPeriod],
806
801
  }
807
802
  );
808
803
 
@@ -862,7 +857,13 @@ const finalDashboardData = computed(() => {
862
857
  let weeklyActivity = weeklyActivityReq.value ?? [];
863
858
  const hasNoActivity =
864
859
  !weeklyActivity.length ||
865
- weeklyActivity.every((item: any) => !item.workOrder && !item.taskCompleted);
860
+ weeklyActivity.every(
861
+ (item: any) =>
862
+ !item.workOrder &&
863
+ !item.taskCompleted &&
864
+ !item.openWorkOrder &&
865
+ !item.closedWorkOrder
866
+ );
866
867
 
867
868
  if (hasNoActivity && currentPeriod.value === "thisWeek") {
868
869
  weeklyActivity = [
@@ -895,9 +896,23 @@ const finalDashboardData = computed(() => {
895
896
  percentage: 0,
896
897
  },
897
898
  activityChart: {
898
- labels: weeklyActivity.map((item: any) => item.day),
899
- workOrders: weeklyActivity.map((item: any) => item.workOrder),
900
- taskCompleted: weeklyActivity.map((item: any) => item.taskCompleted),
899
+ labels: weeklyActivity.map((item: any) => item.day || item.label),
900
+ workOrders: weeklyActivity.map(
901
+ (item: any) => item.workOrder ?? item.openWorkOrder
902
+ ),
903
+ taskCompleted: weeklyActivity.map(
904
+ (item: any) => item.taskCompleted ?? item.closedWorkOrder
905
+ ),
906
+ openWorkOrder: weeklyActivity.some(
907
+ (item: any) => item.openWorkOrder !== undefined
908
+ )
909
+ ? weeklyActivity.map((item: any) => item.openWorkOrder)
910
+ : undefined,
911
+ closedWorkOrder: weeklyActivity.some(
912
+ (item: any) => item.closedWorkOrder !== undefined
913
+ )
914
+ ? weeklyActivity.map((item: any) => item.closedWorkOrder)
915
+ : undefined,
901
916
  },
902
917
  taskScheduleItems: taskScheduleReq.value?.items ?? [],
903
918
  attendanceItems: attendanceReq.value?.items ?? [],
@@ -923,6 +938,28 @@ const snackbar = reactive({
923
938
  text: "",
924
939
  });
925
940
 
941
+ const chartContainerRef = ref<HTMLElement | null>(null);
942
+ const chartWidth = ref(920);
943
+ let resizeObserver: ResizeObserver | null = null;
944
+
945
+ watch(chartContainerRef, (el) => {
946
+ if (resizeObserver) {
947
+ resizeObserver.disconnect();
948
+ }
949
+ if (el && window.ResizeObserver) {
950
+ resizeObserver = new ResizeObserver((entries) => {
951
+ if (entries[0] && entries[0].contentRect.width > 0) {
952
+ chartWidth.value = entries[0].contentRect.width;
953
+ }
954
+ });
955
+ resizeObserver.observe(el);
956
+ }
957
+ });
958
+
959
+ onUnmounted(() => {
960
+ if (resizeObserver) resizeObserver.disconnect();
961
+ });
962
+
926
963
  const rangeOptions = [
927
964
  { label: "Today", value: "Today" },
928
965
  { label: "7d", value: "This Week" },
@@ -1078,6 +1115,7 @@ const kpiCards = computed(() =>
1078
1115
  value: typeof value === "string" ? value : formatNumber(value),
1079
1116
  percentage: toNumber(metric.percentage),
1080
1117
  meta: getMetricMeta(metric),
1118
+ raw: metric,
1081
1119
  filter:
1082
1120
  mapping.key === "visitors" && isSecurityDashboard.value
1083
1121
  ? {
@@ -1112,8 +1150,14 @@ const chartLabels = computed<string[]>(() => {
1112
1150
 
1113
1151
  if (selectedRange.value === "Today")
1114
1152
  return ["12 AM", "4 AM", "8 AM", "12 PM", "4 PM", "8 PM"];
1115
- if (selectedRange.value === "This Month")
1116
- return ["Week 1", "Week 2", "Week 3", "Week 4"];
1153
+ if (selectedRange.value === "This Month") {
1154
+ const daysInMonth = new Date(
1155
+ new Date().getFullYear(),
1156
+ new Date().getMonth() + 1,
1157
+ 0
1158
+ ).getDate();
1159
+ return Array.from({ length: daysInMonth }, (_, i) => String(i + 1));
1160
+ }
1117
1161
  return ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
1118
1162
  });
1119
1163
  const primarySeries = computed(() =>
@@ -1123,10 +1167,10 @@ const secondarySeries = computed(() =>
1123
1167
  normalizeSeries(activityChart.value.secondary, chartLabels.value.length)
1124
1168
  );
1125
1169
  const primaryLegendLabel = computed(
1126
- () => activityChart.value.primaryLabel || "Work Order"
1170
+ () => "Closed Work Order"
1127
1171
  );
1128
1172
  const secondaryLegendLabel = computed(
1129
- () => activityChart.value.secondaryLabel || "Feedback"
1173
+ () => "Open Work Order"
1130
1174
  );
1131
1175
  const chartMax = computed(() =>
1132
1176
  Math.max(1, ...primarySeries.value, ...secondarySeries.value)
@@ -1140,11 +1184,11 @@ const chartYLabels = computed(() => {
1140
1184
  const max = chartMax.value;
1141
1185
  return [
1142
1186
  { text: "0", y: 220 },
1143
- { text: `$${Math.round(max * 0.2)}`, y: 181.6 },
1144
- { text: `$${Math.round(max * 0.4)}`, y: 143.2 },
1145
- { text: `$${Math.round(max * 0.6)}`, y: 104.8 },
1146
- { text: `$${Math.round(max * 0.8)}`, y: 66.4 },
1147
- { text: `$${Math.round(max)}`, y: 28 },
1187
+ { text: `${Math.round(max * 0.2)}`, y: 181.6 },
1188
+ { text: `${Math.round(max * 0.4)}`, y: 143.2 },
1189
+ { text: `${Math.round(max * 0.6)}`, y: 104.8 },
1190
+ { text: `${Math.round(max * 0.8)}`, y: 66.4 },
1191
+ { text: `${Math.round(max)}`, y: 28 },
1148
1192
  ];
1149
1193
  });
1150
1194
 
@@ -1170,10 +1214,11 @@ const secondaryPath = computed(() => buildSmoothPath(secondaryDots.value));
1170
1214
  const chartAxisLabels = computed(() => {
1171
1215
  const labels = chartLabels.value;
1172
1216
  const count = Math.max(1, labels.length - 1);
1217
+ const width = chartWidth.value;
1173
1218
 
1174
1219
  return labels.map((label, index) => ({
1175
1220
  text: label,
1176
- x: 54 + (846 / count) * index,
1221
+ x: 54 + ((width - 74) / count) * index,
1177
1222
  }));
1178
1223
  });
1179
1224
 
@@ -1339,18 +1384,33 @@ function normalizeActivityChart(source: any) {
1339
1384
  const labels = Array.isArray(source?.labels)
1340
1385
  ? source.labels.map((label: any) => String(label))
1341
1386
  : [];
1387
+
1388
+ const secondary = extractChartSeries(source, [
1389
+ "closedWorkOrder",
1390
+ "closedWorkOrders",
1391
+ "closed_work_orders",
1392
+ ]);
1393
+
1394
+ const hasClosedSeries = secondary.length > 0;
1395
+
1342
1396
  const primary = extractChartSeries(source, [
1397
+ "openWorkOrder",
1398
+ "openWorkOrders",
1399
+ "open_work_orders",
1343
1400
  "workOrders",
1344
1401
  "workOrder",
1345
1402
  "work_orders",
1346
1403
  ]);
1347
- const secondaryCandidate = getSecondaryChartSeries(source);
1404
+
1405
+ const secondaryCandidate = hasClosedSeries
1406
+ ? { label: "Closed Work Order", series: secondary }
1407
+ : getSecondaryChartSeries(source);
1348
1408
 
1349
1409
  return {
1350
1410
  labels,
1351
1411
  primary,
1352
1412
  secondary: secondaryCandidate.series,
1353
- primaryLabel: "Work Order",
1413
+ primaryLabel: hasClosedSeries ? "Open Work Order" : "Work Order",
1354
1414
  secondaryLabel: secondaryCandidate.label,
1355
1415
  };
1356
1416
  }
@@ -1401,9 +1461,10 @@ function normalizeSeries(value: any, targetLength: number) {
1401
1461
 
1402
1462
  function buildChartDots(series: number[]) {
1403
1463
  const count = Math.max(1, series.length - 1);
1464
+ const width = chartWidth.value;
1404
1465
 
1405
1466
  return series.map((value, index) => ({
1406
- x: 54 + (846 / count) * index,
1467
+ x: 54 + ((width - 74) / count) * index,
1407
1468
  y: 220 - (toNumber(value) / chartMax.value) * 184,
1408
1469
  }));
1409
1470
  }
@@ -1788,18 +1849,7 @@ function goToListItem(item: TaskItem | FeedbackItem, routeKey: string) {
1788
1849
  router.push(`/${props.orgId}/${props.site}/${path}/${id}`);
1789
1850
  }
1790
1851
 
1791
- function getInitials(name: string) {
1792
- const parts = String(name || "")
1793
- .trim()
1794
- .split(/\s+/)
1795
- .filter(Boolean);
1796
- if (!parts.length) return "?";
1797
- return parts
1798
- .slice(0, 2)
1799
- .map((part) => part[0])
1800
- .join("")
1801
- .toUpperCase();
1802
- }
1852
+
1803
1853
 
1804
1854
  function formatNumber(value: number) {
1805
1855
  return new Intl.NumberFormat("en-US").format(toNumber(value));
@@ -22,7 +22,7 @@
22
22
  <v-autocomplete v-model="selectedPass" v-model:search="passInput" :hide-no-data="false"
23
23
  class="mt-3" :items="passItemsFilteredFinal" item-title="prefixAndName" item-value="_id"
24
24
  label="Pass (optional)" variant="outlined" density="compact" :error-messages="errorScanPassMessage"
25
- persistent-hint small-chips>
25
+ persistent-hint small-chips @click="fetchPasses">
26
26
  <template v-slot:no-data>
27
27
  <v-list-item density="compact">
28
28
  <v-list-item-title v-if="passInput">
@@ -160,20 +160,28 @@ const passTypesComputed = computed(() => {
160
160
  }
161
161
  })
162
162
 
163
- const { data: passesData, refresh: refreshPassesData, pending: fetchPassesPending } = await useLazyAsyncData('get-pass-keys', () => {
164
- return getPassKeysByPageSearch({
165
- search: passInput.value,
166
- page: 1,
167
- limit: 500,
168
- passTypes: passTypesComputed.value,
169
- sites: [props.site],
170
- statuses: ["Available"]
171
- })
172
- })
173
-
174
- watch(passesData, (data: any) => {
175
- passItems.value = Array.isArray(data?.items) ? data.items : []
176
- })
163
+ const isPassListLoading = ref(false);
164
+
165
+ async function fetchPasses() {
166
+ // load pass list
167
+ passItems.value = [];
168
+ isPassListLoading.value = true;
169
+ try {
170
+ const result = await getPassKeysByPageSearch({
171
+ page: 1,
172
+ sites: [props.site],
173
+ limit: 10000,
174
+ passTypes: passTypesComputed.value,
175
+ statuses: ["Available"],
176
+ sortBy: { column: "prefixAndName", order: "1" },
177
+ });
178
+ passItems.value = result?.items;
179
+ } catch (error) {
180
+ showMessage(errorConverter(error as string), "error");
181
+ } finally {
182
+ isPassListLoading.value = false;
183
+ }
184
+ }
177
185
 
178
186
  const passItemsFilteredFinal = computed(() => {
179
187
  return passItems.value.filter((item: TPassKey) => {
@@ -60,7 +60,7 @@
60
60
  </v-col>
61
61
 
62
62
  <!-- Attachment -->
63
- <v-col cols="12" class="px-6 pt-3 pb-4">
63
+ <!-- <v-col cols="12" class="px-6 pt-3 pb-4">
64
64
  <InputLabel title="Attachment (Optional)" />
65
65
 
66
66
  <div v-if="attachmentId" class="d-flex align-center" style="gap: 8px">
@@ -130,7 +130,7 @@
130
130
  <span v-if="attachmentError" class="text-caption text-error d-block mt-1">
131
131
  {{ attachmentError }}
132
132
  </span>
133
- </v-col>
133
+ </v-col> -->
134
134
 
135
135
  <v-col v-if="message" cols="12" class="px-6">
136
136
  <span class="text-caption text-error">{{ message }}</span>