@7365admin1/layer-common 3.1.2 → 3.1.3-staging.40

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 (31) hide show
  1. package/.github/workflows/publish-staging.yml +11 -0
  2. package/components/AddEqupmentForm.vue +52 -1
  3. package/components/CameraMain.vue +17 -9
  4. package/components/DashboardMain.vue +231 -87
  5. package/components/EquipmentManagementMain.vue +56 -0
  6. package/components/InvitationMain.vue +4 -3
  7. package/components/ManageChecklistMain.vue +44 -38
  8. package/components/Nfc/NFCPatrolReportMain.vue +146 -38
  9. package/components/Nfc/PatrolReport/DailyReportTab.vue +10 -6
  10. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +19 -9
  11. package/components/Nfc/PatrolReport/MonthlyReportTable.vue +69 -0
  12. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +20 -0
  13. package/components/Nfc/PatrolReport/PatrolReportTab.vue +5 -3
  14. package/components/Nfc/PatrolReport/ReportDatePicker.vue +61 -0
  15. package/components/Nfc/PatrolReport/ReportFilters.vue +52 -20
  16. package/components/Nfc/PatrolReport/SummaryReportTable.vue +22 -0
  17. package/components/PhotoUpload.vue +19 -4
  18. package/components/ServiceProviderMain.vue +59 -25
  19. package/components/VisitorForm.vue +78 -18
  20. package/components/VisitorManagement.vue +12 -16
  21. package/composables/useCleaningSchedulePermission.ts +16 -51
  22. package/composables/useEquipment.ts +2 -0
  23. package/composables/useLocalAuth.ts +1 -1
  24. package/composables/useNFCPatrolDailyReport.ts +3 -4
  25. package/composables/useNFCPatrolMonthlyReport.ts +62 -30
  26. package/composables/useNFCPatrolReport.ts +2 -0
  27. package/composables/useServiceProvider.ts +21 -0
  28. package/package.json +1 -1
  29. package/types/customer.d.ts +2 -1
  30. package/types/equipment.d.ts +5 -1
  31. package/types/nfc-patrol-report.ts +19 -1
@@ -34,3 +34,14 @@ jobs:
34
34
 
35
35
  - name: Publish to npm with staging tag
36
36
  run: npm publish --tag staging --registry https://registry.npmjs.org
37
+
38
+ - name: Notify Discord on failure
39
+ if: failure()
40
+ env:
41
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
42
+ run: |
43
+ MESSAGE="@everyone :rotating_light: **Publish Staging** failed in \`${{ github.repository }}\` (\`${{ github.workflow }}\`)
44
+ ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
45
+ curl -s -X POST -H "Content-Type: application/json" \
46
+ -d "$(jq -n --arg content "$MESSAGE" '{content: $content}')" \
47
+ "$DISCORD_WEBHOOK_URL"
@@ -12,6 +12,24 @@
12
12
  <v-card-text class="pa-4">
13
13
  <v-form ref="formRef" v-model="valid">
14
14
  <v-row no-gutters class="pa-0">
15
+ <v-col cols="12" class="pa-0 mb-2">
16
+ <PhotoUpload
17
+ v-model="attachmentPhotos"
18
+ v-model:photo-ids="attachmentIds"
19
+ title="Photo"
20
+ @error="photoUploadError = $event"
21
+ />
22
+
23
+ <v-alert
24
+ v-if="photoUploadError"
25
+ type="error"
26
+ density="compact"
27
+ variant="tonal"
28
+ class="mt-2"
29
+ >
30
+ {{ photoUploadError }}
31
+ </v-alert>
32
+ </v-col>
15
33
  <v-col cols="12" class="pa-0 mb-2">
16
34
  <InputLabel for="name" title="Name" required class="mb-1" />
17
35
  <v-text-field
@@ -87,6 +105,10 @@ const unitOfMeasurementModel = defineModel("unitOfMeasurement", {
87
105
  type: String,
88
106
  default: "",
89
107
  });
108
+ const attachmentModel = defineModel("attachment", {
109
+ type: String,
110
+ default: "",
111
+ });
90
112
  const showDialog = defineModel({ type: Boolean, default: false });
91
113
 
