@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.
- package/CHANGELOG.md +6 -0
- package/components/AddEqupmentForm.vue +1 -52
- package/components/AddPassKeyToVisitor.vue +53 -115
- package/components/DashboardMain.vue +87 -231
- package/components/EquipmentManagementMain.vue +0 -56
- package/components/InvitationMain.vue +3 -4
- package/components/ManageChecklistMain.vue +25 -32
- package/components/Nfc/NFCPatrolReportMain.vue +38 -128
- package/components/Nfc/PatrolReport/DailyReportTab.vue +6 -10
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +9 -19
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +3 -5
- package/components/Nfc/PatrolReport/ReportFilters.vue +18 -50
- package/components/PhotoUpload.vue +4 -19
- package/components/ServiceProviderMain.vue +29 -46
- package/components/VisitorForm.vue +182 -19
- package/components/VisitorManagement.vue +30 -109
- package/composables/useCleaningSchedulePermission.ts +51 -16
- package/composables/useEquipment.ts +0 -2
- package/composables/useLocalAuth.ts +1 -1
- package/composables/useNFCPatrolDailyReport.ts +4 -1
- package/composables/useNFCPatrolMonthlyReport.ts +30 -62
- package/package.json +1 -1
- package/types/customer.d.ts +1 -2
- package/types/equipment.d.ts +1 -5
- package/types/nfc-patrol-report.ts +1 -15
- package/components/Nfc/PatrolReport/MonthlyReportTable.vue +0 -69
|
@@ -14,63 +14,44 @@
|
|
|
14
14
|
</v-col>
|
|
15
15
|
</v-row>
|
|
16
16
|
|
|
17
|
-
<v-tabs
|
|
18
|
-
v-
|
|
19
|
-
class="
|
|
20
|
-
|
|
21
|
-
grow
|
|
22
|
-
>
|
|
23
|
-
<v-tab value="patrol" class="text-none border-thin">
|
|
24
|
-
Patrol Report
|
|
25
|
-
</v-tab>
|
|
26
|
-
|
|
27
|
-
<v-tab value="daily" class="text-none border-thin">
|
|
28
|
-
Daily Report
|
|
29
|
-
</v-tab>
|
|
30
|
-
|
|
31
|
-
<v-tab value="monthly" class="text-none border-thin">
|
|
32
|
-
Monthly Report
|
|
33
|
-
</v-tab>
|
|
17
|
+
<v-tabs v-model="activeTab" class="mb-6" bg-color="transparent" grow>
|
|
18
|
+
<v-tab value="patrol" class="text-none border-thin">Patrol Report</v-tab>
|
|
19
|
+
<v-tab value="daily" class="text-none border-thin">Daily Report</v-tab>
|
|
20
|
+
<v-tab value="monthly" class="text-none border-thin">Monthly Report</v-tab>
|
|
34
21
|
</v-tabs>
|
|
35
22
|
|
|
36
23
|
<v-window v-model="activeTab" class="pt-4">
|
|
37
24
|
<v-window-item value="patrol">
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/>
|
|
47
|
-
</div>
|
|
25
|
+
<PatrolReportTab
|
|
26
|
+
:active="activeTab === 'patrol'"
|
|
27
|
+
:site="site"
|
|
28
|
+
:filters="filters"
|
|
29
|
+
:options="options"
|
|
30
|
+
@update:filters="updateFilters"
|
|
31
|
+
@export="exportReport"
|
|
32
|
+
/>
|
|
48
33
|
</v-window-item>
|
|
49
34
|
|
|
50
35
|
<v-window-item value="daily">
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/>
|
|
60
|
-
</div>
|
|
36
|
+
<DailyReportTab
|
|
37
|
+
:active="activeTab === 'daily'"
|
|
38
|
+
:site="site"
|
|
39
|
+
:filters="filters"
|
|
40
|
+
:options="options"
|
|
41
|
+
@update:filters="updateFilters"
|
|
42
|
+
@export="exportReport"
|
|
43
|
+
/>
|
|
61
44
|
</v-window-item>
|
|
62
45
|
|
|
63
46
|
<v-window-item value="monthly">
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/>
|
|
73
|
-
</div>
|
|
47
|
+
<MonthlyReportTab
|
|
48
|
+
:active="activeTab === 'monthly'"
|
|
49
|
+
:site="site"
|
|
50
|
+
:filters="filters"
|
|
51
|
+
:options="options"
|
|
52
|
+
@update:filters="updateFilters"
|
|
53
|
+
@export="exportReport"
|
|
54
|
+
/>
|
|
74
55
|
</v-window-item>
|
|
75
56
|
</v-window>
|
|
76
57
|
</v-col>
|
|
@@ -78,20 +59,15 @@
|
|
|
78
59
|
</template>
|
|
79
60
|
|
|
80
61
|
<script setup lang="ts">
|
|
81
|
-
import html2canvas from "html2canvas";
|
|
82
|
-
import jsPDF from "jspdf";
|
|
83
|
-
|
|
84
62
|
import type {
|
|
85
63
|
NFCPatrolReportFilters,
|
|
86
64
|
NFCPatrolReportTab,
|
|
87
65
|
} from "../../types/nfc-patrol-report";
|
|
88
|
-
|
|
66
|
+
import { useNFCPatrolReportExport } from "../../composables/useNFCPatrolReportExport";
|
|
89
67
|
import { useNFCPatrolReportFilters } from "../../composables/useNFCPatrolReportFilters";
|
|
90
|
-
|
|
91
|
-
import PatrolReportTab from "./PatrolReport/PatrolReportTab.vue";
|
|
92
68
|
import DailyReportTab from "./PatrolReport/DailyReportTab.vue";
|
|
93
69
|
import MonthlyReportTab from "./PatrolReport/MonthlyReportTab.vue";
|
|
94
|
-
|
|
70
|
+
import PatrolReportTab from "./PatrolReport/PatrolReportTab.vue";
|
|
95
71
|
|
|
96
72
|
const props = defineProps<{
|
|
97
73
|
site: string;
|
|
@@ -99,16 +75,11 @@ const props = defineProps<{
|
|
|
99
75
|
}>();
|
|
100
76
|
|
|
101
77
|
const router = useRouter();
|
|
102
|
-
|
|
103
|
-
|
|
104
78
|
const activeTab = ref<NFCPatrolReportTab>("patrol");
|
|
79
|
+
const { filters, options, fetchOptions } = useNFCPatrolReportFilters(props.site);
|
|
80
|
+
const { exportReport: exportActiveReport } = useNFCPatrolReportExport();
|
|
105
81
|
|
|
106
|
-
|
|
107
|
-
useNFCPatrolReportFilters(props.site);
|
|
108
|
-
|
|
109
|
-
onMounted(() => {
|
|
110
|
-
fetchOptions();
|
|
111
|
-
});
|
|
82
|
+
onMounted(fetchOptions);
|
|
112
83
|
|
|
113
84
|
function goBack() {
|
|
114
85
|
router.back();
|
|
@@ -120,72 +91,11 @@ function updateFilters(value: NFCPatrolReportFilters) {
|
|
|
120
91
|
filters.date = value.date;
|
|
121
92
|
}
|
|
122
93
|
|
|
123
|
-
async function exportReport(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (!report) return;
|
|
129
|
-
|
|
130
|
-
if (type === "pdf") {
|
|
131
|
-
const canvas = await html2canvas(report, {
|
|
132
|
-
scale: 2,
|
|
133
|
-
useCORS: true,
|
|
134
|
-
backgroundColor: "#fff",
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
const pdf = new jsPDF("p", "mm", "a4");
|
|
138
|
-
|
|
139
|
-
const pdfWidth = pdf.internal.pageSize.getWidth();
|
|
140
|
-
const pdfHeight = pdf.internal.pageSize.getHeight();
|
|
141
|
-
|
|
142
|
-
const imgWidth = pdfWidth;
|
|
143
|
-
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
|
144
|
-
|
|
145
|
-
const imgData = canvas.toDataURL("image/png");
|
|
146
|
-
|
|
147
|
-
let heightLeft = imgHeight;
|
|
148
|
-
let position = 0;
|
|
149
|
-
|
|
150
|
-
pdf.addImage(imgData, "PNG", 0, position, imgWidth, imgHeight);
|
|
151
|
-
|
|
152
|
-
heightLeft -= pdfHeight;
|
|
153
|
-
|
|
154
|
-
while (heightLeft > 0) {
|
|
155
|
-
position -= pdfHeight;
|
|
156
|
-
|
|
157
|
-
pdf.addPage();
|
|
158
|
-
pdf.addImage(imgData, "PNG", 0, position, imgWidth, imgHeight);
|
|
159
|
-
|
|
160
|
-
heightLeft -= pdfHeight;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
pdf.save(`${activeTab.value}-report.pdf`);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const table = report.querySelector("table");
|
|
168
|
-
|
|
169
|
-
if (!table) return;
|
|
170
|
-
|
|
171
|
-
const csv = Array.from(table.querySelectorAll("tr"))
|
|
172
|
-
.map((row) =>
|
|
173
|
-
Array.from(row.querySelectorAll("th,td"))
|
|
174
|
-
.map((cell) => `"${cell.textContent?.trim().replace(/"/g, '""') ?? ""}"`)
|
|
175
|
-
.join(","),
|
|
176
|
-
)
|
|
177
|
-
.join("\n");
|
|
178
|
-
|
|
179
|
-
const blob = new Blob([csv], {
|
|
180
|
-
type: "text/csv;charset=utf-8;",
|
|
94
|
+
async function exportReport() {
|
|
95
|
+
await exportActiveReport(activeTab.value, {
|
|
96
|
+
route: filters.route,
|
|
97
|
+
timeRange: filters.timeRange,
|
|
98
|
+
date: filters.date,
|
|
181
99
|
});
|
|
182
|
-
|
|
183
|
-
const link = document.createElement("a");
|
|
184
|
-
|
|
185
|
-
link.href = URL.createObjectURL(blob);
|
|
186
|
-
link.download = `${activeTab.value}-report.csv`;
|
|
187
|
-
link.click();
|
|
188
|
-
|
|
189
|
-
URL.revokeObjectURL(link.href);
|
|
190
100
|
}
|
|
191
101
|
</script>
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
<ReportFilters
|
|
4
4
|
:model-value="filters"
|
|
5
5
|
:options="options"
|
|
6
|
-
:show-time-filter="false"
|
|
7
6
|
@update:model-value="$emit('update:filters', $event)"
|
|
8
|
-
@export="
|
|
7
|
+
@export="$emit('export')"
|
|
9
8
|
/>
|
|
10
|
-
|
|
9
|
+
|
|
11
10
|
<v-divider :thickness="1" class="border-opacity-100" />
|
|
12
11
|
|
|
13
12
|
<template v-if="report">
|
|
@@ -16,7 +15,6 @@
|
|
|
16
15
|
<SummaryReportTable :title="report.title" :rows="report.rows" />
|
|
17
16
|
</template>
|
|
18
17
|
|
|
19
|
-
|
|
20
18
|
<ReportEmptyState
|
|
21
19
|
v-else-if="!loading && notFound"
|
|
22
20
|
title="No Daily Report"
|
|
@@ -24,7 +22,6 @@
|
|
|
24
22
|
/>
|
|
25
23
|
|
|
26
24
|
<ReportEmptyState v-else-if="!loading" />
|
|
27
|
-
</div>
|
|
28
25
|
</div>
|
|
29
26
|
</template>
|
|
30
27
|
|
|
@@ -41,23 +38,23 @@ import SummaryReportTable from "./SummaryReportTable.vue";
|
|
|
41
38
|
|
|
42
39
|
const props = defineProps<{
|
|
43
40
|
active: boolean;
|
|
44
|
-
site: string;
|
|
41
|
+
site: string; // ✅ thêm
|
|
45
42
|
filters: NFCPatrolReportFilters;
|
|
46
43
|
options: NFCPatrolReportFilterOptions;
|
|
47
44
|
}>();
|
|
48
45
|
|
|
49
46
|
defineEmits<{
|
|
50
47
|
"update:filters": [value: NFCPatrolReportFilters];
|
|
51
|
-
export: [
|
|
48
|
+
export: [];
|
|
52
49
|
}>();
|
|
53
50
|
|
|
54
51
|
const { report, loading, notFound, fetchReport } = useNFCPatrolDailyReport(
|
|
55
52
|
props.filters,
|
|
56
|
-
props.site,
|
|
53
|
+
props.site, // ✅
|
|
57
54
|
);
|
|
58
55
|
|
|
59
56
|
watch(
|
|
60
|
-
() => [props.filters.route, props.filters.date, props.active],
|
|
57
|
+
() => [props.filters.route, props.filters.timeRange, props.filters.date, props.active],
|
|
61
58
|
() => {
|
|
62
59
|
if (props.active) {
|
|
63
60
|
void fetchReport();
|
|
@@ -65,5 +62,4 @@ watch(
|
|
|
65
62
|
},
|
|
66
63
|
{ immediate: true },
|
|
67
64
|
);
|
|
68
|
-
|
|
69
65
|
</script>
|
|
@@ -3,30 +3,23 @@
|
|
|
3
3
|
<ReportFilters
|
|
4
4
|
:model-value="filters"
|
|
5
5
|
:options="options"
|
|
6
|
-
:show-time-filter="false"
|
|
7
|
-
:show-date-filter="false"
|
|
8
6
|
@update:model-value="$emit('update:filters', $event)"
|
|
9
|
-
@export="
|
|
7
|
+
@export="$emit('export')"
|
|
10
8
|
/>
|
|
11
|
-
|
|
9
|
+
|
|
12
10
|
<v-divider :thickness="1" class="border-opacity-100" />
|
|
13
11
|
|
|
14
12
|
<template v-if="report">
|
|
15
13
|
<ReportLocationHeader :location="report.location" />
|
|
16
14
|
<v-divider :thickness="1" class="border-opacity-100 mb-6" />
|
|
17
|
-
|
|
18
|
-
<MonthlyReportTable
|
|
19
|
-
:title="report.title"
|
|
20
|
-
:rows="report.rows"
|
|
21
|
-
/>
|
|
15
|
+
<SummaryReportTable :title="report.title" :rows="report.rows" />
|
|
22
16
|
</template>
|
|
23
17
|
|
|
24
18
|
<ReportEmptyState
|
|
25
19
|
v-else-if="!loading"
|
|
26
|
-
title="No Monthly Report"
|
|
27
|
-
message="
|
|
20
|
+
title="No Monthly Report Selected"
|
|
21
|
+
message="Please select a route, time range, and date to view the monthly report"
|
|
28
22
|
/>
|
|
29
|
-
</div>
|
|
30
23
|
</div>
|
|
31
24
|
</template>
|
|
32
25
|
|
|
@@ -35,13 +28,11 @@ import type {
|
|
|
35
28
|
NFCPatrolReportFilterOptions,
|
|
36
29
|
NFCPatrolReportFilters,
|
|
37
30
|
} from "../../../types/nfc-patrol-report";
|
|
38
|
-
|
|
39
31
|
import { useNFCPatrolMonthlyReport } from "../../../composables/useNFCPatrolMonthlyReport";
|
|
40
|
-
|
|
41
32
|
import ReportEmptyState from "./ReportEmptyState.vue";
|
|
42
33
|
import ReportFilters from "./ReportFilters.vue";
|
|
43
34
|
import ReportLocationHeader from "./ReportLocationHeader.vue";
|
|
44
|
-
import
|
|
35
|
+
import SummaryReportTable from "./SummaryReportTable.vue";
|
|
45
36
|
|
|
46
37
|
const props = defineProps<{
|
|
47
38
|
active: boolean;
|
|
@@ -52,14 +43,14 @@ const props = defineProps<{
|
|
|
52
43
|
|
|
53
44
|
defineEmits<{
|
|
54
45
|
"update:filters": [value: NFCPatrolReportFilters];
|
|
55
|
-
export: [
|
|
46
|
+
export: [];
|
|
56
47
|
}>();
|
|
57
48
|
|
|
58
|
-
const { report, loading,
|
|
49
|
+
const { report, loading, fetchReport } =
|
|
59
50
|
useNFCPatrolMonthlyReport(props.filters, props.site);
|
|
60
51
|
|
|
61
52
|
watch(
|
|
62
|
-
() => [props.filters.route, props.active],
|
|
53
|
+
() => [props.filters.route, props.filters.timeRange, props.filters.date, props.active],
|
|
63
54
|
() => {
|
|
64
55
|
if (props.active) {
|
|
65
56
|
void fetchReport();
|
|
@@ -67,5 +58,4 @@ watch(
|
|
|
67
58
|
},
|
|
68
59
|
{ immediate: true },
|
|
69
60
|
);
|
|
70
|
-
|
|
71
61
|
</script>
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
:model-value="filters"
|
|
5
5
|
:options="options"
|
|
6
6
|
@update:model-value="$emit('update:filters', $event)"
|
|
7
|
-
@export="
|
|
7
|
+
@export="$emit('export')"
|
|
8
8
|
/>
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
<v-divider :thickness="1" class="border-opacity-100" />
|
|
11
11
|
|
|
12
12
|
<template v-if="report">
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
<ReportEmptyState v-else-if="!loading" />
|
|
31
31
|
|
|
32
32
|
<RemarksDialog v-model="dialogRemarks" :remarks="selectedActivityRemarks" />
|
|
33
|
-
</div>
|
|
34
33
|
</div>
|
|
35
34
|
</template>
|
|
36
35
|
|
|
@@ -56,7 +55,7 @@ const props = defineProps<{
|
|
|
56
55
|
|
|
57
56
|
defineEmits<{
|
|
58
57
|
"update:filters": [value: NFCPatrolReportFilters];
|
|
59
|
-
export: [
|
|
58
|
+
export: [];
|
|
60
59
|
}>();
|
|
61
60
|
|
|
62
61
|
const {
|
|
@@ -78,5 +77,4 @@ watch(
|
|
|
78
77
|
},
|
|
79
78
|
{ immediate: true },
|
|
80
79
|
);
|
|
81
|
-
|
|
82
80
|
</script>
|
|
@@ -16,12 +16,7 @@
|
|
|
16
16
|
/>
|
|
17
17
|
</v-col>
|
|
18
18
|
|
|
19
|
-
<v-col
|
|
20
|
-
v-if="showTimeFilter"
|
|
21
|
-
cols="12"
|
|
22
|
-
md="2"
|
|
23
|
-
class="px-md-2 mb-2 mb-md-0"
|
|
24
|
-
>
|
|
19
|
+
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
25
20
|
<v-select
|
|
26
21
|
:model-value="modelValue.timeRange"
|
|
27
22
|
:items="options.timeRanges"
|
|
@@ -35,12 +30,7 @@
|
|
|
35
30
|
/>
|
|
36
31
|
</v-col>
|
|
37
32
|
|
|
38
|
-
<v-col
|
|
39
|
-
v-if="showDateFilter"
|
|
40
|
-
cols="12"
|
|
41
|
-
md="2"
|
|
42
|
-
class="px-md-2 mb-2 mb-md-0"
|
|
43
|
-
>
|
|
33
|
+
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
44
34
|
<v-text-field
|
|
45
35
|
:model-value="modelValue.date"
|
|
46
36
|
type="date"
|
|
@@ -56,31 +46,17 @@
|
|
|
56
46
|
<v-spacer />
|
|
57
47
|
|
|
58
48
|
<v-col cols="12" md="2" class="pl-md-2">
|
|
59
|
-
<v-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Export
|
|
71
|
-
</v-btn>
|
|
72
|
-
</template>
|
|
73
|
-
|
|
74
|
-
<v-list density="compact">
|
|
75
|
-
<v-list-item @click="$emit('export', 'pdf')">
|
|
76
|
-
<v-list-item-title>PDF</v-list-item-title>
|
|
77
|
-
</v-list-item>
|
|
78
|
-
|
|
79
|
-
<v-list-item @click="$emit('export', 'csv')">
|
|
80
|
-
<v-list-item-title>CSV</v-list-item-title>
|
|
81
|
-
</v-list-item>
|
|
82
|
-
</v-list>
|
|
83
|
-
</v-menu>
|
|
49
|
+
<v-btn
|
|
50
|
+
variant="outlined"
|
|
51
|
+
block
|
|
52
|
+
size="large"
|
|
53
|
+
rounded="lg"
|
|
54
|
+
class="text-none"
|
|
55
|
+
prepend-icon="mdi-export"
|
|
56
|
+
@click="$emit('export')"
|
|
57
|
+
>
|
|
58
|
+
Export
|
|
59
|
+
</v-btn>
|
|
84
60
|
</v-col>
|
|
85
61
|
</v-row>
|
|
86
62
|
</template>
|
|
@@ -91,22 +67,14 @@ import type {
|
|
|
91
67
|
NFCPatrolReportFilters,
|
|
92
68
|
} from "../../../types/nfc-patrol-report";
|
|
93
69
|
|
|
94
|
-
const props =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
showTimeFilter?: boolean;
|
|
99
|
-
showDateFilter?: boolean;
|
|
100
|
-
}>(),
|
|
101
|
-
{
|
|
102
|
-
showTimeFilter: true,
|
|
103
|
-
showDateFilter: true,
|
|
104
|
-
},
|
|
105
|
-
);;
|
|
70
|
+
const props = defineProps<{
|
|
71
|
+
modelValue: NFCPatrolReportFilters;
|
|
72
|
+
options: NFCPatrolReportFilterOptions;
|
|
73
|
+
}>();
|
|
106
74
|
|
|
107
75
|
const emit = defineEmits<{
|
|
108
76
|
"update:modelValue": [value: NFCPatrolReportFilters];
|
|
109
|
-
export: [
|
|
77
|
+
export: [];
|
|
110
78
|
}>();
|
|
111
79
|
|
|
112
80
|
function updateFilter(key: keyof NFCPatrolReportFilters, value: unknown) {
|
|
@@ -59,21 +59,6 @@
|
|
|
59
59
|
</v-col>
|
|
60
60
|
</v-row>
|
|
61
61
|
|
|
62
|
-
<v-dialog v-model="showPhotoPreview" max-width="900">
|
|
63
|
-
<v-card>
|
|
64
|
-
<v-card-actions class="pa-2 justify-end">
|
|
65
|
-
<v-btn
|
|
66
|
-
icon="mdi-close"
|
|
67
|
-
variant="text"
|
|
68
|
-
@click="showPhotoPreview = false"
|
|
69
|
-
/>
|
|
70
|
-
</v-card-actions>
|
|
71
|
-
<v-card-text class="pa-2 pt-0">
|
|
72
|
-
<v-img :src="selectedPhoto" contain max-height="80vh" />
|
|
73
|
-
</v-card-text>
|
|
74
|
-
</v-card>
|
|
75
|
-
</v-dialog>
|
|
76
|
-
|
|
77
62
|
<v-dialog v-model="photoOptionsDialog" max-width="420">
|
|
78
63
|
<v-card>
|
|
79
64
|
<v-card-title class="d-flex align-center pa-4">
|
|
@@ -211,8 +196,6 @@ const photos = ref<string[]>([...props.modelValue]);
|
|
|
211
196
|
const photosIds = ref<string[]>([...props.photoIds]);
|
|
212
197
|
const photoOptionsDialog = ref(false);
|
|
213
198
|
const showCameraDialog = ref(false);
|
|
214
|
-
const showPhotoPreview = ref(false);
|
|
215
|
-
const selectedPhoto = ref("");
|
|
216
199
|
const videoRef = ref<HTMLVideoElement | null>(null);
|
|
217
200
|
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
|
218
201
|
const fileInputRef = ref<HTMLInputElement | null>(null);
|
|
@@ -222,8 +205,10 @@ const uploading = ref(false);
|
|
|
222
205
|
const { addFile } = useFile();
|
|
223
206
|
|
|
224
207
|
function viewPhoto(photoData: string) {
|
|
225
|
-
|
|
226
|
-
|
|
208
|
+
const link = document.createElement("a");
|
|
209
|
+
link.href = photoData;
|
|
210
|
+
link.target = "_blank";
|
|
211
|
+
link.click();
|
|
227
212
|
}
|
|
228
213
|
|
|
229
214
|
watch(
|
|
@@ -548,7 +548,7 @@
|
|
|
548
548
|
variant="flat"
|
|
549
549
|
class="text-none sp-invite-submit-btn"
|
|
550
550
|
height="76"
|
|
551
|
-
:disabled="!
|
|
551
|
+
:disabled="!validServiceProvider"
|
|
552
552
|
:loading="disableServiceProvider"
|
|
553
553
|
@click="submitServiceProviderInvite()"
|
|
554
554
|
>
|
|
@@ -574,7 +574,6 @@ type ListTab = "active" | "pending" | "inactive";
|
|
|
574
574
|
|
|
575
575
|
type TableRow = {
|
|
576
576
|
_id: string;
|
|
577
|
-
userId: string;
|
|
578
577
|
serviceProviderOrgId: string;
|
|
579
578
|
companyName: string;
|
|
580
579
|
email: string;
|
|
@@ -734,10 +733,11 @@ const {
|
|
|
734
733
|
updateStatus: updateServiceProviderStatus,
|
|
735
734
|
invite,
|
|
736
735
|
} = useServiceProvider();
|
|
737
|
-
const {
|
|
736
|
+
const { getAll: getAllMembers } = useMember();
|
|
738
737
|
|
|
739
738
|
const { getVerifications, cancelUserInvitation, getVerificationByEmail } =
|
|
740
739
|
useVerification();
|
|
740
|
+
const { getOrgsByMembership } = useMember();
|
|
741
741
|
|
|
742
742
|
const existingAccount = ref<any>(null);
|
|
743
743
|
const checkingExistingAccount = ref(false);
|
|
@@ -757,10 +757,11 @@ const createInviteDialog = ref(false);
|
|
|
757
757
|
// const loadingInviteRoles = ref(false);
|
|
758
758
|
// const serviceProviderInviteRoles = ref<Array<Record<string, any>>>([]);
|
|
759
759
|
|
|
760
|
+
const { getByUserType } = useMember();
|
|
761
|
+
const { getOrgs } = useOrg();
|
|
760
762
|
|
|
761
763
|
async function checkExistingAccount(email: string) {
|
|
762
764
|
existingAccount.value = null;
|
|
763
|
-
messageServiceProvider.value = "";
|
|
764
765
|
|
|
765
766
|
if (!email.trim() || emailRule(email) !== true) return;
|
|
766
767
|
|
|
@@ -769,50 +770,30 @@ async function checkExistingAccount(email: string) {
|
|
|
769
770
|
try {
|
|
770
771
|
const user = await getUserByEmail(email);
|
|
771
772
|
|
|
772
|
-
if (!user?._id) return;
|
|
773
|
-
|
|
774
|
-
const members = await getAllByUserId(user._id);
|
|
775
|
-
|
|
776
|
-
const appMember = (members.items ?? []).find(
|
|
777
|
-
(member: any) => member.type === serviceProviderInvite.value.app
|
|
778
|
-
);
|
|
779
|
-
|
|
780
|
-
if (!appMember) {
|
|
781
|
-
existingAccount.value = null;
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
773
|
+
if (!user?._id) return;
|
|
784
774
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
) {
|
|
789
|
-
existingAccount.value = null;
|
|
790
|
-
messageServiceProvider.value =
|
|
791
|
-
"This user is already a member of this service provider.";
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
775
|
+
const orgs = await getOrgs({
|
|
776
|
+
user: user._id,
|
|
777
|
+
});
|
|
794
778
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
org: appMember.org,
|
|
799
|
-
orgName: appMember.orgName,
|
|
800
|
-
siteId: appMember.siteId,
|
|
801
|
-
siteName: appMember.siteName,
|
|
802
|
-
};
|
|
779
|
+
const matchedOrg = orgs.items?.find(
|
|
780
|
+
(org: any) => org.type === serviceProviderInvite.value.app
|
|
781
|
+
);
|
|
803
782
|
|
|
783
|
+
if (matchedOrg) {
|
|
784
|
+
existingAccount.value = {
|
|
785
|
+
_id: matchedOrg.value,
|
|
786
|
+
text: matchedOrg.text,
|
|
787
|
+
...matchedOrg,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
804
790
|
} catch (error) {
|
|
805
791
|
existingAccount.value = null;
|
|
806
792
|
} finally {
|
|
807
793
|
checkingExistingAccount.value = false;
|
|
808
794
|
}
|
|
809
795
|
}
|
|
810
|
-
|
|
811
|
-
return (
|
|
812
|
-
validServiceProvider.value &&
|
|
813
|
-
!messageServiceProvider.value
|
|
814
|
-
);
|
|
815
|
-
});
|
|
796
|
+
|
|
816
797
|
watch(
|
|
817
798
|
() => serviceProviderInvite.value.email,
|
|
818
799
|
async (value) => {
|
|
@@ -958,7 +939,6 @@ function formatEmailCell(value: unknown): string {
|
|
|
958
939
|
function mapProviderRows(raw: Record<string, any>[]): TableRow[] {
|
|
959
940
|
return raw.map((row) => ({
|
|
960
941
|
_id: String(row._id ?? ""),
|
|
961
|
-
userId: String(row.user ?? ""),
|
|
962
942
|
serviceProviderOrgId: String(row.serviceProviderOrgId ?? ""),
|
|
963
943
|
companyName: row.name ?? "-",
|
|
964
944
|
email: formatEmailCell(row.email),
|
|
@@ -981,7 +961,6 @@ function inviteTypeLabel(t: string) {
|
|
|
981
961
|
function mapInviteRows(raw: Record<string, any>[]): TableRow[] {
|
|
982
962
|
return raw.map((row) => ({
|
|
983
963
|
_id: String(row._id ?? ""),
|
|
984
|
-
userId: String(row.user ?? ""),
|
|
985
964
|
serviceProviderOrgId: String(row.metadata?.serviceProviderOrgId ?? ""),
|
|
986
965
|
companyName: row.metadata?.name || "-",
|
|
987
966
|
email: formatEmailCell(row.email),
|
|
@@ -1050,12 +1029,16 @@ async function openView(row: TableRow) {
|
|
|
1050
1029
|
|
|
1051
1030
|
loadingMembers.value = true;
|
|
1052
1031
|
try {
|
|
1053
|
-
const data = await
|
|
1054
|
-
|
|
1032
|
+
const data = await getAllMembers({
|
|
1033
|
+
page: 1,
|
|
1034
|
+
limit: 1000,
|
|
1035
|
+
org: row.serviceProviderOrgId,
|
|
1036
|
+
status: "active",
|
|
1037
|
+
});
|
|
1055
1038
|
serviceProviderMembers.value = (data.items ?? []).map((member: any) => ({
|
|
1056
|
-
_id: member._id,
|
|
1057
|
-
name: member.name || "-",
|
|
1058
|
-
role: member.role || "-",
|
|
1039
|
+
_id: String(member._id ?? ""),
|
|
1040
|
+
name: member.name || member.fullName || member.email || "-",
|
|
1041
|
+
role: member.roleName || member.role?.name || "-",
|
|
1059
1042
|
}));
|
|
1060
1043
|
} catch (error: any) {
|
|
1061
1044
|
showMessage(errorConverter(error), "error");
|