@7365admin1/layer-common 1.11.37 → 1.11.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/CHANGELOG.md +12 -0
- package/components/CameraForm.vue +58 -13
- package/components/CameraMain.vue +19 -3
- package/components/ClientMain.vue +91 -70
- package/components/InvitationClientForm.vue +37 -19
- package/components/InvitationForm.vue +35 -5
- package/components/Layout/NavigationDrawer.vue +49 -26
- package/components/RolePermissionFormCreate.vue +106 -46
- package/components/ScheduleTaskForm.vue +168 -18
- package/components/ScheduleTaskMain.vue +115 -4
- package/components/VehicleForm.vue +6 -1
- package/components/VehicleManagement.vue +27 -17
- package/components/VisitorManagement.vue +4 -4
- package/composables/useMember.ts +16 -0
- package/composables/useSiteSettings.ts +1 -0
- package/composables/useVehicle.ts +14 -1
- package/composables/useWorkOrder.ts +13 -7
- package/package.json +1 -1
- package/types/camera.d.ts +1 -0
- package/types/org.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,12 +11,21 @@
|
|
|
11
11
|
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-5">
|
|
12
12
|
<v-form ref="formRef" v-model="validForm" :disabled="disable">
|
|
13
13
|
<v-row no-gutters class="pt-4">
|
|
14
|
+
<v-col v-if="isCCTV" cols="12">
|
|
15
|
+
<InputLabel class="text-capitalize" title="Camera Name" required />
|
|
16
|
+
<v-text-field
|
|
17
|
+
v-model.trim="camera.name"
|
|
18
|
+
density="comfortable"
|
|
19
|
+
:rules="[requiredRule]"
|
|
20
|
+
placeholder="Enter CCTV camera name"
|
|
21
|
+
/>
|
|
22
|
+
</v-col>
|
|
14
23
|
<v-col cols="12">
|
|
15
24
|
<InputLabel class="text-capitalize" title="URL" required />
|
|
16
25
|
<v-text-field
|
|
17
26
|
v-model.trim="camera.host"
|
|
18
27
|
density="comfortable"
|
|
19
|
-
:rules="[requiredRule,
|
|
28
|
+
:rules="[requiredRule, hostRule]"
|
|
20
29
|
placeholder="Enter camera URL"
|
|
21
30
|
/>
|
|
22
31
|
</v-col>
|
|
@@ -32,7 +41,7 @@
|
|
|
32
41
|
</v-col>
|
|
33
42
|
|
|
34
43
|
<v-col v-if="isANPR" cols="12">
|
|
35
|
-
<InputLabel class="text-capitalize" title="
|
|
44
|
+
<InputLabel class="text-capitalize" title="Type" required />
|
|
36
45
|
<v-select
|
|
37
46
|
v-model="camera.direction"
|
|
38
47
|
:items="anprDirectionOptions"
|
|
@@ -50,7 +59,7 @@
|
|
|
50
59
|
/>
|
|
51
60
|
</v-col>
|
|
52
61
|
|
|
53
|
-
<v-col cols="12">
|
|
62
|
+
<v-col v-if="isANPR" cols="12">
|
|
54
63
|
<InputLabel class="text-capitalize" title="User" required />
|
|
55
64
|
<v-text-field
|
|
56
65
|
v-model.trim="camera.username"
|
|
@@ -60,7 +69,7 @@
|
|
|
60
69
|
/>
|
|
61
70
|
</v-col>
|
|
62
71
|
|
|
63
|
-
<v-col cols="12">
|
|
72
|
+
<v-col v-if="isANPR" cols="12">
|
|
64
73
|
<InputLabel class="text-capitalize" title="Password" required />
|
|
65
74
|
<v-text-field
|
|
66
75
|
v-model.trim="camera.password"
|
|
@@ -70,6 +79,20 @@
|
|
|
70
79
|
placeholder="Enter password"
|
|
71
80
|
/>
|
|
72
81
|
</v-col>
|
|
82
|
+
|
|
83
|
+
<v-col v-if="isANPR" cols="12">
|
|
84
|
+
<InputLabel class="text-capitalize" title="Status" />
|
|
85
|
+
<v-switch
|
|
86
|
+
v-model="camera.status"
|
|
87
|
+
hide-details
|
|
88
|
+
inset
|
|
89
|
+
density="comfortable"
|
|
90
|
+
true-value="active"
|
|
91
|
+
false-value="inactive"
|
|
92
|
+
:color="camera.status === 'active' ? 'success' : 'grey'"
|
|
93
|
+
:label="camera.status === 'active' ? 'Active' : 'Inactive'"
|
|
94
|
+
></v-switch>
|
|
95
|
+
</v-col>
|
|
73
96
|
</v-row>
|
|
74
97
|
</v-form>
|
|
75
98
|
</v-card-text>
|
|
@@ -107,7 +130,7 @@
|
|
|
107
130
|
</template>
|
|
108
131
|
|
|
109
132
|
<script setup lang="ts">
|
|
110
|
-
import useSiteSettings from
|
|
133
|
+
import useSiteSettings from "../composables/useSiteSettings";
|
|
111
134
|
|
|
112
135
|
const prop = defineProps({
|
|
113
136
|
title: {
|
|
@@ -139,9 +162,10 @@ const prop = defineProps({
|
|
|
139
162
|
password: "",
|
|
140
163
|
type: "ip",
|
|
141
164
|
category: "standard",
|
|
142
|
-
direction: "
|
|
165
|
+
direction: "both",
|
|
143
166
|
guardPost: null,
|
|
144
167
|
status: "active",
|
|
168
|
+
name: "",
|
|
145
169
|
}),
|
|
146
170
|
},
|
|
147
171
|
});
|
|
@@ -149,6 +173,23 @@ const prop = defineProps({
|
|
|
149
173
|
const { requiredRule, isValidBaseURL } = useUtils();
|
|
150
174
|
const emit = defineEmits(["cancel", "select", "success", "error"]);
|
|
151
175
|
|
|
176
|
+
const hostRule = computed(() => {
|
|
177
|
+
return (v: string | any) => {
|
|
178
|
+
if (!v) return true;
|
|
179
|
+
|
|
180
|
+
const isAnpr = prop.type === 'anpr';
|
|
181
|
+
|
|
182
|
+
const anprRegex = /^http:\/\/[a-z0-9.-]+\.[a-z]{2,}(:\d+)?$/i;
|
|
183
|
+
const defaultRegex = /^https:\/\/[a-z0-9.-]+\.[a-z]{2,}\/[a-z0-9._-]+$/i;
|
|
184
|
+
|
|
185
|
+
if (isAnpr) {
|
|
186
|
+
return anprRegex.test(v) || 'Format: http://domain:port';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return defaultRegex.test(v) || 'Format: https://domain/path';
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
|
|
152
193
|
const camera = ref<TSiteCamera>({
|
|
153
194
|
site: "",
|
|
154
195
|
host: "",
|
|
@@ -156,7 +197,8 @@ const camera = ref<TSiteCamera>({
|
|
|
156
197
|
password: "",
|
|
157
198
|
type: "ip",
|
|
158
199
|
category: "standard",
|
|
159
|
-
direction: "
|
|
200
|
+
direction: "both",
|
|
201
|
+
name: "",
|
|
160
202
|
guardPost: null,
|
|
161
203
|
status: "active",
|
|
162
204
|
});
|
|
@@ -166,11 +208,11 @@ camera.value.site = prop.site;
|
|
|
166
208
|
if (prop.mode === "edit") {
|
|
167
209
|
camera.value = JSON.parse(JSON.stringify(prop.camera));
|
|
168
210
|
}
|
|
169
|
-
|
|
211
|
+
console.log("prop.camera", prop?.camera);
|
|
170
212
|
camera.value.site = prop.site;
|
|
171
213
|
camera.value.type = prop.type;
|
|
172
|
-
camera.value.category = prop.type
|
|
173
|
-
camera.value.direction = prop.type
|
|
214
|
+
camera.value.category = prop.camera?.category && prop.title == "Edit Camera" ? prop.camera?.category : prop.type == "anpr" ? "resident" : "standard";
|
|
215
|
+
camera.value.direction = prop.camera?.direction && prop.title == "Edit Camera" ? prop.camera?.direction : prop.type == "anpr" ? "both" : "none";
|
|
174
216
|
|
|
175
217
|
const validForm = ref(false);
|
|
176
218
|
const formRef = ref<HTMLFormElement | null>(null);
|
|
@@ -178,6 +220,7 @@ const disable = ref(false);
|
|
|
178
220
|
const message = ref("");
|
|
179
221
|
|
|
180
222
|
const isANPR = computed(() => prop.type === "anpr");
|
|
223
|
+
const isCCTV = computed(() => prop.type === "ip");
|
|
181
224
|
|
|
182
225
|
const anprCategoryOptions = computed(() => [
|
|
183
226
|
{ title: "Resident", value: "resident" },
|
|
@@ -187,8 +230,7 @@ const anprCategoryOptions = computed(() => [
|
|
|
187
230
|
const anprDirectionOptions = computed(() => [
|
|
188
231
|
{ title: "Entry", value: "entry" },
|
|
189
232
|
{ title: "Exit", value: "exit" },
|
|
190
|
-
{ title: "Both", value: "both" },
|
|
191
|
-
{ title: "None", value: "none" },
|
|
233
|
+
{ title: "Both Entry and Exit", value: "both" },
|
|
192
234
|
]);
|
|
193
235
|
|
|
194
236
|
function back() {
|
|
@@ -210,7 +252,8 @@ const hasChanges = computed(() => {
|
|
|
210
252
|
camera.value.password !== prop.camera.password ||
|
|
211
253
|
camera.value.category !== prop.camera.category ||
|
|
212
254
|
camera.value.guardPost !== prop.camera.guardPost ||
|
|
213
|
-
camera.value.direction !== prop.camera.direction
|
|
255
|
+
camera.value.direction !== prop.camera.direction ||
|
|
256
|
+
camera.value.name !== prop.camera.name
|
|
214
257
|
);
|
|
215
258
|
}
|
|
216
259
|
|
|
@@ -237,6 +280,8 @@ async function submit() {
|
|
|
237
280
|
category: camera.value.category,
|
|
238
281
|
guardPost: camera.value.guardPost,
|
|
239
282
|
direction: camera.value.direction,
|
|
283
|
+
status: camera.value.status,
|
|
284
|
+
name: camera.value.name,
|
|
240
285
|
});
|
|
241
286
|
}
|
|
242
287
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
<v-card-text class="pa-0 bg-red">
|
|
26
26
|
<v-data-table
|
|
27
|
-
:headers="
|
|
27
|
+
:headers="computedHeaders"
|
|
28
28
|
:items="items"
|
|
29
29
|
fixed-header
|
|
30
30
|
hide-default-footer
|
|
@@ -92,6 +92,12 @@
|
|
|
92
92
|
class="pa-5 my-5 px-7"
|
|
93
93
|
>
|
|
94
94
|
<v-row no-gutters>
|
|
95
|
+
<v-col v-if="camera.type === 'ip'" cols="12" class="mb-2">
|
|
96
|
+
Camera Name:
|
|
97
|
+
<span class="font-weight-bold">
|
|
98
|
+
{{ camera.name }}
|
|
99
|
+
</span>
|
|
100
|
+
</v-col>
|
|
95
101
|
<v-col cols="12" class="mb-2">
|
|
96
102
|
Host:
|
|
97
103
|
<span class="font-weight-bold">
|
|
@@ -99,14 +105,14 @@
|
|
|
99
105
|
</span>
|
|
100
106
|
</v-col>
|
|
101
107
|
|
|
102
|
-
<v-col cols="12" class="mb-2">
|
|
108
|
+
<v-col v-if="camera.type === 'anpr'" cols="12" class="mb-2">
|
|
103
109
|
Username:
|
|
104
110
|
<span class="font-weight-bold">
|
|
105
111
|
{{ camera.username }}
|
|
106
112
|
</span>
|
|
107
113
|
</v-col>
|
|
108
114
|
|
|
109
|
-
<v-col cols="12" class="mb-2">
|
|
115
|
+
<v-col v-if="camera.type === 'anpr'" cols="12" class="mb-2">
|
|
110
116
|
Password:
|
|
111
117
|
<span class="font-weight-bold">
|
|
112
118
|
{{ camera.password }}
|
|
@@ -275,6 +281,15 @@ const prop = defineProps({
|
|
|
275
281
|
},
|
|
276
282
|
});
|
|
277
283
|
|
|
284
|
+
const computedHeaders = computed(() => {
|
|
285
|
+
if (prop.type === "ip") {
|
|
286
|
+
const base = [...prop.headers];
|
|
287
|
+
base.splice(0, 0, { text: "Name", value: "name" });
|
|
288
|
+
return base;
|
|
289
|
+
}
|
|
290
|
+
return prop.headers;
|
|
291
|
+
});
|
|
292
|
+
|
|
278
293
|
const items = ref<Array<TCamera>>([]);
|
|
279
294
|
const page = ref(1);
|
|
280
295
|
const pages = ref(0);
|
|
@@ -341,6 +356,7 @@ const camera = ref<TSiteCamera>({
|
|
|
341
356
|
});
|
|
342
357
|
|
|
343
358
|
function handleRowClick(_: any, data: any) {
|
|
359
|
+
console.log("row clicked", data.item);
|
|
344
360
|
dialogPreview.value = true;
|
|
345
361
|
camera.value = JSON.parse(JSON.stringify(data.item));
|
|
346
362
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
3
|
<v-col cols="12">
|
|
4
|
-
|
|
4
|
+
<TableMain
|
|
5
|
+
|
|
5
6
|
:headers="currentHeaders"
|
|
6
7
|
:items="items"
|
|
7
8
|
:loading="loading"
|
|
8
9
|
:page="page"
|
|
9
10
|
:pages="pages"
|
|
10
|
-
:
|
|
11
|
-
:items-per-page="
|
|
11
|
+
:page-range="pageRange"
|
|
12
|
+
:items-per-page="limit"
|
|
12
13
|
:extension-height="120"
|
|
13
14
|
:offset="300"
|
|
14
15
|
show-header
|
|
@@ -25,30 +26,16 @@
|
|
|
25
26
|
</v-tabs>
|
|
26
27
|
|
|
27
28
|
<div class="px-3 py-2 w-100">
|
|
28
|
-
<v-text-field
|
|
29
|
-
|
|
30
|
-
density="compact"
|
|
31
|
-
placeholder="Search"
|
|
32
|
-
clearable
|
|
33
|
-
max-width="300"
|
|
34
|
-
append-inner-icon="mdi-magnify"
|
|
35
|
-
hide-details
|
|
36
|
-
@update:model-value="handleSearch"
|
|
37
|
-
/>
|
|
29
|
+
<v-text-field v-model="search" density="compact" placeholder="Search" clearable max-width="300"
|
|
30
|
+
append-inner-icon="mdi-magnify" hide-details @update:model-value="handleSearch" />
|
|
38
31
|
</div>
|
|
39
32
|
</v-row>
|
|
40
33
|
</template>
|
|
41
34
|
|
|
42
35
|
<!-- ACTIONS -->
|
|
43
36
|
<template #actions>
|
|
44
|
-
<v-btn
|
|
45
|
-
|
|
46
|
-
class="text-none"
|
|
47
|
-
rounded="pill"
|
|
48
|
-
variant="tonal"
|
|
49
|
-
size="large"
|
|
50
|
-
@click="openInviteDialog"
|
|
51
|
-
>
|
|
37
|
+
<v-btn v-if="tab !== 'suspended'" class="text-none" rounded="pill" variant="tonal" size="large"
|
|
38
|
+
@click="openInviteDialog">
|
|
52
39
|
Invite Client
|
|
53
40
|
</v-btn>
|
|
54
41
|
</template>
|
|
@@ -60,26 +47,16 @@
|
|
|
60
47
|
|
|
61
48
|
<!-- STATUS CELL -->
|
|
62
49
|
<template #item.status="{ value }">
|
|
63
|
-
<v-chip
|
|
64
|
-
size="small"
|
|
65
|
-
:color="value === 'suspended' ? 'error' : 'success'"
|
|
66
|
-
variant="flat"
|
|
67
|
-
>
|
|
50
|
+
<v-chip size="small" :color="value === 'suspended' ? 'error' : 'success'" variant="flat">
|
|
68
51
|
{{ value }}
|
|
69
52
|
</v-chip>
|
|
70
53
|
</template>
|
|
71
54
|
|
|
72
55
|
<!-- ACTION CELL (pending only) -->
|
|
73
56
|
<template #item.action="{ item }">
|
|
74
|
-
<v-btn
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
color="error"
|
|
78
|
-
variant="tonal"
|
|
79
|
-
:loading="cancelLoadingId === item._id"
|
|
80
|
-
:disabled="cancelLoadingId === item._id"
|
|
81
|
-
@click="cancelInvite(item)"
|
|
82
|
-
>
|
|
57
|
+
<v-btn v-if="tab === 'pending'" size="small" color="error" variant="tonal"
|
|
58
|
+
:loading="cancelLoadingId === item._id" :disabled="cancelLoadingId === item._id"
|
|
59
|
+
@click="cancelInvite(item)">
|
|
83
60
|
Cancel Invite
|
|
84
61
|
</v-btn>
|
|
85
62
|
</template>
|
|
@@ -88,11 +65,7 @@
|
|
|
88
65
|
|
|
89
66
|
<!-- DIALOG -->
|
|
90
67
|
<v-dialog v-model="dialog" max-width="500">
|
|
91
|
-
<InvitationClientForm
|
|
92
|
-
title="Invite Client"
|
|
93
|
-
@success="handleSuccess"
|
|
94
|
-
@cancel="dialog = false"
|
|
95
|
-
/>
|
|
68
|
+
<InvitationClientForm title="Invite Client" @success="handleSuccess" @cancel="dialog = false" />
|
|
96
69
|
</v-dialog>
|
|
97
70
|
</v-row>
|
|
98
71
|
</template>
|
|
@@ -125,6 +98,17 @@ const search = ref('')
|
|
|
125
98
|
const totalItems = ref(0)
|
|
126
99
|
const cancelLoadingId = ref<string | null>(null)
|
|
127
100
|
|
|
101
|
+
|
|
102
|
+
const APP_LIST = [
|
|
103
|
+
{ title: "Organization", value: "organization" },
|
|
104
|
+
{ title: "Security Agency", value: "security_agency" },
|
|
105
|
+
{ title: "Cleaning Services", value: "cleaning_services" },
|
|
106
|
+
{ title: "Property Management Agency", value: "property_management_agency" },
|
|
107
|
+
{ title: "Mechanical & Electrical Services", value: "mechanical_electrical_services" },
|
|
108
|
+
{ title: "Pest Control Services", value: "pest_control_services" },
|
|
109
|
+
{ title: "Landscaping Services", value: "landscaping_services" },
|
|
110
|
+
{ title: "Pool Maintenance Services", value: "pool_maintenance_services" },
|
|
111
|
+
]
|
|
128
112
|
/* ================= HEADERS ================= */
|
|
129
113
|
const orgHeaders = [
|
|
130
114
|
{ title: 'Organization Name', key: 'name' },
|
|
@@ -172,10 +156,15 @@ async function resolveRoleName(id: string) {
|
|
|
172
156
|
roleCache.value[id] = roleName
|
|
173
157
|
return roleName
|
|
174
158
|
}
|
|
175
|
-
|
|
159
|
+
const limit = ref(10)
|
|
176
160
|
/* ================= FETCH ================= */
|
|
177
161
|
async function fetchActive() {
|
|
178
|
-
const res = await getAll({
|
|
162
|
+
const res = await getAll({
|
|
163
|
+
page: page.value,
|
|
164
|
+
search: search.value,
|
|
165
|
+
limit: limit.value,
|
|
166
|
+
})
|
|
167
|
+
|
|
179
168
|
const orgs = res?.data?.items || res?.items || []
|
|
180
169
|
|
|
181
170
|
const mapped = await Promise.all(
|
|
@@ -188,6 +177,7 @@ async function fetchActive() {
|
|
|
188
177
|
|
|
189
178
|
try {
|
|
190
179
|
const siteRes = await getCustomerSitesByOrgId(org._id)
|
|
180
|
+
|
|
191
181
|
sitesCount =
|
|
192
182
|
siteRes?.data?.totalItems ||
|
|
193
183
|
siteRes?.totalItems ||
|
|
@@ -197,6 +187,7 @@ async function fetchActive() {
|
|
|
197
187
|
|
|
198
188
|
const subRes = await getSubscriptionByOrgId(org._id)
|
|
199
189
|
const subscription = subRes?.data || subRes
|
|
190
|
+
|
|
200
191
|
if (subscription) {
|
|
201
192
|
planType = subscription?.paidType || '-'
|
|
202
193
|
billingCycle = subscription?.billingCycle || '-'
|
|
@@ -204,25 +195,33 @@ async function fetchActive() {
|
|
|
204
195
|
subscriptionEnd = formatDate(subscription?.nextBillingDate)
|
|
205
196
|
}
|
|
206
197
|
} catch (e) {
|
|
207
|
-
console.error(
|
|
198
|
+
console.error(e)
|
|
208
199
|
}
|
|
209
200
|
|
|
210
201
|
return {
|
|
211
|
-
_id: org._id
|
|
202
|
+
_id: `${org._id}-${page.value}`,
|
|
212
203
|
...org,
|
|
213
204
|
sites: `${sitesCount} Sites`,
|
|
214
205
|
planType,
|
|
215
206
|
billingCycle,
|
|
216
207
|
subscriptionStart,
|
|
217
208
|
subscriptionEnd,
|
|
218
|
-
status: org?.status || '
|
|
209
|
+
status: org?.status || 'active',
|
|
219
210
|
}
|
|
220
211
|
})
|
|
221
212
|
)
|
|
222
213
|
|
|
223
|
-
items.value =
|
|
224
|
-
|
|
225
|
-
|
|
214
|
+
items.value = []
|
|
215
|
+
|
|
216
|
+
await nextTick()
|
|
217
|
+
|
|
218
|
+
items.value = [...mapped]
|
|
219
|
+
|
|
220
|
+
pages.value = res?.pages || 1
|
|
221
|
+
|
|
222
|
+
const match = res?.pageRange?.match(/of\s+(\d+)/i)
|
|
223
|
+
|
|
224
|
+
totalItems.value = match ? Number(match[1]) : mapped.length
|
|
226
225
|
}
|
|
227
226
|
|
|
228
227
|
async function fetchSuspended() {
|
|
@@ -275,9 +274,13 @@ async function fetchSuspended() {
|
|
|
275
274
|
})
|
|
276
275
|
)
|
|
277
276
|
|
|
278
|
-
items.value = mapped
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
items.value = [...mapped]
|
|
278
|
+
|
|
279
|
+
pages.value = res?.pages || 1
|
|
280
|
+
|
|
281
|
+
const match = res?.pageRange?.match(/of\s+(\d+)/i)
|
|
282
|
+
|
|
283
|
+
totalItems.value = match ? Number(match[1]) : mapped.length
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
async function fetchPending() {
|
|
@@ -286,57 +289,75 @@ async function fetchPending() {
|
|
|
286
289
|
type: 'user-invite',
|
|
287
290
|
page: page.value,
|
|
288
291
|
search: search.value,
|
|
289
|
-
app: 'organization',
|
|
290
292
|
})
|
|
291
293
|
|
|
292
|
-
|
|
294
|
+
items.value = [
|
|
295
|
+
...(await Promise.all(
|
|
293
296
|
(res?.items || []).map(async (i: any) => ({
|
|
294
297
|
...i,
|
|
295
|
-
app:
|
|
298
|
+
app:
|
|
299
|
+
APP_LIST.find(a => a.value === i.metadata?.app)?.title ||
|
|
300
|
+
i.metadata?.app ||
|
|
301
|
+
'-',
|
|
302
|
+
|
|
296
303
|
role: await resolveRoleName(i.metadata?.role),
|
|
297
304
|
}))
|
|
298
|
-
)
|
|
305
|
+
))
|
|
306
|
+
]
|
|
299
307
|
|
|
300
308
|
pages.value = res?.pages || 1
|
|
301
|
-
|
|
309
|
+
|
|
310
|
+
const match = res?.pageRange?.match(/of\s+(\d+)/i)
|
|
311
|
+
|
|
312
|
+
totalItems.value = match
|
|
313
|
+
? Number(match[1])
|
|
314
|
+
: items.value.length
|
|
302
315
|
}
|
|
303
316
|
|
|
304
317
|
async function fetchData() {
|
|
305
318
|
loading.value = true
|
|
319
|
+
|
|
306
320
|
try {
|
|
307
|
-
if (tab.value === '
|
|
321
|
+
if (tab.value === 'active') {
|
|
322
|
+
await fetchActive()
|
|
323
|
+
}
|
|
324
|
+
else if (tab.value === 'suspended') {
|
|
308
325
|
items.value = []
|
|
309
326
|
pages.value = 1
|
|
310
327
|
totalItems.value = 0
|
|
311
|
-
return
|
|
312
328
|
}
|
|
313
|
-
|
|
314
|
-
|
|
329
|
+
else {
|
|
330
|
+
await fetchPending()
|
|
331
|
+
}
|
|
315
332
|
} finally {
|
|
316
333
|
loading.value = false
|
|
317
334
|
}
|
|
318
335
|
}
|
|
319
336
|
|
|
320
337
|
/* ================= EVENTS ================= */
|
|
321
|
-
function
|
|
322
|
-
page.value = 1
|
|
323
|
-
fetchData()
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function handleUpdatePage(p: number) {
|
|
338
|
+
async function handleUpdatePage(p: number) {
|
|
327
339
|
page.value = p
|
|
328
|
-
fetchData()
|
|
340
|
+
await fetchData()
|
|
341
|
+
}
|
|
342
|
+
async function onTabChange() {
|
|
343
|
+
page.value = 1
|
|
344
|
+
await fetchData()
|
|
329
345
|
}
|
|
330
346
|
|
|
331
347
|
function handleRefresh() {
|
|
332
348
|
fetchData()
|
|
333
349
|
}
|
|
334
350
|
|
|
351
|
+
let searchTimeout: any = null
|
|
352
|
+
|
|
335
353
|
function handleSearch() {
|
|
336
|
-
|
|
337
|
-
fetchData()
|
|
338
|
-
}
|
|
354
|
+
clearTimeout(searchTimeout)
|
|
339
355
|
|
|
356
|
+
searchTimeout = setTimeout(async () => {
|
|
357
|
+
page.value = 1
|
|
358
|
+
await fetchData()
|
|
359
|
+
}, 500)
|
|
360
|
+
}
|
|
340
361
|
/* ================= DIALOG ================= */
|
|
341
362
|
function openInviteDialog() {
|
|
342
363
|
dialog.value = true
|
|
@@ -119,17 +119,18 @@
|
|
|
119
119
|
import { computed, ref, reactive, watchEffect } from 'vue'
|
|
120
120
|
import useCustomerSite from '../composables/useCustomerSite'
|
|
121
121
|
import useLocal from '../composables/useLocal'
|
|
122
|
-
import { useLocalSetup } from '../composables/useLocalSetup'
|
|
123
122
|
import useRole from '../composables/useRole'
|
|
124
123
|
import useUser from '../composables/useUser'
|
|
125
124
|
import useUtils from '../composables/useUtils'
|
|
125
|
+
import useOrg from '../composables/useOrg'
|
|
126
|
+
import { watch } from 'vue'
|
|
126
127
|
|
|
127
128
|
const APP = useRuntimeConfig().public.APP
|
|
128
129
|
|
|
129
130
|
/* ================= PROPS ================= */
|
|
130
131
|
const props = defineProps({
|
|
131
132
|
title: { type: String, default: "Invite Form" },
|
|
132
|
-
app: { type: String, default: "
|
|
133
|
+
app: { type: String, default: "" },
|
|
133
134
|
org: { type: String, default: "" },
|
|
134
135
|
mode: { type: String, default: "create" },
|
|
135
136
|
})
|
|
@@ -162,17 +163,23 @@ if (props.mode === "create") {
|
|
|
162
163
|
|
|
163
164
|
/* ================= APP LIST ================= */
|
|
164
165
|
const APP_LIST = [
|
|
165
|
-
{ title: "
|
|
166
|
+
{ title: "Property Management Agency", value: "property_management_agency" },
|
|
166
167
|
{ title: "Security Agency", value: "security_agency" },
|
|
167
168
|
{ title: "Cleaning Services", value: "cleaning_services" },
|
|
168
|
-
{ title: "Property Management Agency", value: "property_management_agency" },
|
|
169
169
|
{ title: "Mechanical & Electrical Services", value: "mechanical_electrical_services" },
|
|
170
170
|
{ title: "Pest Control Services", value: "pest_control_services" },
|
|
171
|
-
{ title: "Landscaping Services", value: "landscaping_services" },
|
|
172
171
|
{ title: "Pool Maintenance Services", value: "pool_maintenance_services" },
|
|
173
172
|
]
|
|
174
|
-
|
|
175
|
-
const
|
|
173
|
+
const { getByName } = useOrg()
|
|
174
|
+
const org = ref<any>(null)
|
|
175
|
+
watch(
|
|
176
|
+
() => props.org,
|
|
177
|
+
async (name) => {
|
|
178
|
+
if (!name) return
|
|
179
|
+
org.value = await getByName(name)
|
|
180
|
+
},
|
|
181
|
+
{ immediate: true }
|
|
182
|
+
)
|
|
176
183
|
|
|
177
184
|
const apps = computed(() => APP_LIST)
|
|
178
185
|
|
|
@@ -188,7 +195,7 @@ const { getAll: getAllCustomerSite } = useCustomerSite()
|
|
|
188
195
|
|
|
189
196
|
const { data: siteData, refresh: refreshSiteData } = await useLazyAsyncData(
|
|
190
197
|
"sites",
|
|
191
|
-
() => getAllCustomerSite({ org:
|
|
198
|
+
() => getAllCustomerSite({ org: org.value?._id, limit: 50 })
|
|
192
199
|
)
|
|
193
200
|
|
|
194
201
|
watchEffect(() => {
|
|
@@ -200,9 +207,13 @@ watchEffect(() => {
|
|
|
200
207
|
}
|
|
201
208
|
})
|
|
202
209
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
210
|
+
watch(
|
|
211
|
+
() => org.value?._id,
|
|
212
|
+
async (id) => {
|
|
213
|
+
if (!id) return
|
|
214
|
+
await refreshSiteData()
|
|
215
|
+
}
|
|
216
|
+
)
|
|
206
217
|
|
|
207
218
|
/* ================= ROLE ================= */
|
|
208
219
|
const roles = ref<any[]>([])
|
|
@@ -211,18 +222,24 @@ const { getRoles } = useRole()
|
|
|
211
222
|
const { data: roleData, refresh: refreshRoles } = await useLazyAsyncData(
|
|
212
223
|
"roles",
|
|
213
224
|
() =>
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
225
|
+
getRoles({
|
|
226
|
+
org: org.value?._id,
|
|
227
|
+
type: props.app,
|
|
228
|
+
limit: 50,
|
|
229
|
+
}),
|
|
219
230
|
{ watch: [() => props.app] }
|
|
220
231
|
)
|
|
221
232
|
|
|
222
233
|
watchEffect(() => {
|
|
223
234
|
if (roleData.value) roles.value = roleData.value.items
|
|
224
235
|
})
|
|
225
|
-
|
|
236
|
+
watch(
|
|
237
|
+
() => org.value?._id,
|
|
238
|
+
async (id) => {
|
|
239
|
+
if (!id) return
|
|
240
|
+
await refreshRoles()
|
|
241
|
+
}
|
|
242
|
+
)
|
|
226
243
|
/* ================= EVENTS ================= */
|
|
227
244
|
async function handleUpdateApp(value: string[]) {
|
|
228
245
|
invite.value.role = ""
|
|
@@ -255,7 +272,8 @@ async function submit() {
|
|
|
255
272
|
email: invite.value.email,
|
|
256
273
|
app,
|
|
257
274
|
role: invite.value.role,
|
|
258
|
-
org: invite.value.org,
|
|
275
|
+
// org: invite.value.org,
|
|
276
|
+
org: org.value?._id,
|
|
259
277
|
site: invite.value.site,
|
|
260
278
|
siteName: invite.value.siteName,
|
|
261
279
|
// isMemberInvite: invite.value.isMemberInvite,
|
|
@@ -269,7 +287,7 @@ async function submit() {
|
|
|
269
287
|
email: "",
|
|
270
288
|
app: [],
|
|
271
289
|
role: "",
|
|
272
|
-
org:
|
|
290
|
+
org: org.value?._id || "",
|
|
273
291
|
site: "",
|
|
274
292
|
siteName: "",
|
|
275
293
|
isMemberInvite: false,
|