92
114
  const prop = defineProps({
@@ -105,6 +127,9 @@ const emit = defineEmits(["saved", "close"]);
105
127
  const formRef = ref<any>(null);
106
128
  const valid = ref(false);
107
129
  const submitting = ref(false);
130
+ const attachmentPhotos = ref<string[]>([]);
131
+ const attachmentIds = ref<string[]>([]);
132
+ const photoUploadError = ref("");
108
133
 
109
134
  const { requiredRule } = useUtils();
110
135
 
@@ -124,6 +149,29 @@ const unitOfMeasurementOptions = [
124
149
  watchEffect(() => {
125
150
  nameModel.value = prop.equipmentData?.name || "";
126
151
  unitOfMeasurementModel.value = prop.equipmentData?.unitOfMeasurement || "";
152
+ attachmentModel.value = prop.equipmentData?.attachment || "";
153
+ });
154
+
155
+ watch(
156
+ attachmentIds,
157
+ (photoIds) => {
158
+ const [attachmentId] = photoIds;
159
+ attachmentModel.value = attachmentId || "";
160
+
161
+ if (photoIds.length > 1) {
162
+ attachmentIds.value = attachmentId ? [attachmentId] : [];
163
+ attachmentPhotos.value = attachmentPhotos.value.slice(0, 1);
164
+ }
165
+ },
166
+ { deep: true }
167
+ );
168
+
169
+ watch(showDialog, (isOpen) => {
170
+ if (!isOpen) {
171
+ attachmentPhotos.value = [];
172
+ attachmentIds.value = [];
173
+ photoUploadError.value = "";
174
+ }
127
175
  });
128
176
 
129
177
  function close() {
@@ -147,12 +195,15 @@ async function submit() {
147
195
 
148
196
  emit("saved", {
149
197
  name: nameModel.value,
150
-
151
198
  unitOfMeasurement: unitOfMeasurementModel.value,
199
+ attachment: attachmentModel.value,
152
200
  });
153
201
 
154
202
  nameModel.value = "";
155
203
  unitOfMeasurementModel.value = "";
204
+ attachmentModel.value = "";
205
+ attachmentPhotos.value = [];
206
+ attachmentIds.value = [];
156
207
 
157
208
  showDialog.value = false;
158
209
  emit("close");
@@ -28,7 +28,6 @@
28
28
  :items="items"
29
29
  fixed-header
30
30
  hide-default-footer
31
- hide-default-header
32
31
  @click:row="handleRowClick"
33
32
  style="max-height: 300px"
34
33
  >
@@ -281,10 +280,10 @@ const prop = defineProps({
281
280
  headers: {
282
281
  type: Array as PropType<Array<any>>,
283
282
  default: () => [
284
- { text: "Host", value: "host" },
285
- { text: "Category", value: "category" },
286
- { text: "Guard House", value: "guardPost" },
287
- { text: "Actions", value: "action", sortable: false },
283
+ // { title: "Host", key: "host" },
284
+ // { title: "Category", key: "category" },
285
+ // { title: "Guard House", key: "guardPost" },
286
+ // { title: "Actions", key: "action", sortable: false },
288
287
  ],
289
288
  },
290
289
  readOnly: {
@@ -295,11 +294,20 @@ const prop = defineProps({
295
294
 
296
295
  const computedHeaders = computed(() => {
297
296
  if (prop.type === "ip") {
298
- const base = [...prop.headers];
299
- base.splice(0, 0, { text: "Name", value: "name" });
300
- return base;
297
+ // const base = [...prop.headers];
298
+ // base.splice(0, 0, { title: "Name", key: "name" });
299
+ // return base;
300
+ return [
301
+ { title: "Camera Name", key: "name" },
302
+ { title: "URL", key: "host" },
303
+ ]
304
+ }else if(prop.type === "anpr"){
305
+ return [
306
+ { title: "URL", key: "host" },
307
+ { title: "Type", key: "direction" },
308
+ ]
301
309
  }
302
- return prop.headers;
310
+ // return prop.headers;
303
311
  });
304
312
 
305
313
  const items = ref<Array<TCamera>>([]);
@@ -984,60 +984,44 @@
984
984
  <div
985
985
  v-for="item in formattedActivePatrolItems"
986
986
  :key="item.id || item._id"
987
- class="d-flex align-center justify-space-between"
988
- style="
989
- padding: 16px 20px;
990
- border: 1px solid #e2e8f0;
991
- border-radius: 12px;
992
- margin-bottom: 12px;
993
- background-color: #ffffff;
994
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
995
- "
987
+ class="active-patrol-row"
996
988
  >
997
- <div style="flex: 1; min-width: 140px">
998
- <strong
999
- style="
1000
- display: block;
1001
- font-size: 15px;
1002
- font-weight: 700;
1003
- color: #0b2f47;
1004
- margin-bottom: 4px;
1005
- "
1006
- >{{ item.title || item.name }}</strong
1007
- >
1008
- <span style="font-size: 13px; color: #94a3b8">{{
1009
- item.subtitle || item.location || ""
1010
- }}</span>
989
+ <div class="active-patrol-main">
990
+ <strong>{{ item.title || item.name }}</strong>
991
+ <span>{{ item.subtitle || item.location || "" }}</span>
1011
992
  </div>
1012
993
 
1013
- <div
1014
- style="flex: 1; display: flex; align-items: center; gap: 12px"
1015
- >
1016
- <AvatarMain
1017
- :name="item.person || item.assignedTo"
1018
- :size="36"
1019
- />
1020
- <span style="font-size: 14px; color: #64748b">{{
1021
- item.person || item.assignedTo
1022
- }}</span>
994
+ <div class="active-patrol-assignees">
995
+ <div class="active-patrol-assignee">
996
+ <AvatarMain :name="item.primaryAssignee" :size="24" />
997
+ <span>{{ item.primaryAssignee }}</span>
998
+ </div>
999
+ <span
1000
+ v-if="item.extraAssigneeCount > 0"
1001
+ class="active-patrol-more-pill active-patrol-tooltip"
1002
+ :data-tooltip="item.extraAssignees.join(', ')"
1003
+ >
1004
+ +{{ item.extraAssigneeCount }}
1005
+ </span>
1023
1006
  </div>
1024
1007
 
1025
- <div style="flex: 1; display: flex; justify-content: center">
1008
+ <div class="active-patrol-statuses">
1026
1009
  <div
1027
- :style="getPatrolStatusStyle(item.status)"
1028
- style="
1029
- padding: 6px 16px;
1030
- border-radius: 20px;
1031
- font-size: 13px;
1032
- font-weight: 500;
1033
- text-transform: capitalize;
1034
- "
1010
+ class="active-patrol-status-pill"
1011
+ :style="getPatrolStatusStyle(item.primaryStatus)"
1035
1012
  >
1036
- {{ item.status }}
1013
+ {{ item.primaryStatus }}
1037
1014
  </div>
1015
+ <span
1016
+ v-if="item.extraStatusCount > 0"
1017
+ class="active-patrol-more-pill active-patrol-tooltip"
1018
+ :data-tooltip="item.extraStatuses.join(', ')"
1019
+ >
1020
+ +{{ item.extraStatusCount }}
1021
+ </span>
1038
1022
  </div>
1039
1023
 
1040
- <div style="flex: 0 0 auto; text-align: right">
1024
+ <div class="active-patrol-action">
1041
1025
  <v-btn
1042
1026
  variant="outlined"
1043
1027
  class="text-none font-weight-medium"
@@ -1545,30 +1529,16 @@ const { userAppRole } = useLocalSetup();
1545
1529
  const { downloadPDF } = usePDFDownload();
1546
1530
  const route = useRoute();
1547
1531
 
1548
- const dashboardWidgetPermissions: Record<string, string> = {
1549
- activity: "dashboard:view-activity",
1550
- recentActivity: "dashboard:view-activity",
1551
- taskSchedule: "dashboard:view-task-schedule",
1552
- staffStatus: "dashboard:view-attendance-widget",
1553
- attendance: "dashboard:view-attendance-widget",
1554
- feedbacks: "dashboard:view-feedbacks-widget",
1555
- workOrderStatus: "dashboard:view-work-order-status",
1556
- activePatrol: "dashboard:view-active-patrol",
1557
- upcomingEvents: "dashboard:view-upcoming-events",
1558
- todayReminders: "dashboard:view-today-reminders",
1559
- todayAttentions: "dashboard:view-today-attentions",
1560
- };
1561
-
1562
1532
  const propertyWidgetPermissions: Record<string, string[]> = {
1563
1533
  workOrders: [
1564
- "workOrder:see-all-work-orders",
1534
+ "work_orders:see-all-work-orders",
1565
1535
  "work-order:see-all-work-orders",
1566
1536
  ],
1567
1537
  incidents: ["incident-reports:see-incident-reports"],
1568
1538
  visitors: ["visitorManagement:see-all-visitor"],
1569
1539
  facilityBookings: ["facility-booking-mgmt:see-all-facility-booking"],
1570
1540
  activity: [
1571
- "workOrder:see-all-work-orders",
1541
+ "work_orders:see-all-work-orders",
1572
1542
  "work-order:see-all-work-orders",
1573
1543
  ],
1574
1544
  upcomingEvents: ["event-mgmt:see-all-event"],
@@ -1578,7 +1548,7 @@ const propertyWidgetPermissions: Record<string, string[]> = {
1578
1548
  "facility-booking-mgmt:see-all-facility-booking",
1579
1549
  ],
1580
1550
  workOrderStatus: [
1581
- "workOrder:see-all-work-orders",
1551
+ "work_orders:see-all-work-orders",
1582
1552
  "work-order:see-all-work-orders",
1583
1553
  ],
1584
1554
  feedbacks: ["feedbacks:see-all-feedback"],
@@ -1587,27 +1557,30 @@ const propertyWidgetPermissions: Record<string, string[]> = {
1587
1557
 
1588
1558
  const moduleWidgetPermissions: Record<string, string[]> = {
1589
1559
  openWorkOrder: [
1560
+ "work_orders:see-all-work-orders",
1590
1561
  "work_order:see-all-work-orders",
1591
1562
  "work-order:see-all-work-orders",
1592
1563
  "work-orders:see-all-work-orders",
1593
1564
  ],
1594
1565
  workOrders: [
1566
+ "work_orders:see-all-work-orders",
1595
1567
  "work_order:see-all-work-orders",
1596
1568
  "work-order:see-all-work-orders",
1597
1569
  "work-orders:see-all-work-orders",
1598
1570
  ],
1599
1571
  activity: [
1572
+ "work_orders:see-all-work-orders",
1600
1573
  "work_order:see-all-work-orders",
1601
1574
  "work-order:see-all-work-orders",
1602
1575
  "work-orders:see-all-work-orders",
1576
+ "work_orders:see-all-work-orders",
1603
1577
  ],
1604
- taskSchedule: [
1605
- "cleaning-schedule-mgmt:see-all-schedules",
1606
- "schedule-task-mgmt:see-all-schedule-tasks",
1607
- ],
1608
- taskCompleted: [
1609
- "cleaning-schedule-mgmt:see-all-schedules",
1610
- "schedule-task-mgmt:see-all-schedule-tasks",
1578
+ recentActivity: [
1579
+ "work_orders:see-all-work-orders",
1580
+ "work_order:see-all-work-orders",
1581
+ "work-order:see-all-work-orders",
1582
+ "work-orders:see-all-work-orders",
1583
+ "work_orders:see-all-work-orders",
1611
1584
  ],
1612
1585
  staffStatus: ["attendance-mgmt:see-all-attendance"],
1613
1586
  attendance: ["attendance-mgmt:see-all-attendance"],
@@ -1630,9 +1603,14 @@ const moduleWidgetPermissions: Record<string, string[]> = {
1630
1603
  activePatrol: ["virtual-patrol:see-all-virtual-patrol-logs"],
1631
1604
  };
1632
1605
 
1606
+ function hasSchedulePermission(permissions: string[], action: string) {
1607
+ return permissions.some((permission) =>
1608
+ permission.endsWith(`-schedule-mgmt:${action}`)
1609
+ );
1610
+ }
1611
+
1633
1612
  function canViewWidget(key: string) {
1634
1613
  const permissions = userAppRole.value?.permissions;
1635
- const dashboardPermission = dashboardWidgetPermissions[key];
1636
1614
  const modulePermissions = isPropertyMode.value
1637
1615
  ? propertyWidgetPermissions[key]
1638
1616
  : moduleWidgetPermissions[key];
@@ -1640,20 +1618,22 @@ function canViewWidget(key: string) {
1640
1618
  if (!permissions) return true;
1641
1619
  if (permissions.includes("*")) return true;
1642
1620
 
1621
+ if (
1622
+ !isPropertyMode.value &&
1623
+ ["taskSchedule", "taskCompleted"].includes(key)
1624
+ ) {
1625
+ return hasSchedulePermission(permissions, "see-all-schedules");
1626
+ }
1627
+
1643
1628
  if (isPropertyMode.value && modulePermissions) {
1644
1629
  return modulePermissions.some((permission) =>
1645
1630
  permissions.includes(permission)
1646
1631
  );
1647
1632
  }
1648
1633
 
1649
- if (dashboardPermission || modulePermissions) {
1650
- return (
1651
- (dashboardPermission && permissions.includes(dashboardPermission)) ||
1652
- Boolean(
1653
- modulePermissions?.some((permission) =>
1654
- permissions.includes(permission)
1655
- )
1656
- )
1634
+ if (modulePermissions) {
1635
+ return modulePermissions.some((permission) =>
1636
+ permissions.includes(permission)
1657
1637
  );
1658
1638
  }
1659
1639
 
@@ -2771,18 +2751,23 @@ const activePatrolItems = computed<any[]>(
2771
2751
 
2772
2752
  const formattedActivePatrolItems = computed<any[]>(() => {
2773
2753
  return activePatrolItems.value.map((item) => {
2774
- let status = item.status;
2775
- if (Array.isArray(status)) {
2776
- status = status.join(", ");
2777
- }
2778
- let person = item.person || item.assignedTo || "Unassigned";
2779
- if (Array.isArray(person)) {
2780
- person = person.join(", ");
2781
- }
2754
+ const statuses = normalizeListValue(item.status || "Pending");
2755
+ const assignees = normalizeListValue(
2756
+ item.person || item.assignedTo || "Unassigned"
2757
+ );
2758
+
2782
2759
  return {
2783
2760
  ...item,
2784
- status,
2785
- person,
2761
+ status: statuses.join(", "),
2762
+ statuses,
2763
+ primaryStatus: statuses[0] || "Pending",
2764
+ extraStatuses: statuses.slice(1),
2765
+ extraStatusCount: Math.max(0, statuses.length - 1),
2766
+ person: assignees.join(", "),
2767
+ assignees,
2768
+ primaryAssignee: assignees[0] || "Unassigned",
2769
+ extraAssignees: assignees.slice(1),
2770
+ extraAssigneeCount: Math.max(0, assignees.length - 1),
2786
2771
  };
2787
2772
  });
2788
2773
  });
@@ -3952,6 +3937,20 @@ function normalizeStatus(value: any) {
3952
3937
  return text.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
3953
3938
  }
3954
3939
 
3940
+ function normalizeListValue(value: any) {
3941
+ if (Array.isArray(value)) {
3942
+ return value
3943
+ .flatMap((entry) => normalizeListValue(entry))
3944
+ .map((entry) => String(entry).trim())
3945
+ .filter(Boolean);
3946
+ }
3947
+
3948
+ return String(value || "")
3949
+ .split(",")
3950
+ .map((entry) => entry.trim())
3951
+ .filter(Boolean);
3952
+ }
3953
+
3955
3954
  function getStatusTone(status: any) {
3956
3955
  const s = String(status || "").toLowerCase();
3957
3956
  if (
@@ -4419,6 +4418,151 @@ function formatDashboardTime(value?: string) {
4419
4418
  border: 2px solid #f9c97c;
4420
4419
  }
4421
4420
 
4421
+ .active-patrol-row {
4422
+ align-items: center;
4423
+ background-color: #ffffff;
4424
+ border: 1px solid #e2e8f0;
4425
+ border-radius: 8px;
4426
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
4427
+ display: grid;
4428
+ gap: 10px;
4429
+ grid-template-columns: 150px minmax(0, 1fr) max-content max-content;
4430
+ margin-bottom: 12px;
4431
+ min-height: 64px;
4432
+ overflow: visible;
4433
+ padding: 12px 14px;
4434
+ }
4435
+
4436
+ .active-patrol-main {
4437
+ min-width: 0;
4438
+ }
4439
+
4440
+ .active-patrol-main strong {
4441
+ color: #0b2f47;
4442
+ display: block;
4443
+ font-size: 13px;
4444
+ font-weight: 700;
4445
+ line-height: 1.25;
4446
+ margin-bottom: 3px;
4447
+ overflow-wrap: anywhere;
4448
+ }
4449
+
4450
+ .active-patrol-main span {
4451
+ color: #94a3b8;
4452
+ display: block;
4453
+ font-size: 11px;
4454
+ line-height: 1.25;
4455
+ overflow-wrap: anywhere;
4456
+ }
4457
+
4458
+ .active-patrol-assignees {
4459
+ align-items: center;
4460
+ display: flex;
4461
+ gap: 6px;
4462
+ min-width: 0;
4463
+ }
4464
+
4465
+ .active-patrol-assignee {
4466
+ align-items: center;
4467
+ background: #f8fafc;
4468
+ border: 1px solid #e7edf3;
4469
+ border-radius: 999px;
4470
+ display: inline-flex;
4471
+ flex: 1 1 auto;
4472
+ gap: 6px;
4473
+ max-width: 100%;
4474
+ min-width: 0;
4475
+ padding: 2px 8px 2px 3px;
4476
+ }
4477
+
4478
+ .active-patrol-assignee span {
4479
+ color: #475569;
4480
+ font-size: 11px;
4481
+ font-weight: 600;
4482
+ line-height: 1.2;
4483
+ min-width: 0;
4484
+ overflow: hidden;
4485
+ text-overflow: ellipsis;
4486
+ white-space: nowrap;
4487
+ }
4488
+
4489
+ .active-patrol-statuses {
4490
+ align-items: center;
4491
+ display: flex;
4492
+ gap: 6px;
4493
+ justify-content: flex-start;
4494
+ min-width: 0;
4495
+ }
4496
+
4497
+ .active-patrol-status-pill {
4498
+ border-radius: 20px;
4499
+ font-size: 11px;
4500
+ font-weight: 600;
4501
+ line-height: 1.2;
4502
+ padding: 5px 10px;
4503
+ text-transform: capitalize;
4504
+ white-space: nowrap;
4505
+ }
4506
+
4507
+ .active-patrol-more-pill {
4508
+ align-items: center;
4509
+ background: #eef3f8;
4510
+ border: 1px solid #dbe4ed;
4511
+ border-radius: 999px;
4512
+ color: #607386;
4513
+ display: inline-flex;
4514
+ flex: 0 0 auto;
4515
+ font-size: 11px;
4516
+ font-weight: 800;
4517
+ height: 24px;
4518
+ justify-content: center;
4519
+ line-height: 1;
4520
+ min-width: 30px;
4521
+ padding: 0 8px;
4522
+ position: relative;
4523
+ white-space: nowrap;
4524
+ }
4525
+
4526
+ .active-patrol-action {
4527
+ justify-self: end;
4528
+ text-align: right;
4529
+ }
4530
+
4531
+ .active-patrol-tooltip::after {
4532
+ background: #0f2638;
4533
+ border-radius: 6px;
4534
+ bottom: calc(100% + 8px);
4535
+ box-shadow: 0 8px 24px rgba(15, 38, 56, 0.18);
4536
+ color: #ffffff;
4537
+ content: attr(data-tooltip);
4538
+ font-size: 11px;
4539
+ font-weight: 600;
4540
+ left: 50%;
4541
+ line-height: 1.35;
4542
+ max-width: 260px;
4543
+ opacity: 0;
4544
+ padding: 7px 9px;
4545
+ pointer-events: none;
4546
+ position: absolute;
4547
+ text-align: left;
4548
+ transform: translate(-50%, 4px);
4549
+ transition: opacity 0.14s ease, transform 0.14s ease;
4550
+ white-space: normal;
4551
+ width: max-content;
4552
+ z-index: 5;
4553
+ }
4554
+
4555
+ .active-patrol-tooltip:hover::after {
4556
+ opacity: 1;
4557
+ transform: translate(-50%, 0);
4558
+ }
4559
+
4560
+ @media (max-width: 960px) {
4561
+ .active-patrol-row {
4562
+ grid-template-columns: 120px minmax(0, 1fr) max-content max-content;
4563
+ }
4564
+ }
4565
+
4422
4566
  .figma-icon-badge {
4423
4567
  align-items: center;
4424
4568
  border-radius: 50%;