@7365admin1/layer-common 1.11.51 → 1.11.52

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.11.52
4
+
5
+ ### Patch Changes
6
+
7
+ - 11b93db: fix site org filters invitation module
8
+
3
9
  ## 1.11.51
4
10
 
5
11
  ### 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="_getVerifications({ search: headerSearch, status })"
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,18 +7,85 @@
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
41
  <InputLabel class="text-capitalize" title="Attachment" />
14
- <v-select
15
- v-model="onlineForm.attachment"
16
- :items="attachments"
17
- item-title="name"
18
- item-value="attachment"
19
- density="comfortable"
20
- type="text"
21
- />
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">
@@ -30,62 +97,74 @@
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
- ></v-text-field>
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="6">
52
- <InputLabel
53
- class="text-capitalize"
54
- :title="`Required ${+inputLabelIndex + 1}`"
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
- class="text-capitalize"
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
- <v-btn
67
- block
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
- variant="text"
85
- class="text-none font-weight-bold"
86
- text="Add"
162
+ class="text-none"
163
+ prepend-icon="mdi-plus"
87
164
  @click="addNewInput"
88
- ></v-btn>
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.name == ''"
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 useOnlineForm from "../composables/useOnlineForm";
219
+ import useOnlineFormTemplates from "../composables/useOnlineFormTemplates";
220
+
143
221
  const prop = defineProps({
144
222
  mode: {
145
223
  type: String,
@@ -168,22 +246,40 @@ 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.name = 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 attachments = ref<any>([
177
- {
178
- _id: "",
179
- name: "",
180
- attachment: "",
181
- type: "",
182
- },
183
- ]);
265
+ const { addFile, getFileUrl } = useFile();
266
+ const { add: _add, updateOnlineFormById: _update, getAllBySiteId: _getAllBySiteId } = useOnlineForm();
184
267
 
185
- const { getDocumentsBySiteId: _getAllDocuments } = useDocument();
186
- const { add: _add, updateOnlineFormById: _update } = useOnlineForm();
268
+ const { data: existingFormsData } = useLazyAsyncData(
269
+ `online-forms-template-filter-${siteId}`,
270
+ () => _getAllBySiteId(siteId, { limit: 100 })
271
+ );
272
+
273
+ const availableTemplateNames = computed(() => {
274
+ const existing = new Set(existingFormsData.value?.items?.map((f: any) => f.name) ?? []);
275
+ return templateNames.filter((name) => !existing.has(name));
276
+ });
277
+
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
  name: "",
@@ -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
308
  onlineForm.value.name = prop.onlineForm.name;
227
309
  onlineForm.value.inputs = prop.onlineForm.inputs;
228
310
  }
229
311
 
230
- watch(
231
- () => onlineForm.value.attachment,
232
- (newVal) => {
233
- if (newVal) {
234
- const val = attachments.value.find(
235
- (attachment: any) => attachment.attachment === newVal
236
- );
237
- onlineForm.value.name = val.name;
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
 
@@ -288,3 +391,28 @@ async function submit() {
288
391
  }
289
392
  }
290
393
  </script>
394
+
395
+ <style scoped>
396
+ .upload-drop-zone {
397
+ border: 2px dashed rgba(0, 0, 0, 0.2);
398
+ border-radius: 8px;
399
+ padding: 18px 12px;
400
+ display: flex;
401
+ flex-direction: column;
402
+ align-items: center;
403
+ justify-content: center;
404
+ cursor: pointer;
405
+ min-height: 80px;
406
+ transition: border-color 0.2s, background-color 0.2s;
407
+ user-select: none;
408
+ }
409
+ .upload-drop-zone:hover,
410
+ .upload-drop-zone.drag-over {
411
+ border-color: rgb(var(--v-theme-primary));
412
+ background-color: rgba(var(--v-theme-primary), 0.04);
413
+ }
414
+ .upload-drop-zone.is-uploading {
415
+ pointer-events: none;
416
+ opacity: 0.65;
417
+ }
418
+ </style>
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <v-dialog
3
+ :model-value="modelValue"
4
+ @update:model-value="$emit('update:modelValue', $event)"
5
+ width="560"
6
+ persistent
7
+ >
8
+ <v-card max-height="85vh" class="d-flex flex-column">
9
+ <v-toolbar color="grey-darken-3" class="flex-grow-0">
10
+ <v-toolbar-title class="text-subtitle-1 font-weight-bold text-white text-uppercase">
11
+ {{ form?.name }}
12
+ </v-toolbar-title>
13
+ </v-toolbar>
14
+
15
+ <v-card-subtitle class="px-6 pt-3 pb-0 text-caption text-medium-emphasis flex-grow-0">
16
+ Form preview — fields are read-only
17
+ </v-card-subtitle>
18
+
19
+ <v-card-text class="px-6 py-4 flex-grow-1" style="overflow-y: auto">
20
+ <template v-for="(input, index) in form?.inputs" :key="index">
21
+ <v-text-field
22
+ v-if="input.dataType === 'Text Input'"
23
+ :label="input.label"
24
+ density="comfortable"
25
+ variant="outlined"
26
+ class="mb-1"
27
+ :hint="input.required ? 'Required' : ''"
28
+ persistent-hint
29
+ readonly
30
+ />
31
+ <v-textarea
32
+ v-else-if="input.dataType === 'Text Area'"
33
+ :label="input.label"
34
+ density="comfortable"
35
+ variant="outlined"
36
+ rows="3"
37
+ class="mb-1"
38
+ :hint="input.required ? 'Required' : ''"
39
+ persistent-hint
40
+ readonly
41
+ />
42
+ <v-text-field
43
+ v-else-if="input.dataType === 'Date Input'"
44
+ :label="input.label"
45
+ type="date"
46
+ density="comfortable"
47
+ variant="outlined"
48
+ class="mb-1"
49
+ :hint="input.required ? 'Required' : ''"
50
+ persistent-hint
51
+ readonly
52
+ />
53
+ <v-select
54
+ v-else-if="input.dataType === 'Select'"
55
+ :label="input.label"
56
+ :items="input.options || []"
57
+ density="comfortable"
58
+ variant="outlined"
59
+ class="mb-1"
60
+ :hint="input.required ? 'Required' : ''"
61
+ persistent-hint
62
+ readonly
63
+ />
64
+ <v-checkbox
65
+ v-else-if="input.dataType === 'Checkbox'"
66
+ :label="input.label"
67
+ density="comfortable"
68
+ class="mb-1"
69
+ readonly
70
+ />
71
+ </template>
72
+ </v-card-text>
73
+
74
+ <v-toolbar density="compact" class="flex-grow-0">
75
+ <v-row no-gutters>
76
+ <v-col cols="12">
77
+ <v-btn
78
+ tile
79
+ block
80
+ variant="flat"
81
+ color="black"
82
+ class="text-none"
83
+ height="48"
84
+ @click="$emit('update:modelValue', false)"
85
+ >
86
+ Close Preview
87
+ </v-btn>
88
+ </v-col>
89
+ </v-row>
90
+ </v-toolbar>
91
+ </v-card>
92
+ </v-dialog>
93
+ </template>
94
+ <script setup lang="ts">
95
+ defineProps({
96
+ modelValue: {
97
+ type: Boolean,
98
+ default: false,
99
+ },
100
+ form: {
101
+ type: Object as PropType<TOnlineForm | null>,
102
+ default: null,
103
+ },
104
+ });
105
+
106
+ defineEmits(["update:modelValue"]);
107
+ </script>