@7365admin1/layer-common 1.11.51 → 1.11.53
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/Facility/BookingSetup.vue +146 -0
- package/components/InvitationMain.vue +3 -3
- package/components/OnlineFormConfigurationForm.vue +232 -98
- package/components/OnlineFormRenderPreview.vue +107 -0
- package/components/OnlineFormsConfiguration.vue +47 -27
- package/components/SiteSettings.vue +0 -129
- package/composables/useCustomerSite.ts +33 -1
- package/composables/useFacility.ts +38 -0
- package/composables/useOnlineFormConfiguration.ts +71 -0
- package/composables/useOnlineFormTemplates.ts +145 -0
- package/package.json +1 -1
- package/types/facility.d.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 1.11.53
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- bb3d683: update endpoint for customer site
|
|
8
|
+
- bb3d683: update layer common
|
|
9
|
+
|
|
10
|
+
## 1.11.52
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 11b93db: fix site org filters invitation module
|
|
15
|
+
|
|
3
16
|
## 1.11.51
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -483,6 +483,122 @@
|
|
|
483
483
|
</v-list-item>
|
|
484
484
|
</draggable>
|
|
485
485
|
</section>
|
|
486
|
+
|
|
487
|
+
<v-col cols="12" class="mt-10">
|
|
488
|
+
<p class="header-style font-weight-bold mb-5 mt-md-0 ml-md-4">
|
|
489
|
+
Other Settings
|
|
490
|
+
</p>
|
|
491
|
+
<v-row align="center" class="border pa-md-4" no-gutters>
|
|
492
|
+
<v-col cols="9" class="d-flex align-center text-body-2">
|
|
493
|
+
<v-row no-gutters>
|
|
494
|
+
<v-col cols="12" class="header-style mt-5 mt-md-0">
|
|
495
|
+
Monthly Booking limit per unit
|
|
496
|
+
</v-col>
|
|
497
|
+
<v-col cols="12" class="text-body-2 text-medium-emphasis">
|
|
498
|
+
Sets the maximum number of bookings per unit
|
|
499
|
+
</v-col>
|
|
500
|
+
</v-row>
|
|
501
|
+
</v-col>
|
|
502
|
+
<v-col cols="3" class="d-flex align-center justify-end">
|
|
503
|
+
<v-switch
|
|
504
|
+
v-model="facility.isBookingUnitLimitEnabled"
|
|
505
|
+
class="d-flex align-center"
|
|
506
|
+
color="primary"
|
|
507
|
+
hide-details
|
|
508
|
+
@update:model-value="onToggleBookingUnitLimit"
|
|
509
|
+
/>
|
|
510
|
+
</v-col>
|
|
511
|
+
<v-col cols="12" class="mt-4">
|
|
512
|
+
<v-number-input
|
|
513
|
+
v-model="facility.bookingUnitLimit"
|
|
514
|
+
:reverse="false"
|
|
515
|
+
controlVariant="default"
|
|
516
|
+
label="Monthly booking limit per unit"
|
|
517
|
+
:hideInput="false"
|
|
518
|
+
:inset="false"
|
|
519
|
+
:min="1"
|
|
520
|
+
:max="500"
|
|
521
|
+
hide-details
|
|
522
|
+
:disabled="!facility.isBookingUnitLimitEnabled"
|
|
523
|
+
/>
|
|
524
|
+
</v-col>
|
|
525
|
+
</v-row>
|
|
526
|
+
|
|
527
|
+
<v-row align="center" class="border pa-md-4 mt-4" no-gutters>
|
|
528
|
+
<v-col cols="9" class="d-flex align-center text-body-2">
|
|
529
|
+
<v-row no-gutters>
|
|
530
|
+
<v-col cols="12" class="header-style mt-5 mt-md-0">
|
|
531
|
+
Maximum No. of Guest
|
|
532
|
+
</v-col>
|
|
533
|
+
<v-col cols="12" class="text-body-2 text-medium-emphasis">
|
|
534
|
+
The maximum number of guests refers to the highest allowable
|
|
535
|
+
capacity a venue can safely and comfortably accommodate for an
|
|
536
|
+
event.
|
|
537
|
+
</v-col>
|
|
538
|
+
</v-row>
|
|
539
|
+
</v-col>
|
|
540
|
+
<v-col cols="3" class="d-flex align-center justify-end">
|
|
541
|
+
<v-switch
|
|
542
|
+
v-model="facility.isMaxNumberGuestLimitEnabled"
|
|
543
|
+
class="d-flex align-center"
|
|
544
|
+
color="primary"
|
|
545
|
+
hide-details
|
|
546
|
+
@update:model-value="onToggleMaxNumberGuestLimit"
|
|
547
|
+
/>
|
|
548
|
+
</v-col>
|
|
549
|
+
<v-col cols="12" class="mt-4">
|
|
550
|
+
<v-number-input
|
|
551
|
+
v-model="facility.maxNumberGuest"
|
|
552
|
+
:reverse="false"
|
|
553
|
+
controlVariant="default"
|
|
554
|
+
label="Maximum no. of guests"
|
|
555
|
+
:hideInput="false"
|
|
556
|
+
:inset="false"
|
|
557
|
+
:min="1"
|
|
558
|
+
:max="500"
|
|
559
|
+
hide-details
|
|
560
|
+
:disabled="!facility.isMaxNumberGuestLimitEnabled"
|
|
561
|
+
/>
|
|
562
|
+
</v-col>
|
|
563
|
+
</v-row>
|
|
564
|
+
|
|
565
|
+
<v-row align="center" class="border pa-md-4 mt-4" no-gutters>
|
|
566
|
+
<v-col cols="9" class="d-flex align-center text-body-2">
|
|
567
|
+
<v-row no-gutters>
|
|
568
|
+
<v-col cols="12" class="header-style mt-5 mt-md-0">
|
|
569
|
+
Cancel allowed up to (Days prior)
|
|
570
|
+
</v-col>
|
|
571
|
+
<v-col cols="12" class="text-body-2 text-medium-emphasis">
|
|
572
|
+
Number of days before a scheduled booking that a user is allowed
|
|
573
|
+
to cancel.
|
|
574
|
+
</v-col>
|
|
575
|
+
</v-row>
|
|
576
|
+
</v-col>
|
|
577
|
+
<v-col cols="3" class="d-flex align-center justify-end">
|
|
578
|
+
<v-switch
|
|
579
|
+
v-model="facility.isCancelAllowedLimitEnabled"
|
|
580
|
+
class="d-flex align-center"
|
|
581
|
+
color="primary"
|
|
582
|
+
hide-details
|
|
583
|
+
@update:model-value="onToggleCancelAllowedLimit"
|
|
584
|
+
/>
|
|
585
|
+
</v-col>
|
|
586
|
+
<v-col cols="12" class="mt-4">
|
|
587
|
+
<v-number-input
|
|
588
|
+
v-model="facility.daysAllowedCancel"
|
|
589
|
+
:reverse="false"
|
|
590
|
+
controlVariant="default"
|
|
591
|
+
label="Cancel allowed up to (Days prior)"
|
|
592
|
+
:hideInput="false"
|
|
593
|
+
:inset="false"
|
|
594
|
+
:min="1"
|
|
595
|
+
:max="500"
|
|
596
|
+
hide-details
|
|
597
|
+
:disabled="!facility.isCancelAllowedLimitEnabled"
|
|
598
|
+
/>
|
|
599
|
+
</v-col>
|
|
600
|
+
</v-row>
|
|
601
|
+
</v-col>
|
|
486
602
|
</v-col>
|
|
487
603
|
|
|
488
604
|
<!-- remarks -->
|
|
@@ -896,6 +1012,36 @@ const confirmDelete = () => {
|
|
|
896
1012
|
const handlePolicy = (newContent: string) => {
|
|
897
1013
|
facility.value.bookingPolicy = newContent;
|
|
898
1014
|
};
|
|
1015
|
+
|
|
1016
|
+
function onToggleBookingUnitLimit(val: any) {
|
|
1017
|
+
if (val) {
|
|
1018
|
+
isBookingUnitLimitEnabled.value = true;
|
|
1019
|
+
facility.value.bookingUnitLimit = 1;
|
|
1020
|
+
} else {
|
|
1021
|
+
isBookingUnitLimitEnabled.value = false;
|
|
1022
|
+
facility.value.bookingUnitLimit = null;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function onToggleMaxNumberGuestLimit(val: any) {
|
|
1027
|
+
if (val) {
|
|
1028
|
+
isMaxNumberGuestLimitEnabled.value = true;
|
|
1029
|
+
facility.value.maxNumberGuest = 1;
|
|
1030
|
+
} else {
|
|
1031
|
+
isMaxNumberGuestLimitEnabled.value = false;
|
|
1032
|
+
facility.value.maxNumberGuest = null;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function onToggleCancelAllowedLimit(val: any) {
|
|
1037
|
+
if (val) {
|
|
1038
|
+
isCancelAllowedLimitEnabled.value = true;
|
|
1039
|
+
facility.value.daysAllowedCancel = 1;
|
|
1040
|
+
} else {
|
|
1041
|
+
isCancelAllowedLimitEnabled.value = false;
|
|
1042
|
+
facility.value.daysAllowedCancel = null;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
899
1045
|
</script>
|
|
900
1046
|
|
|
901
1047
|
<style scoped>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
fab
|
|
23
23
|
icon
|
|
24
24
|
density="comfortable"
|
|
25
|
-
@click="
|
|
25
|
+
@click="getVerifications()"
|
|
26
26
|
>
|
|
27
27
|
<v-icon>mdi-refresh</v-icon>
|
|
28
28
|
</v-btn>
|
|
@@ -210,8 +210,6 @@ const headers = [
|
|
|
210
210
|
},
|
|
211
211
|
];
|
|
212
212
|
|
|
213
|
-
const status = (useRoute().params.status as string) ?? "";
|
|
214
|
-
|
|
215
213
|
const { getVerifications: _getVerifications, cancelUserInvitation } =
|
|
216
214
|
useVerification();
|
|
217
215
|
|
|
@@ -248,6 +246,8 @@ const {
|
|
|
248
246
|
search: headerSearch.value,
|
|
249
247
|
type: "user-invite,member-invite",
|
|
250
248
|
app: props.app,
|
|
249
|
+
orgId: props.org,
|
|
250
|
+
siteId: props.siteId,
|
|
251
251
|
})
|
|
252
252
|
);
|
|
253
253
|
|
|
@@ -7,85 +7,164 @@
|
|
|
7
7
|
</span>
|
|
8
8
|
</v-row>
|
|
9
9
|
</v-toolbar>
|
|
10
|
+
<v-col v-if="prop.mode !== 'edit'" cols="12" class="px-6 pt-4 pb-0">
|
|
11
|
+
<InputLabel title="Form Template" />
|
|
12
|
+
<v-list
|
|
13
|
+
density="compact"
|
|
14
|
+
lines="one"
|
|
15
|
+
max-height="160"
|
|
16
|
+
style="overflow-y: auto; border: 1px solid rgba(0,0,0,0.18); border-radius: 4px;"
|
|
17
|
+
class="pa-0"
|
|
18
|
+
>
|
|
19
|
+
<v-list-item
|
|
20
|
+
v-if="availableTemplateNames.length === 0"
|
|
21
|
+
disabled
|
|
22
|
+
title="All templates have already been created"
|
|
23
|
+
class="text-caption text-medium-emphasis"
|
|
24
|
+
/>
|
|
25
|
+
<v-list-item
|
|
26
|
+
v-for="name in availableTemplateNames"
|
|
27
|
+
:key="name"
|
|
28
|
+
:title="name"
|
|
29
|
+
:active="selectedTemplate === name"
|
|
30
|
+
active-color="primary"
|
|
31
|
+
class="text-caption"
|
|
32
|
+
@click="selectedTemplate = name; applyTemplate(name)"
|
|
33
|
+
/>
|
|
34
|
+
</v-list>
|
|
35
|
+
</v-col>
|
|
36
|
+
<v-divider v-if="prop.mode !== 'edit'" class="mt-3" />
|
|
37
|
+
|
|
10
38
|
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-0">
|
|
11
39
|
<v-form v-model="validForm" :disabled="disable">
|
|
12
40
|
<v-col cols="12" class="px-6">
|
|
13
|
-
<InputLabel class="text-capitalize" title="Attachment" />
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
41
|
+
<InputLabel class="text-capitalize" title="Attachment (Optional)" />
|
|
42
|
+
|
|
43
|
+
<div v-if="onlineForm.attachment" class="d-flex align-center" style="gap:8px">
|
|
44
|
+
<v-card
|
|
45
|
+
variant="outlined"
|
|
46
|
+
border="thin"
|
|
47
|
+
class="px-3 flex-grow-1 text-caption"
|
|
48
|
+
height="40"
|
|
49
|
+
:href="getFileUrl(onlineForm.attachment)"
|
|
50
|
+
target="_blank"
|
|
51
|
+
>
|
|
52
|
+
<v-row no-gutters class="fill-height" align="center" style="gap:6px">
|
|
53
|
+
<v-icon size="15">mdi-file-document-outline</v-icon>
|
|
54
|
+
{{ attachmentFileName || 'View attachment' }}
|
|
55
|
+
<v-icon size="13" class="ml-auto">mdi-open-in-new</v-icon>
|
|
56
|
+
</v-row>
|
|
57
|
+
</v-card>
|
|
58
|
+
<v-btn icon variant="text" size="small" density="comfortable" :disabled="disable" @click="clearAttachment">
|
|
59
|
+
<v-icon size="17">mdi-close</v-icon>
|
|
60
|
+
</v-btn>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div
|
|
64
|
+
v-else
|
|
65
|
+
class="upload-drop-zone"
|
|
66
|
+
:class="{ 'drag-over': isDragOver, 'is-uploading': isUploading }"
|
|
67
|
+
@dragover.prevent="isDragOver = true"
|
|
68
|
+
@dragleave.prevent="isDragOver = false"
|
|
69
|
+
@drop.prevent="handleDrop"
|
|
70
|
+
@click="fileInputRef?.click()"
|
|
71
|
+
>
|
|
72
|
+
<v-progress-circular v-if="isUploading" indeterminate size="24" width="2" color="primary" class="mb-1" />
|
|
73
|
+
<v-icon v-else size="26" :color="isDragOver ? 'primary' : undefined" class="mb-1 text-medium-emphasis">
|
|
74
|
+
mdi-cloud-upload-outline
|
|
75
|
+
</v-icon>
|
|
76
|
+
<span class="text-caption text-medium-emphasis">
|
|
77
|
+
{{ isUploading ? 'Uploading…' : 'Drop file here or click to browse' }}
|
|
78
|
+
</span>
|
|
79
|
+
<input
|
|
80
|
+
ref="fileInputRef"
|
|
81
|
+
type="file"
|
|
82
|
+
accept=".pdf,application/pdf,image/*"
|
|
83
|
+
style="display:none"
|
|
84
|
+
@change="handleFileInputChange"
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<span v-if="attachmentError" class="text-caption text-error d-block mt-1">{{ attachmentError }}</span>
|
|
22
89
|
</v-col>
|
|
23
90
|
|
|
24
91
|
<v-col cols="12" class="px-6">
|
|
25
92
|
<InputLabel class="text-capitalize" title="Name" />
|
|
26
93
|
<v-text-field
|
|
27
|
-
v-model.trim="onlineForm.
|
|
94
|
+
v-model.trim="onlineForm.formType"
|
|
28
95
|
density="comfortable"
|
|
29
96
|
></v-text-field>
|
|
30
97
|
</v-col>
|
|
31
98
|
|
|
32
99
|
<template v-for="(inputLabel, inputLabelIndex) in onlineForm.inputs">
|
|
100
|
+
<v-row class="px-6 mb-0" align="center" no-gutters>
|
|
101
|
+
<v-col>
|
|
102
|
+
<span class="text-caption font-weight-medium text-medium-emphasis">
|
|
103
|
+
Field {{ inputLabelIndex + 1 }}
|
|
104
|
+
</span>
|
|
105
|
+
</v-col>
|
|
106
|
+
<v-col cols="auto">
|
|
107
|
+
<v-btn
|
|
108
|
+
icon
|
|
109
|
+
size="small"
|
|
110
|
+
variant="text"
|
|
111
|
+
color="error"
|
|
112
|
+
:disabled="onlineForm.inputs.length === 1"
|
|
113
|
+
@click="onlineForm.inputs.splice(inputLabelIndex, 1)"
|
|
114
|
+
>
|
|
115
|
+
<v-icon size="18">mdi-delete-outline</v-icon>
|
|
116
|
+
</v-btn>
|
|
117
|
+
</v-col>
|
|
118
|
+
</v-row>
|
|
33
119
|
<v-row class="px-6">
|
|
34
120
|
<v-col cols="6">
|
|
35
|
-
<InputLabel
|
|
36
|
-
class="text-capitalize"
|
|
37
|
-
:title="`Label ${+inputLabelIndex + 1}`"
|
|
38
|
-
/>
|
|
121
|
+
<InputLabel title="Label" />
|
|
39
122
|
<v-text-field
|
|
40
123
|
v-model.trim="inputLabel.label"
|
|
41
124
|
density="comfortable"
|
|
42
|
-
|
|
125
|
+
/>
|
|
43
126
|
</v-col>
|
|
44
127
|
<v-col cols="6">
|
|
45
|
-
<InputLabel
|
|
46
|
-
class="text-capitalize"
|
|
47
|
-
:title="`Data Type ${+inputLabelIndex + 1}`"
|
|
48
|
-
/>
|
|
128
|
+
<InputLabel title="Data Type" />
|
|
49
129
|
<v-select v-model="inputLabel.dataType" :items="dataTypes" />
|
|
50
130
|
</v-col>
|
|
51
|
-
<v-col cols="
|
|
52
|
-
<InputLabel
|
|
53
|
-
|
|
54
|
-
|
|
131
|
+
<v-col v-if="inputLabel.dataType === 'Select'" cols="12">
|
|
132
|
+
<InputLabel title="Options" />
|
|
133
|
+
<v-combobox
|
|
134
|
+
v-model="inputLabel.options"
|
|
135
|
+
chips
|
|
136
|
+
closable-chips
|
|
137
|
+
multiple
|
|
138
|
+
density="comfortable"
|
|
139
|
+
variant="outlined"
|
|
140
|
+
placeholder="Type an option and press Enter"
|
|
141
|
+
hint="Press Enter after each option to add it"
|
|
142
|
+
persistent-hint
|
|
55
143
|
/>
|
|
56
|
-
<v-checkbox v-model="inputLabel.required" />
|
|
57
144
|
</v-col>
|
|
58
145
|
<v-col cols="6">
|
|
59
|
-
<InputLabel
|
|
60
|
-
|
|
61
|
-
:title="`Multiple ${+inputLabelIndex + 1}`"
|
|
62
|
-
/>
|
|
63
|
-
<v-checkbox v-model="inputLabel.multiple" />
|
|
146
|
+
<InputLabel title="Required" />
|
|
147
|
+
<v-checkbox v-model="inputLabel.required" />
|
|
64
148
|
</v-col>
|
|
65
149
|
<v-col cols="6">
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
color="red"
|
|
69
|
-
variant="text"
|
|
70
|
-
class="text-none font-weight-bold"
|
|
71
|
-
text="Remove"
|
|
72
|
-
:disabled="onlineForm.inputs.length === 1"
|
|
73
|
-
@click="onlineForm.inputs.splice(inputLabelIndex, 1)"
|
|
74
|
-
/>
|
|
150
|
+
<InputLabel title="Multiple" />
|
|
151
|
+
<v-checkbox v-model="inputLabel.multiple" />
|
|
75
152
|
</v-col>
|
|
76
153
|
</v-row>
|
|
77
154
|
<v-divider class="my-3" />
|
|
78
155
|
</template>
|
|
79
156
|
|
|
80
|
-
<v-col cols="12" class="px-6">
|
|
157
|
+
<v-col cols="12" class="px-6 pb-4">
|
|
81
158
|
<v-btn
|
|
82
159
|
block
|
|
160
|
+
variant="outlined"
|
|
83
161
|
color="primary"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
text="Add"
|
|
162
|
+
class="text-none"
|
|
163
|
+
prepend-icon="mdi-plus"
|
|
87
164
|
@click="addNewInput"
|
|
88
|
-
|
|
165
|
+
>
|
|
166
|
+
Add Field
|
|
167
|
+
</v-btn>
|
|
89
168
|
</v-col>
|
|
90
169
|
|
|
91
170
|
<v-col cols="12">
|
|
@@ -123,12 +202,7 @@
|
|
|
123
202
|
color="black"
|
|
124
203
|
class="text-none"
|
|
125
204
|
size="48"
|
|
126
|
-
:disabled="
|
|
127
|
-
!validForm ||
|
|
128
|
-
disable ||
|
|
129
|
-
onlineForm.attachment.length == 0 ||
|
|
130
|
-
onlineForm.name == ''
|
|
131
|
-
"
|
|
205
|
+
:disabled="!validForm || disable || onlineForm.formType == ''"
|
|
132
206
|
@click="submit"
|
|
133
207
|
:loading="disable"
|
|
134
208
|
>
|
|
@@ -140,6 +214,10 @@
|
|
|
140
214
|
</v-card>
|
|
141
215
|
</template>
|
|
142
216
|
<script setup lang="ts">
|
|
217
|
+
import useFile from "../composables/useFile";
|
|
218
|
+
import useOnlineFormConfiguration from "../composables/useOnlineFormConfiguration";
|
|
219
|
+
import useOnlineFormTemplates from "../composables/useOnlineFormTemplates";
|
|
220
|
+
|
|
143
221
|
const prop = defineProps({
|
|
144
222
|
mode: {
|
|
145
223
|
type: String,
|
|
@@ -168,25 +246,43 @@ const emit = defineEmits(["cancel", "success"]);
|
|
|
168
246
|
const validForm = ref(false);
|
|
169
247
|
const disable = ref(false);
|
|
170
248
|
const message = ref("");
|
|
249
|
+
const selectedTemplate = ref<string | null>(null);
|
|
250
|
+
|
|
251
|
+
const { formTemplates, templateNames } = useOnlineFormTemplates();
|
|
252
|
+
|
|
253
|
+
function applyTemplate(name: string | null) {
|
|
254
|
+
if (!name) return;
|
|
255
|
+
const template = formTemplates[name];
|
|
256
|
+
if (!template) return;
|
|
257
|
+
onlineForm.value.formType = name;
|
|
258
|
+
onlineForm.value.inputs = template.map((f) => ({ ...f }));
|
|
259
|
+
}
|
|
171
260
|
|
|
172
261
|
const route = useRoute();
|
|
173
262
|
const siteId = route.params.site as string;
|
|
174
263
|
const orgId = route.params.org as string;
|
|
175
264
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
265
|
+
const { addFile, getFileUrl } = useFile();
|
|
266
|
+
const { create: _add, updateById: _update, getAll: _getAll } = useOnlineFormConfiguration();
|
|
267
|
+
|
|
268
|
+
const { data: existingFormsData } = useLazyAsyncData(
|
|
269
|
+
`online-form-configurations-filter-${siteId}-${Date.now()}`,
|
|
270
|
+
() => _getAll({ site: siteId, org: orgId, limit: 100 })
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
const availableTemplateNames = computed(() => {
|
|
274
|
+
const existing = new Set(existingFormsData.value?.items?.map((f: any) => f.formType) ?? []);
|
|
275
|
+
return templateNames.filter((name) => !existing.has(name));
|
|
276
|
+
});
|
|
184
277
|
|
|
185
|
-
const
|
|
186
|
-
const
|
|
278
|
+
const isDragOver = ref(false);
|
|
279
|
+
const isUploading = ref(false);
|
|
280
|
+
const attachmentError = ref("");
|
|
281
|
+
const attachmentFileName = ref("");
|
|
282
|
+
const fileInputRef = ref<HTMLInputElement | null>(null);
|
|
187
283
|
|
|
188
284
|
const onlineForm = ref<Record<string, any>>({
|
|
189
|
-
|
|
285
|
+
formType: "",
|
|
190
286
|
attachment: "",
|
|
191
287
|
inputs: [
|
|
192
288
|
{
|
|
@@ -207,37 +303,43 @@ const dataTypes = ref([
|
|
|
207
303
|
"Checkbox",
|
|
208
304
|
]);
|
|
209
305
|
|
|
210
|
-
const {
|
|
211
|
-
data: getDocumentReq,
|
|
212
|
-
refresh: getDocuments,
|
|
213
|
-
status: getAllReqStatus,
|
|
214
|
-
} = useLazyAsyncData("get-all-documents", () =>
|
|
215
|
-
_getAllDocuments(siteId, "Online Form")
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
watchEffect(() => {
|
|
219
|
-
if (getDocumentReq.value) {
|
|
220
|
-
attachments.value = getDocumentReq.value;
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
306
|
if (prop.mode === "edit") {
|
|
225
307
|
onlineForm.value.attachment = prop.onlineForm.attachment;
|
|
226
|
-
onlineForm.value.
|
|
308
|
+
onlineForm.value.formType = prop.onlineForm.formType;
|
|
227
309
|
onlineForm.value.inputs = prop.onlineForm.inputs;
|
|
228
310
|
}
|
|
229
311
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
312
|
+
function handleDrop(e: DragEvent) {
|
|
313
|
+
isDragOver.value = false;
|
|
314
|
+
const file = e.dataTransfer?.files[0];
|
|
315
|
+
if (file) uploadAttachment(file);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function handleFileInputChange(e: Event) {
|
|
319
|
+
const file = (e.target as HTMLInputElement).files?.[0];
|
|
320
|
+
if (file) uploadAttachment(file);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function uploadAttachment(file: File) {
|
|
324
|
+
isUploading.value = true;
|
|
325
|
+
attachmentError.value = "";
|
|
326
|
+
try {
|
|
327
|
+
const res = await addFile(file);
|
|
328
|
+
onlineForm.value.attachment = res.id;
|
|
329
|
+
attachmentFileName.value = file.name;
|
|
330
|
+
} catch (e: any) {
|
|
331
|
+
attachmentError.value = e?.message || "Upload failed. Please try again.";
|
|
332
|
+
} finally {
|
|
333
|
+
isUploading.value = false;
|
|
239
334
|
}
|
|
240
|
-
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function clearAttachment() {
|
|
338
|
+
onlineForm.value.attachment = "";
|
|
339
|
+
attachmentFileName.value = "";
|
|
340
|
+
attachmentError.value = "";
|
|
341
|
+
if (fileInputRef.value) fileInputRef.value.value = "";
|
|
342
|
+
}
|
|
241
343
|
|
|
242
344
|
function cancel() {
|
|
243
345
|
emit("cancel");
|
|
@@ -249,6 +351,7 @@ function addNewInput() {
|
|
|
249
351
|
dataType: "",
|
|
250
352
|
required: true,
|
|
251
353
|
multiple: false,
|
|
354
|
+
options: [],
|
|
252
355
|
});
|
|
253
356
|
}
|
|
254
357
|
|
|
@@ -257,26 +360,32 @@ async function submit() {
|
|
|
257
360
|
disable.value = true;
|
|
258
361
|
try {
|
|
259
362
|
if (prop.mode === "add") {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
363
|
+
const fields: Record<string, string> = Object.fromEntries(
|
|
364
|
+
onlineForm.value.inputs.map((input: any, i: number) => [String(i), JSON.stringify(input)])
|
|
365
|
+
);
|
|
366
|
+
if (onlineForm.value.attachment) fields["attachment"] = onlineForm.value.attachment;
|
|
367
|
+
|
|
368
|
+
await _add({
|
|
369
|
+
formType: onlineForm.value.formType,
|
|
370
|
+
fields,
|
|
264
371
|
status: "active",
|
|
265
372
|
org: orgId,
|
|
266
373
|
site: siteId,
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
await _add(payload);
|
|
374
|
+
});
|
|
270
375
|
}
|
|
271
376
|
|
|
272
377
|
if (prop.mode === "edit") {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
};
|
|
378
|
+
const fields: Record<string, string> = Object.fromEntries(
|
|
379
|
+
onlineForm.value.inputs.map((input: any, i: number) => [String(i), JSON.stringify(input)])
|
|
380
|
+
);
|
|
381
|
+
if (onlineForm.value.attachment) fields["attachment"] = onlineForm.value.attachment;
|
|
278
382
|
|
|
279
|
-
await _update(prop.onlineForm._id ?? "",
|
|
383
|
+
await _update(prop.onlineForm._id ?? "", {
|
|
384
|
+
_id: prop.onlineForm._id ?? "",
|
|
385
|
+
formType: onlineForm.value.formType,
|
|
386
|
+
fields,
|
|
387
|
+
status: onlineForm.value.status || "active",
|
|
388
|
+
});
|
|
280
389
|
}
|
|
281
390
|
|
|
282
391
|
emit("success");
|
|
@@ -288,3 +397,28 @@ async function submit() {
|
|
|
288
397
|
}
|
|
289
398
|
}
|
|
290
399
|
</script>
|
|
400
|
+
|
|
401
|
+
<style scoped>
|
|
402
|
+
.upload-drop-zone {
|
|
403
|
+
border: 2px dashed rgba(0, 0, 0, 0.2);
|
|
404
|
+
border-radius: 8px;
|
|
405
|
+
padding: 18px 12px;
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-direction: column;
|
|
408
|
+
align-items: center;
|
|
409
|
+
justify-content: center;
|
|
410
|
+
cursor: pointer;
|
|
411
|
+
min-height: 80px;
|
|
412
|
+
transition: border-color 0.2s, background-color 0.2s;
|
|
413
|
+
user-select: none;
|
|
414
|
+
}
|
|
415
|
+
.upload-drop-zone:hover,
|
|
416
|
+
.upload-drop-zone.drag-over {
|
|
417
|
+
border-color: rgb(var(--v-theme-primary));
|
|
418
|
+
background-color: rgba(var(--v-theme-primary), 0.04);
|
|
419
|
+
}
|
|
420
|
+
.upload-drop-zone.is-uploading {
|
|
421
|
+
pointer-events: none;
|
|
422
|
+
opacity: 0.65;
|
|
423
|
+
}
|
|
424
|
+
</style>
|