@7365admin1/layer-common 3.2.2-staging.84 → 3.2.2-staging.86

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.
@@ -338,10 +338,10 @@
338
338
  <v-card-title class="dialog-title">Make a Call</v-card-title>
339
339
  <v-card-text class="dialog-body make-call-body">
340
340
  <div class="make-call-field">
341
- <div class="call-label">IP address to dial</div>
341
+ <div class="call-label">Extension, SIP address, or peer IP</div>
342
342
  <v-text-field
343
343
  v-model="makeCallForm.target"
344
- placeholder="000.000.0.0"
344
+ placeholder="7100 or user@example.com"
345
345
  variant="outlined"
346
346
  density="compact"
347
347
  hide-details="auto"
@@ -385,9 +385,19 @@
385
385
  <v-card class="web-phone-dialog" rounded="lg">
386
386
  <v-card-title class="dialog-title web-phone-title">
387
387
  <span>Web Call</span>
388
- <v-chip size="small" variant="flat" :class="webPhoneStatusClass">
389
- {{ webPhoneStatusLabel }}
390
- </v-chip>
388
+ <div class="web-phone-title-actions">
389
+ <v-chip size="small" variant="flat" :class="webPhoneStatusClass">
390
+ {{ webPhoneStatusLabel }}
391
+ </v-chip>
392
+ <v-btn
393
+ icon="mdi-close"
394
+ size="small"
395
+ variant="text"
396
+ title="Close Web Call"
397
+ aria-label="Close Web Call"
398
+ @click="webPhoneDialog = false"
399
+ />
400
+ </div>
391
401
  </v-card-title>
392
402
 
393
403
  <v-card-text class="web-phone-body">
@@ -547,11 +557,6 @@
547
557
  </div>
548
558
  </v-card-text>
549
559
 
550
- <v-card-actions class="dialog-actions pa-0">
551
- <v-btn class="text-none action-submit full-action" variant="flat" height="44" @click="webPhoneDialog = false">
552
- Close
553
- </v-btn>
554
- </v-card-actions>
555
560
  </v-card>
556
561
  </v-dialog>
557
562
 
@@ -1184,12 +1189,18 @@ function openDialCodeDialog(row: IntercomRow) {
1184
1189
  dialCodeDialog.value = true;
1185
1190
  }
1186
1191
 
