@7365admin1/layer-common 3.0.13 → 3.0.15
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 +12 -0
- package/components/BuildingManagement/buildings.vue +14 -1
- package/components/BuildingManagement/units.vue +14 -3
- package/components/DashboardMain.vue +78 -27
- package/components/ServiceProviderMain.vue +226 -152
- package/components/SiteSettings.vue +18 -1
- package/composables/useLocal.ts +12 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
<v-btn fab icon density="comfortable" @click="getBuildings()">
|
|
16
16
|
<v-icon>mdi-refresh</v-icon>
|
|
17
17
|
</v-btn>
|
|
18
|
+
<v-text-field v-model="searchInput" autocomplete="off" placeholder="Search" density="compact" hide-details dense clearable
|
|
19
|
+
class="mx-2" style="width: 200px;" />
|
|
18
20
|
</template>
|
|
19
21
|
|
|
20
22
|
<template #append>
|
|
@@ -219,9 +221,11 @@ const props = defineProps({
|
|
|
219
221
|
const site = (useRoute().params.site as string) ?? "";
|
|
220
222
|
const status = (useRoute().params.status as string) ?? "active";
|
|
221
223
|
|
|
222
|
-
const {
|
|
224
|
+
const { debounce } = useUtils();
|
|
223
225
|
const { getAll: _getBuildings, deleteById } = useBuilding();
|
|
224
226
|
|
|
227
|
+
const searchInput = ref("");
|
|
228
|
+
const headerSearch = ref("");
|
|
225
229
|
const page = ref(1);
|
|
226
230
|
const pages = ref(0);
|
|
227
231
|
const pageRange = ref("-- - -- of --");
|
|
@@ -252,6 +256,15 @@ const {
|
|
|
252
256
|
|
|
253
257
|
const loading = computed(() => getBuildingReqStatus.value === "pending");
|
|
254
258
|
|
|
259
|
+
const debounceSearch = debounce(() => {
|
|
260
|
+
headerSearch.value = searchInput.value?.trim() || "";
|
|
261
|
+
page.value = 1;
|
|
262
|
+
}, 500);
|
|
263
|
+
|
|
264
|
+
watch(searchInput, () => {
|
|
265
|
+
debounceSearch();
|
|
266
|
+
});
|
|
267
|
+
|
|
255
268
|
watchEffect(() => {
|
|
256
269
|
if (getBuildingReq.value) {
|
|
257
270
|
items.value = getBuildingReq.value.items;
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
<v-btn fab icon density="comfortable" @click="getBuildingUnit()">
|
|
16
16
|
<v-icon>mdi-refresh</v-icon>
|
|
17
17
|
</v-btn>
|
|
18
|
+
<v-text-field v-model="searchInput" placeholder="Search" density="compact" hide-details dense clearable
|
|
19
|
+
class="mx-2" style="width: 200px;" />
|
|
18
20
|
</template>
|
|
19
21
|
|
|
20
22
|
<template #append>
|
|
@@ -156,12 +158,12 @@ const status = (useRoute().params.status as string) ?? "active";
|
|
|
156
158
|
|
|
157
159
|
const { smAndUp } = useDisplay();
|
|
158
160
|
|
|
159
|
-
const { toOrdinal } = useUtils();
|
|
161
|
+
const { debounce, formatDateDDMMYYYYLocal, toOrdinal } = useUtils();
|
|
160
162
|
|
|
161
|
-
const { headerSearch } = useLocal();
|
|
162
163
|
const { getAll, deleteById } = useBuildingUnit();
|
|
163
|
-
const { formatDateDDMMYYYYLocal } = useUtils();
|
|
164
164
|
|
|
165
|
+
const searchInput = ref("");
|
|
166
|
+
const headerSearch = ref("");
|
|
165
167
|
const page = ref(1);
|
|
166
168
|
const pages = ref(0);
|
|
167
169
|
const pageRange = ref("-- - -- of --");
|
|
@@ -192,6 +194,15 @@ const {
|
|
|
192
194
|
|
|
193
195
|
const loading = computed(() => getBuildingReqStatus.value === "pending");
|
|
194
196
|
|
|
197
|
+
const debounceSearch = debounce(() => {
|
|
198
|
+
headerSearch.value = searchInput.value?.trim() || "";
|
|
199
|
+
page.value = 1;
|
|
200
|
+
}, 500);
|
|
201
|
+
|
|
202
|
+
watch(searchInput, () => {
|
|
203
|
+
debounceSearch();
|
|
204
|
+
});
|
|
205
|
+
|
|
195
206
|
watchEffect(() => {
|
|
196
207
|
if (getBuildingReq.value) {
|
|
197
208
|
items.value = getBuildingReq.value.items;
|
|
@@ -653,7 +653,15 @@
|
|
|
653
653
|
<button
|
|
654
654
|
class="figma-row-action"
|
|
655
655
|
type="button"
|
|
656
|
-
@click="
|
|
656
|
+
@click="
|
|
657
|
+
navigateTo(
|
|
658
|
+
item.type === 'facility-booking'
|
|
659
|
+
? `${dashboardPath('facility/bookings')}?booking=${
|
|
660
|
+
item.id
|
|
661
|
+
}`
|
|
662
|
+
: dashboardPath(`incident-reports/${item.id}`)
|
|
663
|
+
)
|
|
664
|
+
"
|
|
657
665
|
>
|
|
658
666
|
{{ item.actionLabel || "Review" }}
|
|
659
667
|
</button>
|
|
@@ -1146,6 +1154,8 @@ type DashboardListItem = {
|
|
|
1146
1154
|
tone?: string;
|
|
1147
1155
|
color?: string;
|
|
1148
1156
|
actionLabel?: string;
|
|
1157
|
+
type?: string;
|
|
1158
|
+
raw?: Record<string, any>;
|
|
1149
1159
|
};
|
|
1150
1160
|
|
|
1151
1161
|
type TaskItem = {
|
|
@@ -1236,21 +1246,36 @@ function canViewWidget(key: string) {
|
|
|
1236
1246
|
|
|
1237
1247
|
const widgetToModulePermissionMap: Record<string, string[]> = {
|
|
1238
1248
|
workOrders: ["work-orders:see-all-work-orders"],
|
|
1239
|
-
incidents: [
|
|
1249
|
+
incidents: [
|
|
1250
|
+
"incident-reports:see-incident-reports",
|
|
1251
|
+
"incident-reports:see-all-incident-reports",
|
|
1252
|
+
],
|
|
1240
1253
|
visitors: ["visitor:see-all-visitor"],
|
|
1241
1254
|
facilityBookings: ["facility-booking:see-all-facility-booking"],
|
|
1242
|
-
patrolCompliance: [
|
|
1255
|
+
patrolCompliance: [
|
|
1256
|
+
"virtual-patrol:see-all-virtual-patrol-logs",
|
|
1257
|
+
"virtual-patrol:see-all-virtual-patrol-route",
|
|
1258
|
+
],
|
|
1243
1259
|
staffStatus: ["attendance:see-all-attendance"],
|
|
1244
1260
|
attendance: ["attendance:see-all-attendance"],
|
|
1245
1261
|
feedbacks: ["feedbacks:see-all-feedbacks"],
|
|
1246
|
-
upcomingEvents: [
|
|
1262
|
+
upcomingEvents: [
|
|
1263
|
+
"bulletin-boards:see-all-bulletin-boards",
|
|
1264
|
+
"bulletin-board:see-all-bulletin-board",
|
|
1265
|
+
],
|
|
1247
1266
|
workOrderStatus: ["work-orders:see-all-work-orders"],
|
|
1248
|
-
activePatrol: [
|
|
1249
|
-
|
|
1267
|
+
activePatrol: [
|
|
1268
|
+
"virtual-patrol:see-all-virtual-patrol-logs",
|
|
1269
|
+
"virtual-patrol:see-all-virtual-patrol-route",
|
|
1270
|
+
],
|
|
1271
|
+
taskSchedule: ["cleaning-schedule-mgmt:see-all-schedules"],
|
|
1250
1272
|
};
|
|
1251
1273
|
|
|
1252
1274
|
const modulePerms = widgetToModulePermissionMap[key];
|
|
1253
|
-
if (
|
|
1275
|
+
if (
|
|
1276
|
+
modulePerms &&
|
|
1277
|
+
modulePerms.some((p) => userAppRole.value.permissions?.includes(p))
|
|
1278
|
+
) {
|
|
1254
1279
|
return true;
|
|
1255
1280
|
}
|
|
1256
1281
|
|
|
@@ -2034,23 +2059,26 @@ function computeFacilityMetric(): DashboardMetric {
|
|
|
2034
2059
|
).toLowerCase();
|
|
2035
2060
|
return val === selected.toLowerCase();
|
|
2036
2061
|
});
|
|
2062
|
+
const ongoing = items.filter((i: any) => {
|
|
2063
|
+
const s = String(i.status || "").toLowerCase();
|
|
2064
|
+
return (
|
|
2065
|
+
s.includes("ongoing") ||
|
|
2066
|
+
s.includes("approved") ||
|
|
2067
|
+
s.includes("in-progress")
|
|
2068
|
+
);
|
|
2069
|
+
}).length;
|
|
2070
|
+
const waitingApproval = items.filter((i: any) => {
|
|
2071
|
+
const s = String(i.status || "").toLowerCase();
|
|
2072
|
+
return (
|
|
2073
|
+
s.includes("pending") || s.includes("review") || s.includes("approval")
|
|
2074
|
+
);
|
|
2075
|
+
}).length;
|
|
2076
|
+
|
|
2037
2077
|
return {
|
|
2038
2078
|
...metric,
|
|
2039
|
-
count:
|
|
2040
|
-
ongoing
|
|
2041
|
-
|
|
2042
|
-
return (
|
|
2043
|
-
s.includes("ongoing") ||
|
|
2044
|
-
s.includes("approved") ||
|
|
2045
|
-
s.includes("in-progress")
|
|
2046
|
-
);
|
|
2047
|
-
}).length,
|
|
2048
|
-
waitingApproval: items.filter((i: any) => {
|
|
2049
|
-
const s = String(i.status || "").toLowerCase();
|
|
2050
|
-
return (
|
|
2051
|
-
s.includes("pending") || s.includes("review") || s.includes("approval")
|
|
2052
|
-
);
|
|
2053
|
-
}).length,
|
|
2079
|
+
count: ongoing + waitingApproval,
|
|
2080
|
+
ongoing,
|
|
2081
|
+
waitingApproval,
|
|
2054
2082
|
};
|
|
2055
2083
|
}
|
|
2056
2084
|
|
|
@@ -2413,7 +2441,9 @@ function getMetricMeta(metric: DashboardMetric) {
|
|
|
2413
2441
|
parts.push(`${formatNumber(toNumber(metric.ongoing))} ongoing`);
|
|
2414
2442
|
if (typeof metric.waitingApproval !== "undefined")
|
|
2415
2443
|
parts.push(
|
|
2416
|
-
`${formatNumber(toNumber(metric.waitingApproval))}
|
|
2444
|
+
`${formatNumber(toNumber(metric.waitingApproval))} ${
|
|
2445
|
+
isPropertyMode.value ? "for approval" : "waiting approval"
|
|
2446
|
+
}`
|
|
2417
2447
|
);
|
|
2418
2448
|
if (typeof metric.lowStock !== "undefined")
|
|
2419
2449
|
parts.push(`${formatNumber(toNumber(metric.lowStock))} low stock`);
|
|
@@ -2446,6 +2476,10 @@ function getFilteredVisitorMetric(): DashboardMetric {
|
|
|
2446
2476
|
};
|
|
2447
2477
|
}
|
|
2448
2478
|
|
|
2479
|
+
if (!items || !items.length) {
|
|
2480
|
+
return metric;
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2449
2483
|
const filtered = items.filter(
|
|
2450
2484
|
(item: any) => getVisitorFilterValue(item) === selected
|
|
2451
2485
|
);
|
|
@@ -2487,6 +2521,8 @@ function normalizeListItems(keys: string[]): DashboardListItem[] {
|
|
|
2487
2521
|
icon: item.icon || getListIcon(context),
|
|
2488
2522
|
tone: item.tone,
|
|
2489
2523
|
actionLabel: item.actionLabel,
|
|
2524
|
+
type: item.type,
|
|
2525
|
+
raw: item,
|
|
2490
2526
|
}));
|
|
2491
2527
|
}
|
|
2492
2528
|
|
|
@@ -2865,16 +2901,26 @@ function dashboardPath(path: string) {
|
|
|
2865
2901
|
return `/${props.orgId}/${props.site}/${path}`;
|
|
2866
2902
|
}
|
|
2867
2903
|
|
|
2904
|
+
function getSchedulePath() {
|
|
2905
|
+
const service = props.serviceType;
|
|
2906
|
+
if (service === "cleaning_services") return "cleaning-schedule";
|
|
2907
|
+
if (service === "landscaping_services") return "landscape-schedule";
|
|
2908
|
+
if (service === "pest_control_services") return "pest-schedule";
|
|
2909
|
+
if (service === "pool_maintenance_services") return "pool-schedule";
|
|
2910
|
+
if (service === "mechanical_electrical_services") return "technician-schedule";
|
|
2911
|
+
return "schedule-task";
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2868
2914
|
function goToDashboardRoute(routeKey: string) {
|
|
2869
2915
|
if (!props.orgId || !props.site) return;
|
|
2870
2916
|
const pathMap: Record<string, string> = {
|
|
2871
2917
|
attendance: "attendance",
|
|
2872
2918
|
feedbacks: "feedbacks",
|
|
2873
|
-
scheduleTask:
|
|
2919
|
+
scheduleTask: getSchedulePath(),
|
|
2874
2920
|
virtualPatrol: "virtual-patrol/logs",
|
|
2875
2921
|
manpower: "manpower-monitoring/attendance",
|
|
2876
2922
|
incidentReports: "incident-reports",
|
|
2877
|
-
"virtual-patrol": "virtual-patrol",
|
|
2923
|
+
"virtual-patrol": "virtual-patrol/logs",
|
|
2878
2924
|
"work-orders": "work-orders",
|
|
2879
2925
|
"event-mgmt": "event-mgmt",
|
|
2880
2926
|
calendar: "calendar",
|
|
@@ -2896,10 +2942,15 @@ function goToListItem(item: TaskItem | FeedbackItem, routeKey: string) {
|
|
|
2896
2942
|
}
|
|
2897
2943
|
const pathMap: Record<string, string> = {
|
|
2898
2944
|
feedbacks: "feedbacks",
|
|
2899
|
-
scheduleTask:
|
|
2945
|
+
scheduleTask: getSchedulePath(),
|
|
2900
2946
|
};
|
|
2901
2947
|
const path = pathMap[routeKey] || routeKey;
|
|
2902
|
-
|
|
2948
|
+
const scheduleId = item.raw?.schedule?._id || item.raw?.schedule;
|
|
2949
|
+
if (routeKey === "scheduleTask" && scheduleId) {
|
|
2950
|
+
router.push(`/${props.orgId}/${props.site}/${path}/${scheduleId}/${id}`);
|
|
2951
|
+
} else {
|
|
2952
|
+
router.push(`/${props.orgId}/${props.site}/${path}/${id}`);
|
|
2953
|
+
}
|
|
2903
2954
|
}
|
|
2904
2955
|
|
|
2905
2956
|
// ─── Widget Management ────────────────────────────────────────────────────────
|
|
@@ -319,7 +319,7 @@
|
|
|
319
319
|
v-model="serviceProvider.type"
|
|
320
320
|
item-title="title"
|
|
321
321
|
item-value="value"
|
|
322
|
-
:items="
|
|
322
|
+
:items="natureOfBusinessV3"
|
|
323
323
|
density="comfortable"
|
|
324
324
|
:rules="[requiredRule]"
|
|
325
325
|
></v-select>
|
|
@@ -402,7 +402,7 @@
|
|
|
402
402
|
variant="flat"
|
|
403
403
|
color="black"
|
|
404
404
|
class="text-none"
|
|
405
|
-
size="
|
|
405
|
+
size="small"
|
|
406
406
|
:disabled="!validServiceProvider"
|
|
407
407
|
:loading="disableServiceProvider"
|
|
408
408
|
@click="submitServiceProviderAdd"
|
|
@@ -415,39 +415,68 @@
|
|
|
415
415
|
</v-card>
|
|
416
416
|
</v-dialog>
|
|
417
417
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
<v-card-text class="sp-invite-body">
|
|
428
|
-
<v-form
|
|
429
|
-
v-model="validServiceProvider"
|
|
430
|
-
:disabled="disableServiceProvider"
|
|
431
|
-
>
|
|
432
|
-
<v-row no-gutters>
|
|
433
|
-
<v-col cols="12">
|
|
434
|
-
<v-row no-gutters>
|
|
435
|
-
<InputLabel class="text-capitalize" title="Email" required />
|
|
436
|
-
<v-col cols="12">
|
|
437
|
-
<v-text-field
|
|
438
|
-
v-model="serviceProviderInvite.email"
|
|
439
|
-
variant="outlined"
|
|
440
|
-
density="comfortable"
|
|
441
|
-
:rules="[requiredRule, emailRule]"
|
|
442
|
-
></v-text-field>
|
|
443
|
-
</v-col>
|
|
444
|
-
</v-row>
|
|
445
|
-
</v-col>
|
|
418
|
+
<v-dialog v-model="createInviteDialog" width="580" persistent>
|
|
419
|
+
<v-card width="100%" rounded="lg" class="sp-invite-card">
|
|
420
|
+
<v-toolbar flat color="grey-lighten-4" height="76">
|
|
421
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
422
|
+
<span class="font-weight-bold text-h5">
|
|
423
|
+
Invite Service Provider
|
|
424
|
+
</span>
|
|
425
|
+
</v-row>
|
|
426
|
+
</v-toolbar>
|
|
446
427
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
428
|
+
<v-card-text class="sp-invite-body">
|
|
429
|
+
<v-form
|
|
430
|
+
v-model="validServiceProvider"
|
|
431
|
+
:disabled="disableServiceProvider"
|
|
432
|
+
>
|
|
433
|
+
<v-row no-gutters>
|
|
434
|
+
<!-- Email -->
|
|
435
|
+
<v-col cols="12">
|
|
436
|
+
<v-row no-gutters>
|
|
437
|
+
<InputLabel class="text-capitalize" title="Email" required />
|
|
438
|
+
<v-col cols="12">
|
|
439
|
+
<v-text-field
|
|
440
|
+
v-model="serviceProviderInvite.email"
|
|
441
|
+
variant="outlined"
|
|
442
|
+
density="comfortable"
|
|
443
|
+
:rules="[requiredRule, emailRule]"
|
|
444
|
+
></v-text-field>
|
|
445
|
+
</v-col>
|
|
446
|
+
</v-row>
|
|
447
|
+
</v-col>
|
|
450
448
|
|
|
449
|
+
<!-- Existing account block -->
|
|
450
|
+
<v-col cols="12" v-if="existingAccount">
|
|
451
|
+
<v-divider class="mb-6" />
|
|
452
|
+
|
|
453
|
+
<div class="sp-existing-title">Existing Account Verified</div>
|
|
454
|
+
|
|
455
|
+
<div class="sp-existing-copy">
|
|
456
|
+
{{ serviceProviderInvite.email }} is already onboarded.
|
|
457
|
+
Sending this invite will add this site to the list of
|
|
458
|
+
locations this company manages.
|
|
459
|
+
</div>
|
|
460
|
+
|
|
461
|
+
<v-row no-gutters>
|
|
462
|
+
<InputLabel title="Organization" />
|
|
463
|
+
<v-col cols="12">
|
|
464
|
+
<v-text-field
|
|
465
|
+
:model-value="existingAccount.text"
|
|
466
|
+
readonly
|
|
467
|
+
variant="outlined"
|
|
468
|
+
density="comfortable"
|
|
469
|
+
/>
|
|
470
|
+
</v-col>
|
|
471
|
+
</v-row>
|
|
472
|
+
</v-col>
|
|
473
|
+
|
|
474
|
+
<v-col cols="12">
|
|
475
|
+
<v-divider class="mb-6" />
|
|
476
|
+
</v-col>
|
|
477
|
+
|
|
478
|
+
<!-- App & Site: only shown when no existing account is verified -->
|
|
479
|
+
<template v-if="!existingAccount">
|
|
451
480
|
<v-col cols="12">
|
|
452
481
|
<v-row no-gutters>
|
|
453
482
|
<InputLabel class="text-capitalize" title="App" required />
|
|
@@ -460,7 +489,6 @@
|
|
|
460
489
|
variant="outlined"
|
|
461
490
|
density="comfortable"
|
|
462
491
|
:rules="[requiredRule]"
|
|
463
|
-
@update:model-value="loadInviteRoles"
|
|
464
492
|
></v-select>
|
|
465
493
|
</v-col>
|
|
466
494
|
</v-row>
|
|
@@ -483,79 +511,64 @@
|
|
|
483
511
|
</v-col>
|
|
484
512
|
</v-row>
|
|
485
513
|
</v-col>
|
|
514
|
+
</template>
|
|
486
515
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
:loading="loadingInviteRoles"
|
|
499
|
-
:rules="[requiredRule]"
|
|
500
|
-
></v-select>
|
|
501
|
-
</v-col>
|
|
502
|
-
</v-row>
|
|
503
|
-
</v-col>
|
|
504
|
-
|
|
505
|
-
<v-col cols="12" class="my-2">
|
|
506
|
-
<v-row no-gutters>
|
|
507
|
-
<v-col cols="12" class="text-center">
|
|
508
|
-
<span
|
|
509
|
-
class="text-none text-subtitle-2 font-weight-medium text-error"
|
|
510
|
-
>
|
|
511
|
-
{{ messageServiceProvider }}
|
|
512
|
-
</span>
|
|
513
|
-
</v-col>
|
|
514
|
-
</v-row>
|
|
515
|
-
</v-col>
|
|
516
|
-
</v-row>
|
|
517
|
-
</v-form>
|
|
518
|
-
</v-card-text>
|
|
519
|
-
|
|
520
|
-
<v-toolbar flat height="76" class="sp-invite-footer">
|
|
521
|
-
<v-row no-gutters class="fill-height">
|
|
522
|
-
<v-col cols="6" class="fill-height">
|
|
523
|
-
<v-btn
|
|
524
|
-
block
|
|
525
|
-
variant="flat"
|
|
526
|
-
class="text-none sp-invite-footer-btn"
|
|
527
|
-
height="76"
|
|
528
|
-
@click="setServiceProvider({ mode: 'invite', dialog: false })"
|
|
529
|
-
>
|
|
530
|
-
Cancel
|
|
531
|
-
</v-btn>
|
|
532
|
-
</v-col>
|
|
533
|
-
|
|
534
|
-
<v-col cols="6" class="fill-height">
|
|
535
|
-
<v-btn
|
|
536
|
-
block
|
|
537
|
-
variant="flat"
|
|
538
|
-
class="text-none sp-invite-submit-btn"
|
|
539
|
-
height="76"
|
|
540
|
-
:disabled="!validServiceProvider"
|
|
541
|
-
:loading="disableServiceProvider"
|
|
542
|
-
@click="submitServiceProviderInvite()"
|
|
543
|
-
>
|
|
544
|
-
Submit
|
|
545
|
-
</v-btn>
|
|
516
|
+
<!-- Error message -->
|
|
517
|
+
<v-col cols="12" class="my-2">
|
|
518
|
+
<v-row no-gutters>
|
|
519
|
+
<v-col cols="12" class="text-center">
|
|
520
|
+
<span
|
|
521
|
+
class="text-none text-subtitle-2 font-weight-medium text-error"
|
|
522
|
+
>
|
|
523
|
+
{{ messageServiceProvider }}
|
|
524
|
+
</span>
|
|
525
|
+
</v-col>
|
|
526
|
+
</v-row>
|
|
546
527
|
</v-col>
|
|
547
528
|
</v-row>
|
|
548
|
-
</v-
|
|
549
|
-
</v-card>
|
|
550
|
-
|
|
529
|
+
</v-form>
|
|
530
|
+
</v-card-text>
|
|
531
|
+
|
|
532
|
+
<v-toolbar flat height="76" class="sp-invite-footer">
|
|
533
|
+
<v-row no-gutters class="fill-height">
|
|
534
|
+
<v-col cols="6" class="fill-height">
|
|
535
|
+
<v-btn
|
|
536
|
+
block
|
|
537
|
+
variant="flat"
|
|
538
|
+
class="text-none sp-invite-footer-btn"
|
|
539
|
+
height="76"
|
|
540
|
+
@click="setServiceProvider({ mode: 'invite', dialog: false })"
|
|
541
|
+
>
|
|
542
|
+
Back
|
|
543
|
+
</v-btn>
|
|
544
|
+
</v-col>
|
|
545
|
+
|
|
546
|
+
<v-col cols="6" class="fill-height">
|
|
547
|
+
<v-btn
|
|
548
|
+
block
|
|
549
|
+
variant="flat"
|
|
550
|
+
class="text-none sp-invite-submit-btn"
|
|
551
|
+
height="76"
|
|
552
|
+
:disabled="!validServiceProvider"
|
|
553
|
+
:loading="disableServiceProvider"
|
|
554
|
+
@click="submitServiceProviderInvite()"
|
|
555
|
+
>
|
|
556
|
+
Submit
|
|
557
|
+
</v-btn>
|
|
558
|
+
</v-col>
|
|
559
|
+
</v-row>
|
|
560
|
+
</v-toolbar>
|
|
561
|
+
</v-card>
|
|
562
|
+
</v-dialog>
|
|
551
563
|
|
|
552
564
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
553
565
|
</template>
|
|
554
566
|
|
|
555
567
|
<script lang="ts" setup>
|
|
556
568
|
import useServiceProvider from "../composables/useServiceProvider";
|
|
569
|
+
import useUser from "../composables/useUser";
|
|
557
570
|
import useVerification from "../composables/useVerification";
|
|
558
|
-
import useRole from "../composables/useRole";
|
|
571
|
+
// import useRole from "../composables/useRole";
|
|
559
572
|
import { errorConverter } from "../utils/data";
|
|
560
573
|
|
|
561
574
|
type ListTab = "active" | "pending" | "inactive";
|
|
@@ -674,8 +687,9 @@ const headers = [
|
|
|
674
687
|
},
|
|
675
688
|
];
|
|
676
689
|
|
|
677
|
-
const {
|
|
690
|
+
const { natureOfBusinessV3 } = useLocal();
|
|
678
691
|
const { requiredRule, emailRule, debounce } = useUtils();
|
|
692
|
+
const {getUserByEmail} = useUser()
|
|
679
693
|
|
|
680
694
|
const listTab = ref<ListTab>("active");
|
|
681
695
|
const page = ref(1);
|
|
@@ -705,13 +719,12 @@ const serviceProvider = ref({
|
|
|
705
719
|
orgName: "",
|
|
706
720
|
category: "",
|
|
707
721
|
});
|
|
708
|
-
const defaultInviteApp = "
|
|
722
|
+
const defaultInviteApp = "security_agency";
|
|
709
723
|
const serviceProviderInvite = ref({
|
|
710
724
|
email: "",
|
|
711
725
|
app: defaultInviteApp,
|
|
712
726
|
siteId: props.siteId,
|
|
713
727
|
siteName: props.siteName,
|
|
714
|
-
role: "",
|
|
715
728
|
});
|
|
716
729
|
|
|
717
730
|
const {
|
|
@@ -722,8 +735,14 @@ const {
|
|
|
722
735
|
} = useServiceProvider();
|
|
723
736
|
const { getAll: getAllMembers } = useMember();
|
|
724
737
|
|
|
725
|
-
const { getVerifications, cancelUserInvitation } = useVerification();
|
|
726
|
-
const {
|
|
738
|
+
const { getVerifications, cancelUserInvitation, getVerificationByEmail } = useVerification();
|
|
739
|
+
const {
|
|
740
|
+
getOrgsByMembership,
|
|
741
|
+
} = useMember();
|
|
742
|
+
|
|
743
|
+
const existingAccount = ref<any>(null);
|
|
744
|
+
const checkingExistingAccount = ref(false);
|
|
745
|
+
// const { getRoles } = useRole();
|
|
727
746
|
|
|
728
747
|
const { getSiteById } = useSite();
|
|
729
748
|
|
|
@@ -736,11 +755,53 @@ const searchText = ref("");
|
|
|
736
755
|
|
|
737
756
|
const createDialog = ref(false);
|
|
738
757
|
const createInviteDialog = ref(false);
|
|
739
|
-
const loadingInviteRoles = ref(false);
|
|
740
|
-
const serviceProviderInviteRoles = ref<Array<Record<string, any>>>([]);
|
|
758
|
+
// const loadingInviteRoles = ref(false);
|
|
759
|
+
// const serviceProviderInviteRoles = ref<Array<Record<string, any>>>([]);
|
|
760
|
+
|
|
761
|
+
const { getByUserType } = useMember();
|
|
762
|
+
|
|
763
|
+
async function checkExistingAccount(email: string) {
|
|
764
|
+
// existingAccount.value = null;
|
|
765
|
+
|
|
766
|
+
if (!email.trim() || emailRule(email) !== true) return;
|
|
767
|
+
|
|
768
|
+
checkingExistingAccount.value = true;
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
const user = await getUserByEmail(email);
|
|
772
|
+
|
|
773
|
+
if (!user?._id) return;
|
|
774
|
+
|
|
775
|
+
const member = await getByUserType(user._id, "organization");
|
|
776
|
+
console.log("member raw:", member);
|
|
777
|
+
console.log("onboardingCompleted:", member?.onboardingCompleted);
|
|
778
|
+
|
|
779
|
+
if (member?.onboardingCompleted) {
|
|
780
|
+
existingAccount.value = {
|
|
781
|
+
_id: member.org,
|
|
782
|
+
text: member.orgName,
|
|
783
|
+
...member,
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
} catch (error) {
|
|
787
|
+
console.error(error);
|
|
788
|
+
existingAccount.value = null;
|
|
789
|
+
} finally {
|
|
790
|
+
checkingExistingAccount.value = false;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
watch(
|
|
796
|
+
() => serviceProviderInvite.value.email,
|
|
797
|
+
async (value) => {
|
|
798
|
+
console.log("typing:", value);
|
|
799
|
+
await checkExistingAccount(value);
|
|
800
|
+
}
|
|
801
|
+
);
|
|
741
802
|
|
|
742
803
|
const serviceProviderInviteApps = computed(() =>
|
|
743
|
-
|
|
804
|
+
natureOfBusinessV3.map((item) => ({
|
|
744
805
|
...item,
|
|
745
806
|
title:
|
|
746
807
|
item.value === "cleaning_services"
|
|
@@ -801,7 +862,7 @@ function rowActionLabel(row: TableRow) {
|
|
|
801
862
|
|
|
802
863
|
function typeDisplay(type: string, nature?: string) {
|
|
803
864
|
if (nature && String(nature).trim()) return String(nature);
|
|
804
|
-
const list =
|
|
865
|
+
const list = natureOfBusinessV3.value as Array<{ title?: string; value?: string }>;
|
|
805
866
|
const hit = list?.find((x) => x.value === type);
|
|
806
867
|
return hit?.title ?? type ?? "-";
|
|
807
868
|
}
|
|
@@ -822,25 +883,25 @@ function statusChipColor(statusLabel: string) {
|
|
|
822
883
|
return "primary";
|
|
823
884
|
}
|
|
824
885
|
|
|
825
|
-
async function loadInviteRoles() {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
}
|
|
886
|
+
// async function loadInviteRoles() {
|
|
887
|
+
// loadingInviteRoles.value = true;
|
|
888
|
+
// serviceProviderInvite.value.role = "";
|
|
889
|
+
// try {
|
|
890
|
+
// const response = await getRoles({
|
|
891
|
+
// org: props.orgId,
|
|
892
|
+
// type: serviceProviderInvite.value.app,
|
|
893
|
+
// limit: 50,
|
|
894
|
+
// });
|
|
895
|
+
// serviceProviderInviteRoles.value = response?.items ?? [];
|
|
896
|
+
// serviceProviderInvite.value.role =
|
|
897
|
+
// serviceProviderInviteRoles.value[0]?._id ?? "";
|
|
898
|
+
// } catch (error: any) {
|
|
899
|
+
// serviceProviderInviteRoles.value = [];
|
|
900
|
+
// showMessage(errorConverter(error), "error");
|
|
901
|
+
// } finally {
|
|
902
|
+
// loadingInviteRoles.value = false;
|
|
903
|
+
// }
|
|
904
|
+
// }
|
|
844
905
|
|
|
845
906
|
function setInviteSiteName(siteId: string) {
|
|
846
907
|
const selectedSite = serviceProviderInviteSites.value.find(
|
|
@@ -1067,37 +1128,38 @@ function setServiceProvider({
|
|
|
1067
1128
|
createDialog.value = dialog;
|
|
1068
1129
|
}
|
|
1069
1130
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1131
|
+
if (dialog) {
|
|
1132
|
+
serviceProvider.value = {
|
|
1133
|
+
name: "",
|
|
1134
|
+
type: "",
|
|
1135
|
+
email: "",
|
|
1136
|
+
description: "",
|
|
1137
|
+
orgId: props.orgId,
|
|
1138
|
+
siteId: props.siteId,
|
|
1139
|
+
siteName: site.value?.name ?? "",
|
|
1140
|
+
orgName: "Org Name",
|
|
1141
|
+
category: mode === "add" ? "internal" : "external",
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
if (mode === "invite") {
|
|
1145
|
+
existingAccount.value = null;
|
|
1146
|
+
|
|
1147
|
+
serviceProviderInvite.value = {
|
|
1074
1148
|
email: "",
|
|
1075
|
-
|
|
1076
|
-
orgId: props.orgId,
|
|
1149
|
+
app: defaultInviteApp,
|
|
1077
1150
|
siteId: props.siteId,
|
|
1078
|
-
siteName: site.value?.name
|
|
1079
|
-
orgName: "Org Name",
|
|
1080
|
-
category: mode === "add" ? "internal" : "external",
|
|
1151
|
+
siteName: site.value?.name || props.siteName,
|
|
1081
1152
|
};
|
|
1082
|
-
|
|
1083
|
-
if (mode === "invite") {
|
|
1084
|
-
serviceProviderInvite.value = {
|
|
1085
|
-
email: "",
|
|
1086
|
-
app: defaultInviteApp,
|
|
1087
|
-
siteId: props.siteId,
|
|
1088
|
-
siteName: site.value?.name || props.siteName,
|
|
1089
|
-
role: "",
|
|
1090
|
-
};
|
|
1091
|
-
createMoreServiceProvider.value = false;
|
|
1092
|
-
void loadInviteRoles();
|
|
1093
|
-
}
|
|
1094
|
-
return;
|
|
1095
1153
|
}
|
|
1096
1154
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1155
|
+
createMoreServiceProvider.value = false;
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
serviceProvider.value.description = data.description ?? "";
|
|
1160
|
+
serviceProvider.value.email = data.email ?? "";
|
|
1161
|
+
serviceProvider.value.name = data.name ?? "";
|
|
1162
|
+
serviceProvider.value.type = data.type ?? "";
|
|
1101
1163
|
}
|
|
1102
1164
|
|
|
1103
1165
|
async function submitServiceProviderAdd() {
|
|
@@ -1130,7 +1192,6 @@ async function submitServiceProviderInvite() {
|
|
|
1130
1192
|
email: serviceProviderInvite.value.email.trim(),
|
|
1131
1193
|
name: "",
|
|
1132
1194
|
app: serviceProviderInvite.value.app,
|
|
1133
|
-
role: serviceProviderInvite.value.role,
|
|
1134
1195
|
orgId: props.orgId,
|
|
1135
1196
|
siteId: serviceProviderInvite.value.siteId,
|
|
1136
1197
|
siteName:
|
|
@@ -1318,4 +1379,17 @@ async function submitServiceProviderInvite() {
|
|
|
1318
1379
|
font-size: 1.5rem;
|
|
1319
1380
|
font-weight: 700;
|
|
1320
1381
|
}
|
|
1382
|
+
.sp-existing-title {
|
|
1383
|
+
font-size: 1.125rem;
|
|
1384
|
+
font-weight: 700;
|
|
1385
|
+
color: #101828;
|
|
1386
|
+
margin-bottom: 16px;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
.sp-existing-copy {
|
|
1390
|
+
font-size: 1rem;
|
|
1391
|
+
color: #344054;
|
|
1392
|
+
line-height: 1.5;
|
|
1393
|
+
margin-bottom: 24px;
|
|
1394
|
+
}
|
|
1321
1395
|
</style>
|
|
@@ -206,7 +206,7 @@ const { canManageSiteInformation,
|
|
|
206
206
|
const { getSiteById, updateSitebyId, updateSiteInformation } =
|
|
207
207
|
useSiteSettings();
|
|
208
208
|
const { requiredRule } = useUtils();
|
|
209
|
-
const { getFileUrl } = useFile();
|
|
209
|
+
const { getFileUrl, getFileById } = useFile();
|
|
210
210
|
|
|
211
211
|
const blocks = ref(0);
|
|
212
212
|
const guardPosts = ref(0);
|
|
@@ -229,6 +229,23 @@ const siteDocuments = ref<string[]>([]);
|
|
|
229
229
|
const siteDocumentsNames = ref<Record<string, string>>({});
|
|
230
230
|
const savingSiteInfo = ref<boolean>(false);
|
|
231
231
|
|
|
232
|
+
watch(
|
|
233
|
+
siteDocuments,
|
|
234
|
+
async (ids) => {
|
|
235
|
+
for (const id of ids) {
|
|
236
|
+
if (!siteDocumentsNames.value[id]) {
|
|
237
|
+
try {
|
|
238
|
+
const file = (await getFileById(id)) as any;
|
|
239
|
+
siteDocumentsNames.value[id] = file?.data?.name ?? file?.name ?? "";
|
|
240
|
+
} catch {
|
|
241
|
+
siteDocumentsNames.value[id] = "";
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
{ deep: true }
|
|
247
|
+
);
|
|
248
|
+
|
|
232
249
|
const coverPhotoUrl = computed(() =>
|
|
233
250
|
coverPhoto.value.length > 0 ? getFileUrl(coverPhoto.value[0]) : ""
|
|
234
251
|
);
|
package/composables/useLocal.ts
CHANGED
|
@@ -65,6 +65,17 @@ export default function useLocal() {
|
|
|
65
65
|
{ title: "Pest Control Services", value: "pest_control_services" },
|
|
66
66
|
{ title: "Pool Maintenance Services", value: "pool_maintenance_services" },
|
|
67
67
|
];
|
|
68
|
+
const natureOfBusinessV3 = [
|
|
69
|
+
{ title: "Security", value: "security_agency" },
|
|
70
|
+
{ title: "Cleaning", value: "cleaning_services" },
|
|
71
|
+
{
|
|
72
|
+
title: "Mechanical & Electrical ",
|
|
73
|
+
value: "mechanical_electrical_services",
|
|
74
|
+
},
|
|
75
|
+
{ title: "Landscaping", value: "landscaping_services" },
|
|
76
|
+
{ title: "Pest Control", value: "pest_control_services" },
|
|
77
|
+
{ title: "Pool Maintenance", value: "pool_maintenance_services" },
|
|
78
|
+
];
|
|
68
79
|
|
|
69
80
|
|
|
70
81
|
const landingPage = useCookie("landing-page", cookieConfig);
|
|
@@ -143,6 +154,7 @@ export default function useLocal() {
|
|
|
143
154
|
headerSearch,
|
|
144
155
|
natureOfBusiness,
|
|
145
156
|
natureOfBusinessV2,
|
|
157
|
+
natureOfBusinessV3,
|
|
146
158
|
landingPage,
|
|
147
159
|
subjects,
|
|
148
160
|
};
|