@7365admin1/layer-common 3.1.2 → 3.1.3-staging.39
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.
- package/.github/workflows/publish-staging.yml +11 -0
- package/components/AddEqupmentForm.vue +52 -1
- package/components/CameraMain.vue +5 -6
- package/components/DashboardMain.vue +231 -87
- package/components/EquipmentManagementMain.vue +56 -0
- package/components/InvitationMain.vue +4 -3
- package/components/ManageChecklistMain.vue +44 -38
- package/components/Nfc/NFCPatrolReportMain.vue +146 -38
- package/components/Nfc/PatrolReport/DailyReportTab.vue +10 -6
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +19 -9
- package/components/Nfc/PatrolReport/MonthlyReportTable.vue +69 -0
- package/components/Nfc/PatrolReport/PatrolActivityTable.vue +20 -0
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +5 -3
- package/components/Nfc/PatrolReport/ReportDatePicker.vue +61 -0
- package/components/Nfc/PatrolReport/ReportFilters.vue +52 -20
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +22 -0
- package/components/PhotoUpload.vue +19 -4
- package/components/ServiceProviderMain.vue +59 -25
- package/components/VisitorForm.vue +78 -18
- package/components/VisitorManagement.vue +12 -16
- package/composables/useCleaningSchedulePermission.ts +16 -51
- package/composables/useEquipment.ts +2 -0
- package/composables/useLocalAuth.ts +1 -1
- package/composables/useNFCPatrolDailyReport.ts +3 -4
- package/composables/useNFCPatrolMonthlyReport.ts +62 -30
- package/composables/useNFCPatrolReport.ts +2 -0
- package/composables/useServiceProvider.ts +21 -0
- package/package.json +1 -1
- package/types/customer.d.ts +2 -1
- package/types/equipment.d.ts +5 -1
- 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
|
-
{
|
|
285
|
-
{
|
|
286
|
-
{
|
|
287
|
-
{
|
|
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: {
|
|
@@ -296,7 +295,7 @@ const prop = defineProps({
|
|
|
296
295
|
const computedHeaders = computed(() => {
|
|
297
296
|
if (prop.type === "ip") {
|
|
298
297
|
const base = [...prop.headers];
|
|
299
|
-
base.splice(0, 0, {
|
|
298
|
+
base.splice(0, 0, { title: "Name", key: "name" });
|
|
300
299
|
return base;
|
|
301
300
|
}
|
|
302
301
|
return prop.headers;
|
|
@@ -984,60 +984,44 @@
|
|
|
984
984
|
<div
|
|
985
985
|
v-for="item in formattedActivePatrolItems"
|
|
986
986
|
:key="item.id || item._id"
|
|
987
|
-
class="
|
|
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
|
|
998
|
-
<strong
|
|
999
|
-
|
|
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
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
item.
|
|
1022
|
-
|
|
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
|
|
1008
|
+
<div class="active-patrol-statuses">
|
|
1026
1009
|
<div
|
|
1027
|
-
|
|
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.
|
|
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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
1605
|
-
"
|
|
1606
|
-
"
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
"
|
|
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 (
|
|
1650
|
-
return (
|
|
1651
|
-
|
|
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
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
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
|
-
|
|
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%;
|
|
@@ -46,6 +46,19 @@
|
|
|
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
|
+
|
|
49
62
|
<template #item.status="{ value }">
|
|
50
63
|
<v-chip class="text-capitalize">{{ value || "N/A" }}</v-chip>
|
|
51
64
|
</template>
|
|
@@ -61,6 +74,7 @@
|
|
|
61
74
|
v-model="dialogShowForm"
|
|
62
75
|
v-model:name="equipmentName"
|
|
63
76
|
v-model:unitOfMeasurement="equipmentUnit"
|
|
77
|
+
v-model:attachment="equipmentAttachment"
|
|
64
78
|
:mode="dialogMode"
|
|
65
79
|
:equipmentData="editingEquipment"
|
|
66
80
|
:site="props.site"
|
|
@@ -242,6 +256,29 @@
|
|
|
242
256
|
</v-card>
|
|
243
257
|
</v-dialog>
|
|
244
258
|
|
|
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
|
+
|
|
245
282
|
<Snackbar
|
|
246
283
|
v-model="messageSnackbar"
|
|
247
284
|
:text="snackbarMessage"
|
|
@@ -253,6 +290,7 @@ import useEquipment from "../composables/useEquipment";
|
|
|
253
290
|
import useStock from "../composables/useStock";
|
|
254
291
|
import useUtils from "../composables/useUtils";
|
|
255
292
|
import useEquipmentManagement from "../composables/useEquipmentManagement";
|
|
293
|
+
import useFile from "../composables/useFile";
|
|
256
294
|
|
|
257
295
|
const props = defineProps({
|
|
258
296
|
orgId: { type: String, default: "" },
|
|
@@ -280,6 +318,7 @@ const siteName = ref<string>("");
|
|
|
280
318
|
const submitting = ref(false);
|
|
281
319
|
|
|
282
320
|
const headers = [
|
|
321
|
+
{ title: "Image", value: "attachment" },
|
|
283
322
|
{ title: "Name", value: "name" },
|
|
284
323
|
{ title: "Available Stock Qty", value: "qty" },
|
|
285
324
|
{ title: "Status", value: "status" },
|
|
@@ -293,6 +332,7 @@ const {
|
|
|
293
332
|
deleteEquipment,
|
|
294
333
|
} = useEquipment();
|
|
295
334
|
const { debounce, requiredRule } = useUtils();
|
|
335
|
+
const { getFileUrl } = useFile();
|
|
296
336
|
|
|
297
337
|
const searchInput = ref("");
|
|
298
338
|
const startDate = ref("");
|
|
@@ -352,11 +392,21 @@ const dialogMode = ref<"add" | "edit">("add");
|
|
|
352
392
|
const editingEquipment = ref<Record<string, any>>({});
|
|
353
393
|
const equipmentName = ref("");
|
|
354
394
|
const equipmentUnit = ref("");
|
|
395
|
+
const equipmentAttachment = ref("");
|
|
355
396
|
|
|
356
397
|
const stockActionFormRef = ref<any>(null);
|
|
357
398
|
const stockActionValid = ref(false);
|
|
358
399
|
|
|
359
400
|
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
|
+
}
|
|
360
410
|
|
|
361
411
|
function showMessage(msg: string, color: string = "error") {
|
|
362
412
|
message.value = msg;
|
|
@@ -370,6 +420,7 @@ function onCreateUnit() {
|
|
|
370
420
|
editingEquipment.value = {};
|
|
371
421
|
equipmentName.value = "";
|
|
372
422
|
equipmentUnit.value = "";
|
|
423
|
+
equipmentAttachment.value = "";
|
|
373
424
|
dialogShowForm.value = true;
|
|
374
425
|
}
|
|
375
426
|
|
|
@@ -379,6 +430,9 @@ async function _createEquipment() {
|
|
|
379
430
|
const payload: TEquipmentCreate = {
|
|
380
431
|
name: equipmentName.value,
|
|
381
432
|
unitOfMeasurement: equipmentUnit.value,
|
|
433
|
+
...(equipmentAttachment.value
|
|
434
|
+
? { attachment: equipmentAttachment.value }
|
|
435
|
+
: {}),
|
|
382
436
|
};
|
|
383
437
|
|
|
384
438
|
let response;
|
|
@@ -398,6 +452,7 @@ async function _createEquipment() {
|
|
|
398
452
|
editingEquipment.value = {};
|
|
399
453
|
equipmentName.value = "";
|
|
400
454
|
equipmentUnit.value = "";
|
|
455
|
+
equipmentAttachment.value = "";
|
|
401
456
|
|
|
402
457
|
await getEquipmentsRefresh();
|
|
403
458
|
} catch (error: any) {
|
|
@@ -455,6 +510,7 @@ const onEditFromMoreAction = async () => {
|
|
|
455
510
|
editingEquipment.value = response;
|
|
456
511
|
equipmentName.value = response.name || "";
|
|
457
512
|
equipmentUnit.value = response.unitOfMeasurement || "";
|
|
513
|
+
equipmentAttachment.value = response.attachment || "";
|
|
458
514
|
dialogShowForm.value = true;
|
|
459
515
|
};
|
|
460
516
|
|