1187
- function openMakeCallDialog(row: IntercomRow) {
1192
+ async function openMakeCallDialog(row: IntercomRow) {
1188
1193
  selectedRow.value = row;
1189
- makeCallForm.target = isIpAddress(row.serverAddress) ? row.serverAddress : "";
1194
+ makeCallForm.target = sipAccount.value?.extension || "";
1190
1195
  makeCallForm.status = "";
1191
1196
  makeCallForm.statusType = "info";
1192
1197
  makeCallDialog.value = true;
1198
+ if (!sipAccount.value && !sipAccountLoading.value) {
1199
+ await loadSipAccount();
1200
+ if (makeCallDialog.value && !makeCallForm.target) {
1201
+ makeCallForm.target = sipAccount.value?.extension || "";
1202
+ }
1203
+ }
1193
1204
  }
1194
1205
 
1195
1206
  function openWebPhoneDialog(row?: IntercomRow) {
@@ -1397,13 +1408,13 @@ async function runMakeCall() {
1397
1408
  if (!selectedRow.value) return;
1398
1409
  const target = makeCallForm.target.trim();
1399
1410
  if (!target) {
1400
- makeCallForm.status = "Please enter an IP address to dial.";
1411
+ makeCallForm.status = "Please enter an extension, SIP address, or peer IP.";
1401
1412
  makeCallForm.statusType = "warning";
1402
1413
  return;
1403
1414
  }
1404
1415
 
1405
- if (!isIpAddress(target)) {
1406
- makeCallForm.status = "Address to dial must be an IP address.";
1416
+ if (!isSipCallTarget(target)) {
1417
+ makeCallForm.status = "Enter a valid extension, SIP address, or IPv4 address.";
1407
1418
  makeCallForm.statusType = "warning";
1408
1419
  return;
1409
1420
  }
@@ -1425,13 +1436,8 @@ async function runMakeCall() {
1425
1436
  }
1426
1437
  }
1427
1438
 
1428
- function isIpAddress(value = "") {
1429
- const parts = value.trim().split(".");
1430
- return parts.length === 4 && parts.every((part) => {
1431
- if (!/^\d{1,3}$/.test(part)) return false;
1432
- const value = Number(part);
1433
- return value >= 0 && value <= 255;
1434
- });
1439
+ function isSipCallTarget(value = "") {
1440
+ return /^(?:sip:)?[A-Za-z0-9_.+-]+(?:@[A-Za-z0-9.-]+(?::\d{1,5})?)?$/.test(value.trim());
1435
1441
  }
1436
1442
 
1437
1443
  async function runHangupCall() {
@@ -2273,19 +2279,29 @@ export default {
2273
2279
  }
2274
2280
 
2275
2281
  .web-phone-dialog {
2282
+ display: flex;
2276
2283
  width: 820px;
2284
+ max-height: min(90vh, 760px);
2277
2285
  max-width: 94vw;
2278
2286
  overflow: hidden;
2279
2287
  background: #fff;
2288
+ flex-direction: column;
2280
2289
  }
2281
2290
 
2282
2291
  .web-phone-title {
2292
+ flex: 0 0 auto;
2283
2293
  display: flex;
2284
2294
  align-items: center;
2285
2295
  justify-content: space-between;
2286
2296
  gap: 16px;
2287
2297
  }
2288
2298
 
2299
+ .web-phone-title-actions {
2300
+ display: flex;
2301
+ align-items: center;
2302
+ gap: 8px;
2303
+ }
2304
+
2289
2305
  .web-phone-status {
2290
2306
  min-width: 92px;
2291
2307
  justify-content: center;
@@ -2317,6 +2333,7 @@ export default {
2317
2333
  grid-template-columns: minmax(280px, 0.9fr) minmax(320px, 1.1fr);
2318
2334
  gap: 24px;
2319
2335
  min-height: 440px;
2336
+ overflow: auto;
2320
2337
  padding: 22px 24px 24px;
2321
2338
  }
2322
2339
 
@@ -2668,8 +2685,7 @@ export default {
2668
2685
 
2669
2686
  .web-phone-body {
2670
2687
  grid-template-columns: minmax(0, 1fr);
2671
- max-height: 78vh;
2672
- overflow: auto;
2688
+ min-height: 0;
2673
2689
  }
2674
2690
 
2675
2691
  .web-phone-settings {
@@ -583,7 +583,7 @@ async function loadConfig() {
583
583
  enabled: Boolean(config.enabled),
584
584
  onlineMode: Boolean(config.onlineMode),
585
585
  readerId: String(config.readerId || readers.value[0]?._id || ""),
586
- qrFormat: ["0", "1", "2"].includes(String(config.qrFormat)) ? String(config.qrFormat) as "0" | "1" | "2" : "0",
586
+ qrFormat: "0",
587
587
  identificationMethods: {
588
588
  facial: configuredMethods?.facial ?? true,
589
589
  card: configuredMethods?.card ?? true,
@@ -750,13 +750,20 @@ async function loadReaderConfiguration() {
750
750
  try {
751
751
  const response = await getReaderConfiguration(form.readerId, {
752
752
  general: ["online"],
753
+ identifier: ["pin_identification_enabled", "multi_factor_authentication"],
753
754
  });
754
755
  const configuration = getConfigurationData(response);
755
756
  const general = isUnknownRecord(configuration.general) ? configuration.general : {};
757
+ const identifier = isUnknownRecord(configuration.identifier) ? configuration.identifier : {};
756
758
 
757
759
  if (general.online !== undefined) {
758
760
  form.onlineMode = isEnabledConfiguration(general.online);
759
761
  }
762
+ if (identifier.pin_identification_enabled !== undefined) {
763
+ const pinEnabled = isEnabledConfiguration(identifier.pin_identification_enabled);
764
+ form.identificationMethods.pin = pinEnabled;
765
+ form.identificationMethods.idPassword = !pinEnabled;
766
+ }
760
767
  } catch (error: unknown) {
761
768
  console.warn("Unable to load HID reader configuration:", errorConverter(error));
762
769
  }
@@ -769,7 +776,8 @@ function buildPayload() {
769
776
  enabled: form.identificationMethods.qrCode,
770
777
  onlineMode: form.onlineMode,
771
778
  readerId: form.readerId,
772
- qrFormat: form.qrFormat,
779
+ // Visitor QR passes always use Amico alphanumeric qrcodes.
780
+ qrFormat: "0",
773
781
  identificationMethods: { ...form.identificationMethods },
774
782
  printer: {
775
783
  vendorId: form.printer.vendorId.trim(),
@@ -827,6 +835,20 @@ async function saveIdentificationMethod(method: HidIdentificationMethod) {
827
835
 
828
836
  savingSection.value = `method-${method}`;
829
837
  try {
838
+ if (method === "pin" || method === "idPassword") {
839
+ if (method === "pin" && form.identificationMethods.pin) {
840
+ form.identificationMethods.idPassword = false;
841
+ } else if (method === "idPassword" && form.identificationMethods.idPassword) {
842
+ form.identificationMethods.pin = false;
843
+ }
844
+ await setReaderConfiguration(form.readerId, {
845
+ identifier: {
846
+ pin_identification_enabled: form.identificationMethods.pin ? "1" : "0",
847
+ // Mode 1: PIN is an alternative credential, never a second factor.
848
+ multi_factor_authentication: "0",
849
+ },
850
+ });
851
+ }
830
852
  await updateSitebyId(props.site, buildPayload());
831
853
  showMessage(`${identificationMethodOptions.find((item) => item.key === method)?.label || "Identification method"} updated.`, "success");
832
854
  } catch (error: unknown) {
@@ -238,28 +238,28 @@
238
238
  class="mb-3"
239
239
  />
240
240
 
241
- <div class="field-label">Panic Pin (optional)</div>
241
+ <div class="field-label">Access PIN (optional)</div>
242
242
  <v-text-field
243
- v-model="form.cardNo"
244
- aria-label="Panic Pin"
245
- placeholder="Enter panic pin"
243
+ v-model="form.accessPin"
244
+ aria-label="Access PIN"
245
+ placeholder="Enter numeric access PIN"
246
246
  density="compact"
247
247
  variant="outlined"
248
- hide-details="auto"
248
+ inputmode="numeric"
249
+ autocomplete="new-password"
250
+ :type="showPin ? 'text' : 'password'"
251
+ :append-inner-icon="showPin ? 'mdi-eye-off' : 'mdi-eye'"
252
+ :hint="selectedUser && pinEnrolled ? 'Leave blank to keep the current PIN.' : 'PIN works as an alternative to face or QR.'"
253
+ persistent-hint
254
+ @click:append-inner="showPin = !showPin"
249
255
  class="mb-3"
250
256
  />
251
-
252
- <div class="field-label">Panic Password (optional)</div>
253
- <v-text-field
254
- v-model="form.pinPassword"
255
- aria-label="Panic Password"
256
- placeholder="Enter password"
257
+ <v-checkbox
258
+ v-if="selectedUser && pinEnrolled"
259
+ v-model="removePinRequested"
260
+ label="Remove current access PIN"
257
261
  density="compact"
258
- variant="outlined"
259
- hide-details="auto"
260
- :type="showPassword ? 'text' : 'password'"
261
- :append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
262
- @click:append-inner="showPassword = !showPassword"
262
+ hide-details
263
263
  />
264
264
  </v-card-text>
265
265
 
@@ -306,12 +306,8 @@
306
306
  ><strong>{{
307
307
  selectedUser ? getMappingStatus(selectedUser) : "N/A"
308
308
  }}</strong>
309
- <span>Panic Pin</span
310
- ><strong>{{ selectedUser?.cardNo || "N/A" }}</strong>
311
- <span>Private Password</span
312
- ><strong>{{
313
- selectedUser?.metadata?.pinPassword ? "********" : "N/A"
314
- }}</strong>
309
+ <span>Access PIN</span
310
+ ><strong>{{ selectedUser?.metadata?.pinEnrolled ? "Configured" : "N/A" }}</strong>
315
311
  </div>
316
312
  </v-card-text>
317
313
  <v-card-actions>
@@ -377,6 +373,9 @@ const {
377
373
  getUserImage,
378
374
  setUserImage,
379
375
  deleteUserImage,
376
+ getUserPinStatus,
377
+ setUserPin,
378
+ deleteUserPin,
380
379
  runObjectOperation,
381
380
  createIdentity,
382
381
  updateIdentity,
@@ -401,11 +400,13 @@ const formDialog = ref(false);
401
400
  const viewDialog = ref(false);
402
401
  const deleteDialog = ref(false);
403
402
  const selectedUser = ref<Record<string, any> | null>(null);
404
- const showPassword = ref(false);
403
+ const showPin = ref(false);
404
+ const pinEnrolled = ref(false);
405
405
  const photoInput = ref<HTMLInputElement | null>(null);
406
406
  const cameraDialog = ref(false);
407
407
  const selectedPhotoFile = ref<File | null>(null);
408
408
  const removePhotoRequested = ref(false);
409
+ const removePinRequested = ref(false);
409
410
  const snackbar = reactive({
410
411
  show: false,
411
412
  color: "success",
@@ -420,8 +421,7 @@ const form = reactive({
420
421
  name: "",
421
422
  hidUserId: "",
422
423
  registration: "",
423
- cardNo: "",
424
- pinPassword: "",
424
+ accessPin: "",
425
425
  photoPreview: "",
426
426
  });
427
427
 
@@ -554,8 +554,10 @@ function resetForm() {
554
554
  form.name = "";
555
555
  form.hidUserId = "";
556
556
  form.registration = "";
557
- form.cardNo = "";
558
- form.pinPassword = "";
557
+ form.accessPin = "";
558
+ pinEnrolled.value = false;
559
+ showPin.value = false;
560
+ removePinRequested.value = false;
559
561
  form.photoPreview = "";
560
562
  selectedPhotoFile.value = null;
561
563
  removePhotoRequested.value = false;
@@ -582,13 +584,24 @@ async function openEdit(user: Record<string, any>) {
582
584
  form.name = getName(user);
583
585
  form.hidUserId = formatHidUid(user.hidUserId);
584
586
  form.registration = user.registration ?? "";
585
- form.cardNo = user.cardNo ?? "";
586
- form.pinPassword = user.metadata?.pinPassword ?? "";
587
+ form.accessPin = "";
588
+ pinEnrolled.value = Boolean(user.metadata?.pinEnrolled);
589
+ removePinRequested.value = false;
587
590
  form.photoPreview = getUserImageSrc(user);
588
591
  selectedPhotoFile.value = null;
589
592
  removePhotoRequested.value = false;
590
593
  formDialog.value = true;
591
594
 
595
+ try {
596
+ const status = await getUserPinStatus(
597
+ form.reader,
598
+ toHidNumericId(user.hidUserId),
599
+ );
600
+ pinEnrolled.value = Boolean(status?.data?.pinEnrolled);
601
+ } catch {
602
+ // Preserve the last known status if the reader is temporarily offline.
603
+ }
604
+
592
605
  if (!form.photoPreview) {
593
606
  await loadUserImages([user]);
594
607
  if (selectedUser.value?._rowKey === user._rowKey) {
@@ -672,6 +685,10 @@ async function saveUser() {
672
685
  showToast("Please complete the required HID user fields.", "error");
673
686
  return;
674
687
  }
688
+ if (form.accessPin && !/^\d{1,32}$/.test(form.accessPin)) {
689
+ showToast("Access PIN must contain numbers only.", "error");
690
+ return;
691
+ }
675
692
 
676
693
  saving.value = true;
677
694
  try {
@@ -688,7 +705,6 @@ async function saveUser() {
688
705
  const payload = {
689
706
  hidUserId: String(hidUser.id),
690
707
  registration: form.registration,
691
- cardNo: form.cardNo,
692
708
  type: props.identityType as
693
709
  | "resident"
694
710
  | "staff"
@@ -707,7 +723,7 @@ async function saveUser() {
707
723
  photo: form.photoPreview,
708
724
  imageTimestamp: selectedUser.value?.metadata?.imageTimestamp || "",
709
725
  facialScores: selectedUser.value?.metadata?.facialScores || {},
710
- pinPassword: form.pinPassword,
726
+ pinEnrolled: pinEnrolled.value,
711
727
  },
712
728
  };
713
729
 
@@ -716,6 +732,7 @@ async function saveUser() {
716
732
  await syncHidUserRole(readerId, hidUser.id, isAdministratorIdentity());
717
733
  const facialResult = await syncFacialImage(readerId, hidUser.id);
718
734
  applyFacialResult(payload.metadata, facialResult);
735
+ payload.metadata.pinEnrolled = await syncAccessPin(readerId, hidUser.id);
719
736
  if (selectedUser.value._id) {
720
737
  await updateIdentity(selectedUser.value._id, payload);
721
738
  } else {
@@ -726,6 +743,7 @@ async function saveUser() {
726
743
  await syncHidUserRole(readerId, hidUser.id, isAdministratorIdentity());
727
744
  const facialResult = await syncFacialImage(readerId, hidUser.id);
728
745
  applyFacialResult(payload.metadata, facialResult);
746
+ payload.metadata.pinEnrolled = await syncAccessPin(readerId, hidUser.id);
729
747
  await saveIdentityOnReader(readerId, payload);
730
748
  selectedReaderId.value = readerId;
731
749
  }
@@ -745,6 +763,21 @@ async function saveUser() {
745
763
  }
746
764
  }
747
765
 
766
+ async function syncAccessPin(readerId: string, hidUserId: number) {
767
+ if (removePinRequested.value) {
768
+ await deleteUserPin(readerId, hidUserId);
769
+ pinEnrolled.value = false;
770
+ return false;
771
+ }
772
+ if (form.accessPin) {
773
+ await setUserPin(readerId, hidUserId, form.accessPin);
774
+ form.accessPin = "";
775
+ pinEnrolled.value = true;
776
+ return true;
777
+ }
778
+ return pinEnrolled.value;
779
+ }
780
+
748
781
  type FacialSyncResult = {
749
782
  action: "none" | "enrolled" | "removed";
750
783
  timestamp?: number;
@@ -806,6 +839,10 @@ async function deleteUser() {
806
839
  toHidNumericId(selectedUser.value.hidUserId),
807
840
  false
808
841
  );
842
+ await deleteUserPin(
843
+ selectedReaderId.value,
844
+ toHidNumericId(selectedUser.value.hidUserId),
845
+ );
809
846
  await deleteHidUserFromReader(selectedReaderId.value, selectedUser.value);
810
847
  if (selectedUser.value._id) {
811
848
  await deleteIdentity(selectedUser.value._id);
@@ -3,10 +3,8 @@
3
3
  <v-col cols="12" class="mb-2">
4
4
  <v-row no-gutters>
5
5
  <v-btn
6
- class="text-none mr-2"
7
- rounded="pill"
8
- variant="tonal"
9
- size="large"
6
+ class="text-none mr-2 screen-btn-primary"
7
+ variant="flat"
10
8
  @click="inviteMember()"
11
9
  v-if="props.inviteMember"
12
10
  >
@@ -15,8 +13,12 @@
15
13
  </v-row>
16
14
  </v-col>
17
15
  <v-col cols="12">
18
- <v-card width="100%" variant="outlined" border="thin" rounded="lg">
19
- <v-toolbar density="compact" color="grey-lighten-4">
16
+ <v-card width="100%" variant="flat" class="table-card">
17
+ <v-toolbar
18
+ density="compact"
19
+ color="transparent"
20
+ class="table-card__toolbar"
21
+ >
20
22
  <template #prepend>
21
23
  <v-btn
22
24
  fab
@@ -30,7 +32,7 @@
30
32
 
31
33
  <template #append>
32
34
  <v-row no-gutters justify="end" align="center">
33
- <span class="mr-2 text-caption text-fontgray">
35
+ <span class="mr-2 table-card__range">
34
36
  {{ pageRange }}
35
37
  </span>
36
38
  <local-pagination
@@ -77,7 +79,7 @@
77
79
  <span class="text-caption font-weight-bold text-capitalize">
78
80
  permissions
79
81
  </span>
80
- <v-chip>{{ value.length }}</v-chip>
82
+ <StatusChip :dot="false" tone="info" :label="String(value.length)" />
81
83
  </template>
82
84
  <template #item.createdAt="{ item }">
83
85
  {{ item.createdAt ? formatDate(item.createdAt) : "" }}
@@ -4,9 +4,8 @@
4
4
  <v-row no-gutters>
5
5
  <v-btn
6
6
  v-if="props.seatManagement !== 'index'"
7
- class="text-none"
8
- rounded="pill"
9
- variant="tonal"
7
+ class="text-none screen-btn-primary"
8
+ variant="flat"
10
9
  :to="{
11
10
  name: props.seatManagement,
12
11
  params: { organization: props.orgId },
@@ -19,8 +18,12 @@
19
18
  </v-col>
20
19
 
21
20
  <v-col cols="12">
22
- <v-card width="100%" variant="outlined" border="thin" rounded="lg">
23
- <v-toolbar density="compact" color="grey-lighten-4">
21
+ <v-card width="100%" variant="flat" class="table-card">
22
+ <v-toolbar
23
+ density="compact"
24
+ color="transparent"
25
+ class="table-card__toolbar"
26
+ >
24
27
  <template #prepend>
25
28
  <v-btn fab icon density="comfortable" @click="getAll()">
26
29
  <v-icon>mdi-refresh</v-icon>
@@ -29,7 +32,7 @@
29
32
 
30
33
  <template #append>
31
34
  <v-row no-gutters justify="end" align="center">
32
- <span class="mr-2 text-caption text-font gray">
35
+ <span class="mr-2 table-card__range">
33
36
  {{ pageRange }}
34
37
  </span>
35
38
  <local-pagination
@@ -17,10 +17,8 @@
17
17
  <v-row no-gutters align="center" class="w-100">
18
18
  <v-col cols="auto" v-if="canCheckInOut">
19
19
  <v-btn
20
- class="text-none"
21
- rounded="pill"
22
- variant="tonal"
23
- size="large"
20
+ class="text-none screen-btn-primary"
21
+ variant="flat"
24
22
  @click="onCreateItem"
25
23
  >
26
24
  {{ hasCheckedInToday ? "Check Out" : "Check In" }}
@@ -28,7 +26,7 @@
28
26
  </v-col>
29
27
  <v-spacer />
30
28
 
31
- <v-col cols="auto">
29
+ <v-col cols="auto" class="filter-field">
32
30
  <v-text-field
33
31
  v-model="searchInput"
34
32
  density="compact"
@@ -3,11 +3,9 @@
3
3
  <v-col cols="12" class="mb-2">
4
4
  <v-row no-gutters>
5
5
  <v-btn
6
- class="text-none"
7
- rounded="pill"
8
- variant="tonal"
6
+ class="text-none screen-btn-primary"
7
+ variant="flat"
9
8
  @click="createDialog = true"
10
- size="large"
11
9
  v-if="canCreateRole"
12
10
  >
13
11
  Create role
@@ -17,12 +15,15 @@
17
15
  <v-col cols="12">
18
16
  <v-card
19
17
  width="100%"
20
- variant="outlined"
21
- border="thin"
22
- rounded="lg"
18
+ variant="flat"
19
+ class="table-card"
23
20
  :loading="loading"
24
21
  >
25
- <v-toolbar density="compact" color="grey-lighten-4">
22
+ <v-toolbar
23
+ density="compact"
24
+ color="transparent"
25
+ class="table-card__toolbar"
26
+ >
26
27
  <template #prepend>
27
28
  <v-btn fab icon density="comfortable" @click="getRoles()">
28
29
  <v-icon>mdi-refresh</v-icon>
@@ -31,7 +32,7 @@
31
32
 
32
33
  <template #append>
33
34
  <v-row no-gutters justify="end" align="center">
34
- <span class="mr-2 text-caption text-fontgray">
35
+ <span class="mr-2 table-card__range">
35
36
  {{ pageRange }}
36
37
  </span>
37
38
  <local-pagination
@@ -58,7 +59,11 @@
58
59
  <span class="text-caption font-weight-bold text-capitalize">
59
60
  permissions
60
61
  </span>
61
- <v-chip>{{ getPermissionCount(value) }}</v-chip>
62
+ <StatusChip
63
+ :dot="false"
64
+ tone="info"
65
+ :label="String(getPermissionCount(value))"
66
+ />
62
67
  </template>
63
68
 
64
69
  <template #item.action-table="{ item }" v-if="canDeleteRole">
@@ -21,10 +21,8 @@
21
21
  <v-row no-gutters align="center" class="w-100">
22
22
  <v-col cols="auto" v-if="canCreateScheduleTask">
23
23
  <v-btn
24
- class="text-none"
25
- rounded="pill"
26
- variant="tonal"
27
- size="large"
24
+ class="text-none screen-btn-primary"
25
+ variant="flat"
28
26
  @click="openFormDialog"
29
27
  >
30
28
  Create Schedule Task
@@ -33,7 +31,7 @@
33
31
 
34
32
  <v-spacer />
35
33
 
36
- <v-col cols="auto">
34
+ <v-col cols="auto" class="filter-field">
37
35
  <v-text-field
38
36
  v-model="searchInput"
39
37
  density="compact"
@@ -49,28 +47,31 @@
49
47
 
50
48
  <template #item.areas="{ value }">
51
49
  <div v-if="value && value.length > 0">
52
- <v-chip
50
+ <StatusChip
53
51
  v-for="(area, idx) in value.slice(0, 2)"
54
52
  :key="idx"
55
- size="small"
56
53
  class="mr-1 mb-1"
57
- >
58
- {{ area.name }}
59
- </v-chip>
60
- <v-chip v-if="value.length > 2" size="small" class="mb-1">
61
- +{{ value.length - 2 }} more
62
- </v-chip>
54
+ :dot="false"
55
+ tone="neutral"
56
+ :label="area.name"
57
+ />
58
+ <StatusChip
59
+ v-if="value.length > 2"
60
+ class="mb-1"
61
+ :dot="false"
62
+ tone="neutral"
63
+ :label="`+${value.length - 2} more`"
64
+ />
63
65
  </div>
64
66
  <span v-else>N/A</span>
65
67
  </template>
66
68
 
67
69
  <template #item.status="{ value }">
68
- <v-chip
69
- :color="value === 'active' ? 'success' : 'default'"
70
+ <StatusChip
70
71
  class="text-capitalize"
71
- >
72
- {{ value || "N/A" }}
73
- </v-chip>
72
+ :status="value"
73
+ :label="value || 'N/A'"
74
+ />
74
75
  </template>
75
76
  </TableMain>
76
77
 
@@ -270,15 +271,11 @@
270
271
  Status:
271
272
  </v-col>
272
273
  <v-col cols="7" class="text-subtitle-2">
273
- <v-chip
274
- :color="
275
- selectedTask.status === 'active' ? 'success' : 'default'
276
- "
277
- size="small"
274
+ <StatusChip
278
275
  class="text-capitalize"
279
- >
280
- {{ selectedTask.status || "N/A" }}
281
- </v-chip>
276
+ :status="selectedTask.status"
277
+ :label="selectedTask.status || 'N/A'"
278
+ />
282
279
  </v-col>
283
280
  </v-row>
284
281