@7365admin1/layer-common 3.0.31-staging.23 → 3.1.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.
@@ -984,44 +984,60 @@
984
984
  <div
985
985
  v-for="item in formattedActivePatrolItems"
986
986
  :key="item.id || item._id"
987
- class="active-patrol-row"
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
+ "
988
996
  >
989
- <div class="active-patrol-main">
990
- <strong>{{ item.title || item.name }}</strong>
991
- <span>{{ item.subtitle || item.location || "" }}</span>
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>
992
1011
  </div>
993
1012
 
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>
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>
1006
1023
  </div>
1007
1024
 
1008
- <div class="active-patrol-statuses">
1025
+ <div style="flex: 1; display: flex; justify-content: center">
1009
1026
  <div
1010
- class="active-patrol-status-pill"
1011
- :style="getPatrolStatusStyle(item.primaryStatus)"
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
+ "
1012
1035
  >
1013
- {{ item.primaryStatus }}
1036
+ {{ item.status }}
1014
1037
  </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>
1022
1038
  </div>
1023
1039
 
1024
- <div class="active-patrol-action">
1040
+ <div style="flex: 0 0 auto; text-align: right">
1025
1041
  <v-btn
1026
1042
  variant="outlined"
1027
1043
  class="text-none font-weight-medium"
@@ -1529,16 +1545,30 @@ const { userAppRole } = useLocalSetup();
1529
1545
  const { downloadPDF } = usePDFDownload();
1530
1546
  const route = useRoute();
