@7365admin1/layer-common 1.11.46 → 1.11.48
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 +13 -0
- package/components/AccessCardPreviewDialog.vue +63 -10
- package/components/AccessManagement.vue +10 -0
- package/components/CameraForm.vue +6 -4
- package/components/ClientMain.vue +70 -58
- package/components/DocumentForm.vue +50 -17
- package/components/DocumentManagement.vue +310 -48
- package/components/RolePermissionFormCreate.vue +2 -3
- package/components/SiteSettings.vue +105 -0
- package/components/TableV3.vue +10 -0
- package/components/VehicleManagement.vue +10 -9
- package/components/VisitorManagement.vue +7 -1
- package/composables/useANPRSettings.ts +27 -0
- package/composables/useAccessManagement.ts +8 -0
- package/composables/useDocument.ts +42 -12
- package/composables/useLocal.ts +18 -0
- package/composables/useOrg.ts +60 -26
- package/composables/useUtils.ts +14 -0
- package/package.json +1 -1
- package/types/customer.d.ts +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 1.11.48
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 75d1ea7: add new nature business v2
|
|
8
|
+
|
|
9
|
+
## 1.11.47
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 3ca3d41: fix search, filter and add get orgs with subscription
|
|
14
|
+
- ecacc9d: update site category, get permissions by invitation
|
|
15
|
+
|
|
3
16
|
## 1.11.46
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
+
<Snackbar v-model="snackbar" :text="snackbarMessage" :color="snackbarColor" />
|
|
3
|
+
|
|
2
4
|
<AccessCardDetailsDialog
|
|
3
5
|
v-model="historyDialog"
|
|
4
6
|
:card="selectedCardInUnit"
|
|
@@ -62,6 +64,7 @@
|
|
|
62
64
|
</v-chip>
|
|
63
65
|
</template>
|
|
64
66
|
</v-autocomplete>
|
|
67
|
+
<p v-if="assignError" class="text-error text-caption mt-2">{{ assignError }}</p>
|
|
65
68
|
</v-card-text>
|
|
66
69
|
|
|
67
70
|
<v-toolbar density="compact">
|
|
@@ -84,7 +87,8 @@
|
|
|
84
87
|
color="black"
|
|
85
88
|
class="text-none"
|
|
86
89
|
height="48"
|
|
87
|
-
:disabled="!assignPerson"
|
|
90
|
+
:disabled="!assignPerson || assignLoading"
|
|
91
|
+
:loading="assignLoading"
|
|
88
92
|
@click="confirmAssign"
|
|
89
93
|
>
|
|
90
94
|
Assign
|
|
@@ -395,10 +399,11 @@ const emit = defineEmits<{
|
|
|
395
399
|
"update:selectedCardInUnit": [value: Record<string, any> | null];
|
|
396
400
|
replace: [];
|
|
397
401
|
delete: [];
|
|
402
|
+
refresh: [];
|
|
398
403
|
"assign-to-person": [{ card: Record<string, any> | null; person: Record<string, any> | null }];
|
|
399
404
|
}>();
|
|
400
405
|
|
|
401
|
-
const { getResidents } = useAccessManagement();
|
|
406
|
+
const { getResidents, assignUser } = useAccessManagement();
|
|
402
407
|
|
|
403
408
|
const historyDialog = ref(false);
|
|
404
409
|
const showAssignForm = ref(false);
|
|
@@ -406,6 +411,18 @@ const assignPersonType = ref<string>("");
|
|
|
406
411
|
const assignPerson = ref<Record<string, any> | null>(null);
|
|
407
412
|
const peopleItems = ref<Record<string, any>[]>([]);
|
|
408
413
|
const peopleLoading = ref(false);
|
|
414
|
+
const assignLoading = ref(false);
|
|
415
|
+
const assignError = ref("");
|
|
416
|
+
const encryptedAcmUrl = ref("");
|
|
417
|
+
const snackbar = ref(false);
|
|
418
|
+
const snackbarMessage = ref("");
|
|
419
|
+
const snackbarColor = ref<"success" | "error">("success");
|
|
420
|
+
|
|
421
|
+
function showToast(message: string, color: "success" | "error") {
|
|
422
|
+
snackbarMessage.value = message;
|
|
423
|
+
snackbarColor.value = color;
|
|
424
|
+
snackbar.value = true;
|
|
425
|
+
}
|
|
409
426
|
|
|
410
427
|
const isSelectedCardAvailable = computed(() => {
|
|
411
428
|
if (!props.selectedCardInUnit?._id) return false;
|
|
@@ -444,13 +461,19 @@ async function openAssignForm() {
|
|
|
444
461
|
showAssignForm.value = true;
|
|
445
462
|
assignPerson.value = null;
|
|
446
463
|
assignPersonType.value = "";
|
|
464
|
+
assignError.value = "";
|
|
447
465
|
peopleItems.value = [];
|
|
466
|
+
encryptedAcmUrl.value = "";
|
|
448
467
|
|
|
449
468
|
if (!props.unit?._id || !props.orgId || !props.siteId) return;
|
|
450
469
|
peopleLoading.value = true;
|
|
451
470
|
try {
|
|
452
|
-
const
|
|
453
|
-
|
|
471
|
+
const [residentsRes, acmRes] = await Promise.all([
|
|
472
|
+
getResidents({ orgId: props.orgId, siteId: props.siteId, unitId: props.unit._id }),
|
|
473
|
+
$fetch<{ encrypted: string }>("/api/encrypt-acm-url"),
|
|
474
|
+
]);
|
|
475
|
+
peopleItems.value = residentsRes?.data ?? [];
|
|
476
|
+
encryptedAcmUrl.value = acmRes?.encrypted ?? "";
|
|
454
477
|
} catch {
|
|
455
478
|
peopleItems.value = [];
|
|
456
479
|
} finally {
|
|
@@ -462,14 +485,44 @@ function cancelAssign() {
|
|
|
462
485
|
showAssignForm.value = false;
|
|
463
486
|
assignPerson.value = null;
|
|
464
487
|
assignPersonType.value = "";
|
|
488
|
+
assignError.value = "";
|
|
465
489
|
}
|
|
466
490
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
491
|
+
const selectedCardType = computed(() => {
|
|
492
|
+
const id = props.selectedCardInUnit?._id;
|
|
493
|
+
if (!id) return "QRCODE";
|
|
494
|
+
const isPhysical = [
|
|
495
|
+
...(props.unit?.available?.physical ?? []),
|
|
496
|
+
...(props.unit?.assigned?.physical ?? []),
|
|
497
|
+
].some((c: any) => c._id === id);
|
|
498
|
+
return isPhysical ? "NFC" : "QRCODE";
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
async function confirmAssign() {
|
|
502
|
+
if (!assignPerson.value || !props.unit?._id) return;
|
|
503
|
+
assignLoading.value = true;
|
|
504
|
+
assignError.value = "";
|
|
505
|
+
try {
|
|
506
|
+
await assignUser({
|
|
507
|
+
assignees: [assignPerson.value._id],
|
|
508
|
+
unit: props.unit._id,
|
|
509
|
+
type: selectedCardType.value,
|
|
510
|
+
acm_url: encryptedAcmUrl.value,
|
|
511
|
+
});
|
|
512
|
+
emit("assign-to-person", {
|
|
513
|
+
card: props.selectedCardInUnit,
|
|
514
|
+
person: assignPerson.value,
|
|
515
|
+
});
|
|
516
|
+
showToast("Card assigned successfully.", "success");
|
|
517
|
+
cancelAssign();
|
|
518
|
+
emit("refresh");
|
|
519
|
+
} catch (e: any) {
|
|
520
|
+
const msg = e?.data?.message ?? "Failed to assign. Please try again.";
|
|
521
|
+
assignError.value = msg;
|
|
522
|
+
showToast(msg, "error");
|
|
523
|
+
} finally {
|
|
524
|
+
assignLoading.value = false;
|
|
525
|
+
}
|
|
473
526
|
}
|
|
474
527
|
|
|
475
528
|
function toggleCard(card: Record<string, any>) {
|
|
@@ -116,6 +116,7 @@
|
|
|
116
116
|
:org-id="orgId"
|
|
117
117
|
@replace="openReplaceDialog()"
|
|
118
118
|
@delete="openDeleteDialog()"
|
|
119
|
+
@refresh="handleAssignRefresh"
|
|
119
120
|
/>
|
|
120
121
|
|
|
121
122
|
<!-- Replace Card Dialog -->
|
|
@@ -305,6 +306,15 @@ function setCard({ mode = "create", dialog = true, data = {} as TCard } = {}) {
|
|
|
305
306
|
}
|
|
306
307
|
}
|
|
307
308
|
|
|
309
|
+
async function handleAssignRefresh() {
|
|
310
|
+
await getCards();
|
|
311
|
+
await nextTick();
|
|
312
|
+
statsRef.value?.refresh();
|
|
313
|
+
const updated = items.value.find((item: any) => item._id === selectedCard.value?._id);
|
|
314
|
+
if (updated) selectedCard.value = updated;
|
|
315
|
+
previewDialog.value = true;
|
|
316
|
+
}
|
|
317
|
+
|
|
308
318
|
function successCreate() {
|
|
309
319
|
createDialog.value = false;
|
|
310
320
|
getCards();
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
/>
|
|
31
31
|
</v-col>
|
|
32
32
|
|
|
33
|
-
<v-col v-if="isANPR" cols="12">
|
|
33
|
+
<!-- <v-col v-if="isANPR" cols="12">
|
|
34
34
|
<InputLabel class="text-capitalize" title="Category" required />
|
|
35
35
|
<v-select
|
|
36
36
|
v-model="camera.category"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
density="comfortable"
|
|
39
39
|
:rules="[requiredRule]"
|
|
40
40
|
/>
|
|
41
|
-
</v-col>
|
|
41
|
+
</v-col> -->
|
|
42
42
|
|
|
43
43
|
<v-col v-if="isANPR" cols="12">
|
|
44
44
|
<InputLabel class="text-capitalize" title="Type" required />
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
/>
|
|
51
51
|
</v-col>
|
|
52
52
|
|
|
53
|
-
<v-col v-if="isANPR" cols="12">
|
|
53
|
+
<!-- <v-col v-if="isANPR" cols="12">
|
|
54
54
|
<InputLabel class="text-capitalize" title="Guard House" />
|
|
55
55
|
<v-select
|
|
56
56
|
v-model="camera.guardPost"
|
|
57
57
|
:items="guardPostOptions"
|
|
58
58
|
density="comfortable"
|
|
59
59
|
/>
|
|
60
|
-
</v-col>
|
|
60
|
+
</v-col> -->
|
|
61
61
|
|
|
62
62
|
<v-col v-if="isANPR" cols="12">
|
|
63
63
|
<InputLabel class="text-capitalize" title="User" required />
|
|
@@ -231,6 +231,8 @@ const anprDirectionOptions = computed(() => [
|
|
|
231
231
|
{ title: "Entry", value: "entry" },
|
|
232
232
|
{ title: "Exit", value: "exit" },
|
|
233
233
|
{ title: "Both Entry and Exit", value: "both" },
|
|
234
|
+
{ title: "Visitors", value: "visitors" },
|
|
235
|
+
{ title: "Residents", value: "residents" },
|
|
234
236
|
]);
|
|
235
237
|
|
|
236
238
|
function back() {
|
|
@@ -228,7 +228,7 @@ import useSubscription from '../composables/useSubscription'
|
|
|
228
228
|
type TabStatus = 'active' | 'suspended' | 'pending'
|
|
229
229
|
|
|
230
230
|
/* ── COMPOSABLES ── */
|
|
231
|
-
const {
|
|
231
|
+
const { getOrganizationsWithSubscription } = useOrg()
|
|
232
232
|
const { getVerifications, cancelUserInvitation } = useVerification()
|
|
233
233
|
const { getCustomerSites: getCustomerSitesByOrgId } = useCustomerSite()
|
|
234
234
|
const { getByOrgId: getSubscriptionByOrgId } = useSubscription()
|
|
@@ -250,7 +250,7 @@ const APP_LIST = [
|
|
|
250
250
|
const tab = ref<TabStatus>('active')
|
|
251
251
|
const dialog = ref(false)
|
|
252
252
|
const openMenuId = ref<string | null>(null)
|
|
253
|
-
// filterStatus
|
|
253
|
+
// filterStatus
|
|
254
254
|
const filterStatus = ref<'active' | 'suspended'>('active')
|
|
255
255
|
const filterPlan = ref('') // '' = All | any APP_LIST value
|
|
256
256
|
const filterBilling = ref('') // '' = All | 'monthly' | 'yearly'
|
|
@@ -303,60 +303,72 @@ function parseTotalItems(pageRangeStr: string, fallback: number): number {
|
|
|
303
303
|
return match ? Number(match[1]) : fallback
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
/* ── FETCH ── */
|
|
307
|
-
async function mapOrg(org: any, status: string) {
|
|
308
|
-
let sitesCount = 0, planType = '-', billingCycle = '-',
|
|
309
|
-
subscriptionStart = '-', subscriptionEnd = '-'
|
|
310
|
-
try {
|
|
311
|
-
const siteRes = await getCustomerSitesByOrgId(org._id)
|
|
312
|
-
sitesCount = siteRes?.data?.totalItems
|
|
313
|
-
?? siteRes?.totalItems
|
|
314
|
-
?? siteRes?.data?.items?.length
|
|
315
|
-
?? siteRes?.items?.length
|
|
316
|
-
?? 0
|
|
317
|
-
|
|
318
|
-
const subRes = await getSubscriptionByOrgId(org._id)
|
|
319
|
-
const sub = subRes?.data ?? subRes
|
|
320
|
-
if (sub) {
|
|
321
|
-
planType = planTypeLabel(sub)
|
|
322
|
-
billingCycle = sub?.billingCycle ?? '-'
|
|
323
|
-
subscriptionStart = formatDate(sub?.createdAt)
|
|
324
|
-
subscriptionEnd = formatDate(sub?.nextBillingDate)
|
|
325
|
-
}
|
|
326
|
-
} catch (e) {
|
|
327
|
-
console.error('mapOrg error:', org._id, e)
|
|
328
|
-
}
|
|
329
|
-
return {
|
|
330
|
-
_id: `${org._id}-${page.value}`,
|
|
331
|
-
...org,
|
|
332
|
-
sites: `${sitesCount} Sites`,
|
|
333
|
-
planType,
|
|
334
|
-
billingCycle,
|
|
335
|
-
subscriptionStart,
|
|
336
|
-
subscriptionEnd,
|
|
337
|
-
status: org?.status ?? status,
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
306
|
|
|
341
307
|
async function fetchOrgs(status: 'active' | 'suspended') {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
308
|
+
loading.value = true
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const res = await getOrganizationsWithSubscription({
|
|
312
|
+
page: page.value,
|
|
313
|
+
search: search.value,
|
|
314
|
+
limit: limit.value,
|
|
315
|
+
status,
|
|
316
|
+
type: filterPlan.value,
|
|
317
|
+
billingCycle: filterBilling.value,
|
|
318
|
+
})
|
|
351
319
|
|
|
352
|
-
|
|
353
|
-
const mapped = await Promise.all(orgs.map((org: any) => mapOrg(org, status)))
|
|
320
|
+
const orgs = res?.data?.items ?? res?.items ?? []
|
|
354
321
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
322
|
+
const siteCache: Record<string, number> = {}
|
|
323
|
+
|
|
324
|
+
items.value = await Promise.all(
|
|
325
|
+
orgs.map(async (org: any) => {
|
|
326
|
+
const sub = org?.subscription ?? {}
|
|
327
|
+
|
|
328
|
+
let siteCount = siteCache[org._id]
|
|
329
|
+
|
|
330
|
+
if (siteCount === undefined) {
|
|
331
|
+
const sitesRes = await getCustomerSitesByOrgId(org._id)
|
|
332
|
+
|
|
333
|
+
siteCount =
|
|
334
|
+
sitesRes?.items?.length ??
|
|
335
|
+
sitesRes?.data?.items?.length ??
|
|
336
|
+
0
|
|
337
|
+
|
|
338
|
+
siteCache[org._id] = siteCount
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
_id: org._id,
|
|
343
|
+
|
|
344
|
+
name: org.name,
|
|
345
|
+
email: org.email,
|
|
346
|
+
nature: org.nature,
|
|
347
|
+
|
|
348
|
+
sites: `${siteCount} Sites`,
|
|
349
|
+
|
|
350
|
+
planType: planTypeLabel(sub),
|
|
351
|
+
|
|
352
|
+
billingCycle: sub?.billingCycle ?? "-",
|
|
353
|
+
|
|
354
|
+
subscriptionStart: formatDate(sub?.createdAt),
|
|
355
|
+
|
|
356
|
+
subscriptionEnd: formatDate(sub?.nextBillingDate),
|
|
357
|
+
|
|
358
|
+
status: org?.status ?? status,
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
pages.value = res?.pages ?? 1
|
|
364
|
+
|
|
365
|
+
totalItems.value = parseTotalItems(
|
|
366
|
+
res?.pageRange,
|
|
367
|
+
items.value.length
|
|
368
|
+
)
|
|
369
|
+
} finally {
|
|
370
|
+
loading.value = false
|
|
371
|
+
}
|
|
360
372
|
}
|
|
361
373
|
|
|
362
374
|
async function fetchPending() {
|
|
@@ -364,7 +376,8 @@ async function fetchPending() {
|
|
|
364
376
|
status: 'pending',
|
|
365
377
|
type: 'user-invite',
|
|
366
378
|
page: page.value,
|
|
367
|
-
search: search.value,
|
|
379
|
+
search: search.value,
|
|
380
|
+
email: search.value,
|
|
368
381
|
})
|
|
369
382
|
|
|
370
383
|
items.value = await Promise.all(
|
|
@@ -391,7 +404,7 @@ async function fetchData() {
|
|
|
391
404
|
}
|
|
392
405
|
}
|
|
393
406
|
|
|
394
|
-
/* ── WATCH: filterStatus
|
|
407
|
+
/* ── WATCH: filterStatus ── */
|
|
395
408
|
watch(filterStatus, (val) => {
|
|
396
409
|
tab.value = val
|
|
397
410
|
page.value = 1
|
|
@@ -406,7 +419,7 @@ async function handleUpdatePage(p: number) {
|
|
|
406
419
|
|
|
407
420
|
async function onTabChange(value: TabStatus) {
|
|
408
421
|
tab.value = value
|
|
409
|
-
// sync filterStatus
|
|
422
|
+
// sync filterStatus ( active/suspended)
|
|
410
423
|
if (value !== 'pending') filterStatus.value = value
|
|
411
424
|
page.value = 1
|
|
412
425
|
await fetchData()
|
|
@@ -421,13 +434,12 @@ function handleSearch() {
|
|
|
421
434
|
if (searchTimeout) clearTimeout(searchTimeout)
|
|
422
435
|
searchTimeout = setTimeout(() => {
|
|
423
436
|
page.value = 1
|
|
424
|
-
fetchData()
|
|
437
|
+
fetchData()
|
|
425
438
|
}, 300)
|
|
426
439
|
}
|
|
427
440
|
|
|
428
441
|
function handleFilterChange() {
|
|
429
|
-
|
|
430
|
-
// nếu chỉ plan/billing thay đổi thì gọi thủ công
|
|
442
|
+
|
|
431
443
|
page.value = 1
|
|
432
444
|
fetchData()
|
|
433
445
|
}
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
v-model="document.attachment"
|
|
16
16
|
:multiple="false"
|
|
17
17
|
:max-length="10"
|
|
18
|
-
title="Upload
|
|
19
|
-
accept=".pdf
|
|
18
|
+
title="Upload Files"
|
|
19
|
+
accept=".pdf,.csv,.doc,.docx"
|
|
20
20
|
/>
|
|
21
21
|
</v-col>
|
|
22
22
|
|
|
@@ -28,15 +28,6 @@
|
|
|
28
28
|
></v-text-field>
|
|
29
29
|
</v-col>
|
|
30
30
|
|
|
31
|
-
<v-col cols="12" class="px-6">
|
|
32
|
-
<InputLabel class="text-capitalize" title="Document Type" />
|
|
33
|
-
<v-select
|
|
34
|
-
v-model="document.type"
|
|
35
|
-
:items="documentTypes"
|
|
36
|
-
density="comfortable"
|
|
37
|
-
/>
|
|
38
|
-
</v-col>
|
|
39
|
-
|
|
40
31
|
<v-col cols="12">
|
|
41
32
|
<v-row no-gutters>
|
|
42
33
|
<v-col cols="12" class="text-center">
|
|
@@ -97,6 +88,10 @@ const prop = defineProps({
|
|
|
97
88
|
type: String,
|
|
98
89
|
default: "add",
|
|
99
90
|
},
|
|
91
|
+
parentId: {
|
|
92
|
+
type: String,
|
|
93
|
+
default: "",
|
|
94
|
+
},
|
|
100
95
|
document: {
|
|
101
96
|
type: Object as PropType<TDocument>,
|
|
102
97
|
default: () => ({
|
|
@@ -116,8 +111,6 @@ const validForm = ref(false);
|
|
|
116
111
|
const disable = ref(false);
|
|
117
112
|
const message = ref("");
|
|
118
113
|
|
|
119
|
-
const documentTypes = ref(["Online Form", "Other"]);
|
|
120
|
-
|
|
121
114
|
const document = ref<Record<string, any>>({
|
|
122
115
|
name: "",
|
|
123
116
|
attachment: [],
|
|
@@ -127,6 +120,10 @@ const document = ref<Record<string, any>>({
|
|
|
127
120
|
const route = useRoute();
|
|
128
121
|
const siteId = route.params.site as string;
|
|
129
122
|
const orgId = route.params.org as string;
|
|
123
|
+
const routeParentId = computed(() =>
|
|
124
|
+
typeof route.query.parentId === "string" ? route.query.parentId : ""
|
|
125
|
+
);
|
|
126
|
+
const effectiveParentId = computed(() => prop.parentId || routeParentId.value);
|
|
130
127
|
|
|
131
128
|
if (prop.mode === "edit") {
|
|
132
129
|
document.value.name = prop.document.name;
|
|
@@ -138,7 +135,8 @@ watch(
|
|
|
138
135
|
() => document.value.attachment,
|
|
139
136
|
async (newVal) => {
|
|
140
137
|
if (newVal && prop.mode !== "edit") {
|
|
141
|
-
const
|
|
138
|
+
const fileId = Array.isArray(newVal) ? newVal[0] : newVal;
|
|
139
|
+
const fileData = await getFileById(fileId);
|
|
142
140
|
if (fileData) {
|
|
143
141
|
document.value.name = fileData.data.name;
|
|
144
142
|
}
|
|
@@ -146,6 +144,14 @@ watch(
|
|
|
146
144
|
}
|
|
147
145
|
);
|
|
148
146
|
|
|
147
|
+
function deriveTypeFromFilename(name = "") {
|
|
148
|
+
const n = String(name).toLowerCase();
|
|
149
|
+
if (n.endsWith(".pdf")) return "pdf";
|
|
150
|
+
if (n.endsWith(".csv")) return "csv";
|
|
151
|
+
if (n.endsWith(".doc") || n.endsWith(".docx")) return "doc";
|
|
152
|
+
return "";
|
|
153
|
+
}
|
|
154
|
+
|
|
149
155
|
function cancel() {
|
|
150
156
|
// createMore.value = false;
|
|
151
157
|
message.value = "";
|
|
@@ -156,23 +162,50 @@ async function submit() {
|
|
|
156
162
|
disable.value = true;
|
|
157
163
|
try {
|
|
158
164
|
if (prop.mode === "add") {
|
|
165
|
+
const fileId = Array.isArray(document.value.attachment)
|
|
166
|
+
? document.value.attachment[0]
|
|
167
|
+
: document.value.attachment;
|
|
168
|
+
|
|
169
|
+
const fileData = await getFileById(fileId);
|
|
170
|
+
const fileName = fileData?.data?.name || String(fileId || "");
|
|
171
|
+
const derivedType = deriveTypeFromFilename(fileName);
|
|
172
|
+
|
|
173
|
+
if (!derivedType) {
|
|
174
|
+
throw new Error("Unsupported file type. Allowed: pdf, csv, doc.");
|
|
175
|
+
}
|
|
176
|
+
|
|
159
177
|
const payload = {
|
|
160
178
|
name: document.value.name,
|
|
161
179
|
attachment: document.value.attachment,
|
|
162
|
-
type:
|
|
180
|
+
type: derivedType,
|
|
181
|
+
size: fileData?.data?.size ?? fileData?.data?.file_size ?? 0,
|
|
163
182
|
status: "active",
|
|
164
183
|
org: orgId,
|
|
165
184
|
site: siteId,
|
|
185
|
+
parentId: effectiveParentId.value,
|
|
166
186
|
};
|
|
187
|
+
|
|
167
188
|
await add(payload);
|
|
168
189
|
}
|
|
169
190
|
|
|
170
191
|
if (prop.mode === "edit") {
|
|
171
|
-
const
|
|
192
|
+
const fileId = Array.isArray(document.value.attachment)
|
|
193
|
+
? document.value.attachment[0]
|
|
194
|
+
: document.value.attachment;
|
|
195
|
+
|
|
196
|
+
let payload: Record<string, any> = {
|
|
172
197
|
name: document.value.name,
|
|
173
198
|
attachment: document.value.attachment,
|
|
174
|
-
type: document.value.type,
|
|
175
199
|
};
|
|
200
|
+
|
|
201
|
+
if (fileId) {
|
|
202
|
+
const fileData = await getFileById(fileId);
|
|
203
|
+
const fileName = fileData?.data?.name || String(fileId || "");
|
|
204
|
+
const derivedType = deriveTypeFromFilename(fileName);
|
|
205
|
+
if (derivedType) payload.type = derivedType;
|
|
206
|
+
payload.size = fileData?.data?.size ?? fileData?.data?.file_size ?? 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
176
209
|
await updateById(prop.document._id ?? "", payload);
|
|
177
210
|
}
|
|
178
211
|
|