1531
1547
 
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
+
1532
1562
  const propertyWidgetPermissions: Record<string, string[]> = {
1533
1563
  workOrders: [
1534
- "work_orders:see-all-work-orders",
1564
+ "workOrder:see-all-work-orders",
1535
1565
  "work-order:see-all-work-orders",
1536
1566
  ],
1537
1567
  incidents: ["incident-reports:see-incident-reports"],
1538
1568
  visitors: ["visitorManagement:see-all-visitor"],
1539
1569
  facilityBookings: ["facility-booking-mgmt:see-all-facility-booking"],
1540
1570
  activity: [
1541
- "work_orders:see-all-work-orders",
1571
+ "workOrder:see-all-work-orders",
1542
1572
  "work-order:see-all-work-orders",
1543
1573
  ],
1544
1574
  upcomingEvents: ["event-mgmt:see-all-event"],
@@ -1548,7 +1578,7 @@ const propertyWidgetPermissions: Record<string, string[]> = {
1548
1578
  "facility-booking-mgmt:see-all-facility-booking",
1549
1579
  ],
1550
1580
  workOrderStatus: [
1551
- "work_orders:see-all-work-orders",
1581
+ "workOrder:see-all-work-orders",
1552
1582
  "work-order:see-all-work-orders",
1553
1583
  ],
1554
1584
  feedbacks: ["feedbacks:see-all-feedback"],
@@ -1557,30 +1587,27 @@ const propertyWidgetPermissions: Record<string, string[]> = {
1557
1587
 
1558
1588
  const moduleWidgetPermissions: Record<string, string[]> = {
1559
1589
  openWorkOrder: [
1560
- "work_orders:see-all-work-orders",
1561
1590
  "work_order:see-all-work-orders",
1562
1591
  "work-order:see-all-work-orders",
1563
1592
  "work-orders:see-all-work-orders",
1564
1593
  ],
1565
1594
  workOrders: [
1566
- "work_orders:see-all-work-orders",
1567
1595
  "work_order:see-all-work-orders",
1568
1596
  "work-order:see-all-work-orders",
1569
1597
  "work-orders:see-all-work-orders",
1570
1598
  ],
1571
1599
  activity: [
1572
- "work_orders:see-all-work-orders",
1573
1600
  "work_order:see-all-work-orders",
1574
1601
  "work-order:see-all-work-orders",
1575
1602
  "work-orders:see-all-work-orders",
1576
- "work_orders:see-all-work-orders",
1577
1603
  ],
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",
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",
1584
1611
  ],
1585
1612
  staffStatus: ["attendance-mgmt:see-all-attendance"],
1586
1613
  attendance: ["attendance-mgmt:see-all-attendance"],
@@ -1603,14 +1630,9 @@ const moduleWidgetPermissions: Record<string, string[]> = {
1603
1630
  activePatrol: ["virtual-patrol:see-all-virtual-patrol-logs"],
1604
1631
  };
1605
1632
 
1606
- function hasSchedulePermission(permissions: string[], action: string) {
1607
- return permissions.some((permission) =>
1608
- permission.endsWith(`-schedule-mgmt:${action}`)
1609
- );
1610
- }
1611
-
1612
1633
  function canViewWidget(key: string) {
1613
1634
  const permissions = userAppRole.value?.permissions;
1635
+ const dashboardPermission = dashboardWidgetPermissions[key];
1614
1636
  const modulePermissions = isPropertyMode.value
1615
1637
  ? propertyWidgetPermissions[key]
1616
1638
  : moduleWidgetPermissions[key];
@@ -1618,22 +1640,20 @@ function canViewWidget(key: string) {
1618
1640
  if (!permissions) return true;
1619
1641
  if (permissions.includes("*")) return true;
1620
1642
 
1621
- if (
1622
- !isPropertyMode.value &&
1623
- ["taskSchedule", "taskCompleted"].includes(key)
1624
- ) {
1625
- return hasSchedulePermission(permissions, "see-all-schedules");
1626
- }
1627
-
1628
1643
  if (isPropertyMode.value && modulePermissions) {
1629
1644
  return modulePermissions.some((permission) =>
1630
1645
  permissions.includes(permission)
1631
1646
  );
1632
1647
  }
1633
1648
 
1634
- if (modulePermissions) {
1635
- return modulePermissions.some((permission) =>
1636
- permissions.includes(permission)
1649
+ if (dashboardPermission || modulePermissions) {
1650
+ return (
1651
+ (dashboardPermission && permissions.includes(dashboardPermission)) ||
1652
+ Boolean(
1653
+ modulePermissions?.some((permission) =>
1654
+ permissions.includes(permission)
1655
+ )
1656
+ )
1637
1657
  );
1638
1658
  }
1639
1659
 
@@ -2751,23 +2771,18 @@ const activePatrolItems = computed<any[]>(
2751
2771
 
2752
2772
  const formattedActivePatrolItems = computed<any[]>(() => {
2753
2773
  return activePatrolItems.value.map((item) => {
2754
- const statuses = normalizeListValue(item.status || "Pending");
2755
- const assignees = normalizeListValue(
2756
- item.person || item.assignedTo || "Unassigned"
2757
- );
2758
-
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
+ }
2759
2782
  return {
2760
2783
  ...item,
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),
2784
+ status,
2785
+ person,
2771
2786
  };
2772
2787
  });
2773
2788
  });
@@ -3937,20 +3952,6 @@ function normalizeStatus(value: any) {
3937
3952
  return text.replace(/[_-]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
3938
3953
  }
3939
3954
 
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
-
3954
3955
  function getStatusTone(status: any) {
3955
3956
  const s = String(status || "").toLowerCase();
3956
3957
  if (
@@ -4418,151 +4419,6 @@ function formatDashboardTime(value?: string) {
4418
4419
  border: 2px solid #f9c97c;
4419
4420
  }
4420
4421
 
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
-
4566
4422
  .figma-icon-badge {
4567
4423
  align-items: center;
4568
4424
  border-radius: 50%;
@@ -46,19 +46,6 @@
46
46
  </v-row>
47
47
  </template>
48
48
 
49
- <template #item.attachment="{ value }">
50
- <v-btn
51
- v-if="value"
52
- size="small"
53
- variant="tonal"
54
- class="text-none"
55
- @click.stop="viewAttachment(value)"
56
- >
57
- View Image
58
- </v-btn>
59
- <span v-else>—</span>
60
- </template>
61
-
62
49
  <template #item.status="{ value }">
63
50
  <v-chip class="text-capitalize">{{ value || "N/A" }}</v-chip>
64
51
  </template>
@@ -74,7 +61,6 @@
74
61
  v-model="dialogShowForm"
75
62
  v-model:name="equipmentName"
76
63
  v-model:unitOfMeasurement="equipmentUnit"
77
- v-model:attachment="equipmentAttachment"
78
64
  :mode="dialogMode"
79
65
  :equipmentData="editingEquipment"
80
66
  :site="props.site"
@@ -256,29 +242,6 @@
256
242
  </v-card>
257
243
  </v-dialog>
258
244
 
259
- <v-dialog v-model="showAttachmentDialog" max-width="720" persistent>
260
- <v-card rounded="lg">
261
- <v-card-title class="d-flex align-center pa-4">
262
- <span class="text-h6 font-weight-bold">Attachment</span>
263
-
264
- <v-spacer />
265
-
266
- <v-btn
267
- icon="mdi-close"
268
- variant="text"
269
- size="small"
270
- @click="(showAttachmentDialog = false), (attachmentUrl = '')"
271
- />
272
- </v-card-title>
273
-
274
- <v-divider />
275
-
276
- <v-card-text class="pa-6">
277
- <v-img :src="attachmentUrl" contain />
278
- </v-card-text>
279
- </v-card>
280
- </v-dialog>
281
-
282
245
  <Snackbar
283
246
  v-model="messageSnackbar"
284
247
  :text="snackbarMessage"
@@ -290,7 +253,6 @@ import useEquipment from "../composables/useEquipment";
290
253
  import useStock from "../composables/useStock";
291
254
  import useUtils from "../composables/useUtils";
292
255
  import useEquipmentManagement from "../composables/useEquipmentManagement";
293
- import useFile from "../composables/useFile";
294
256
 
295
257
  const props = defineProps({
296
258
  orgId: { type: String, default: "" },
@@ -318,7 +280,6 @@ const siteName = ref<string>("");
318
280
  const submitting = ref(false);
319
281
 
320
282
  const headers = [
321
- { title: "Image", value: "attachment" },
322
283
  { title: "Name", value: "name" },
323
284
  { title: "Available Stock Qty", value: "qty" },
324
285
  { title: "Status", value: "status" },
@@ -332,7 +293,6 @@ const {
332
293
  deleteEquipment,
333
294
  } = useEquipment();
334
295
  const { debounce, requiredRule } = useUtils();
335
- const { getFileUrl } = useFile();
336
296
 
337
297
  const searchInput = ref("");
338
298
  const startDate = ref("");
@@ -392,21 +352,11 @@ const dialogMode = ref<"add" | "edit">("add");
392
352
  const editingEquipment = ref<Record<string, any>>({});
393
353
  const equipmentName = ref("");
394
354
  const equipmentUnit = ref("");
395
- const equipmentAttachment = ref("");
396
355
 
397
356
  const stockActionFormRef = ref<any>(null);
398
357
  const stockActionValid = ref(false);
399
358
 
400
359
  const snackbarMessage = ref("");
401
- const showAttachmentDialog = ref(false);
402
- const attachmentUrl = ref("");
403
-
404
- function viewAttachment(attachmentId: string) {
405
- if (!attachmentId) return;
406
-
407
- attachmentUrl.value = getFileUrl(attachmentId);
408
- showAttachmentDialog.value = true;
409
- }
410
360
 
411
361
  function showMessage(msg: string, color: string = "error") {
412
362
  message.value = msg;
@@ -420,7 +370,6 @@ function onCreateUnit() {
420
370
  editingEquipment.value = {};
421
371
  equipmentName.value = "";
422
372
  equipmentUnit.value = "";
423
- equipmentAttachment.value = "";
424
373
  dialogShowForm.value = true;
425
374
  }
426
375
 
@@ -430,9 +379,6 @@ async function _createEquipment() {
430
379
  const payload: TEquipmentCreate = {
431
380
  name: equipmentName.value,
432
381
  unitOfMeasurement: equipmentUnit.value,
433
- ...(equipmentAttachment.value
434
- ? { attachment: equipmentAttachment.value }
435
- : {}),
436
382
  };
437
383
 
438
384
  let response;
@@ -452,7 +398,6 @@ async function _createEquipment() {
452
398
  editingEquipment.value = {};
453
399
  equipmentName.value = "";
454
400
  equipmentUnit.value = "";
455
- equipmentAttachment.value = "";
456
401
 
457
402
  await getEquipmentsRefresh();
458
403
  } catch (error: any) {
@@ -510,7 +455,6 @@ const onEditFromMoreAction = async () => {
510
455
  editingEquipment.value = response;
511
456
  equipmentName.value = response.name || "";
512
457
  equipmentUnit.value = response.unitOfMeasurement || "";
513
- equipmentAttachment.value = response.attachment || "";
514
458
  dialogShowForm.value = true;
515
459
  };
516
460
 
@@ -221,7 +221,7 @@ const headers = computed(() => {
221
221
  title: "Status",
222
222
  value: "status",
223
223
  },
224
-
224
+
225
225
  ];
226
226
 
227
227
  if (props.status === "pending" && props.cancelInvitation) {
@@ -270,11 +270,10 @@ const {
270
270
  page: page.value,
271
271
  status: props.status,
272
272
  search: headerSearch.value,
273
- // type: "user-invite,member-invite",
274
- type: "member-invite",
273
+ type: "user-invite,member-invite",
275
274
  app: props.app,
276
275
  orgId: props.org,
277
- // siteId: props.siteId,
276
+ siteId: props.siteId,
278
277
  }),
279
278
  {
280
279
  watch: [() => props.status]
@@ -305,38 +305,31 @@
305
305
 
306
306
  <v-divider />
307
307
 
308
- <v-toolbar class="pa-0" density="compact">
309
- <v-row no-gutters>
310
- <v-col cols="6" class="pa-0">
311
- <v-btn
312
- block
313
- variant="text"
314
- class="text-none"
315
- size="large"
316
- height="48"
317
- :disabled="rejectModal.submitting"
318
- @click="closeRejectDialog"
319
- >
320
- Cancel
321
- </v-btn>
322
- </v-col>
323
-
324
- <v-col cols="6" class="pa-0">
325
- <v-btn
326
- block
327
- variant="flat"
328
- color="black"
329
- class="text-none font-weight-bold rounded-0"
330
- height="48"
331
- :loading="rejectModal.submitting"
332
- :disabled="!isRejectModalValid || rejectModal.submitting"
333
- @click="submitRejectDialog"
334
- >
335
- Confirm
336
- </v-btn>
337
- </v-col>
338
- </v-row>
339
- </v-toolbar>
308
+ <v-card-actions class="pa-0">
309
+ <v-btn
310
+ color="grey"
311
+ variant="flat"
312
+ rounded="0"
313
+ class="flex-grow-1 text-none"
314
+ height="48"
315
+ :disabled="rejectModal.submitting"
316
+ @click="closeRejectDialog"
317
+ >
318
+ Cancel
319
+ </v-btn>
320
+ <v-btn
321
+ color="error"
322
+ variant="flat"
323
+ rounded="0"
324
+ class="flex-grow-1 text-none"
325
+ height="48"
326
+ :loading="rejectModal.submitting"
327
+ :disabled="!isRejectModalValid || rejectModal.submitting"
328
+ @click="submitRejectDialog"
329
+ >
330
+ Confirm
331
+ </v-btn>
332
+ </v-card-actions>
340
333
  </v-card>
341
334
  </v-dialog>
342
335