@frigear-nu/forms 0.0.9 → 0.1.0
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/dist/module.json +1 -1
- package/dist/module.mjs +7 -5
- package/dist/runtime/app/components/FormBuilderAccess.vue +6 -13
- package/dist/runtime/app/components/FormBuilderFieldRow.d.vue.ts +2 -2
- package/dist/runtime/app/components/FormBuilderFieldRow.vue.d.ts +2 -2
- package/dist/runtime/app/components/FormBuilderStepHeader.d.vue.ts +2 -2
- package/dist/runtime/app/components/FormBuilderStepHeader.vue.d.ts +2 -2
- package/dist/runtime/app/components/FormCreateSlideover.d.vue.ts +8 -0
- package/dist/runtime/app/components/FormCreateSlideover.vue +73 -0
- package/dist/runtime/app/components/FormCreateSlideover.vue.d.ts +8 -0
- package/dist/runtime/app/components/FormPreview.vue +2 -8
- package/dist/runtime/app/composables/index.d.ts +1 -0
- package/dist/runtime/app/composables/index.js +1 -0
- package/dist/runtime/app/composables/useFormActions.d.ts +26 -0
- package/dist/runtime/app/composables/useFormActions.js +103 -0
- package/dist/runtime/app/composables/useForms.d.ts +1 -1
- package/dist/runtime/app/composables/useForms.js +2 -9
- package/dist/runtime/app/pages/admin-form.vue +84 -82
- package/dist/runtime/app/pages/admin-submission.vue +60 -29
- package/dist/runtime/app/pages/admin-submissions.vue +81 -47
- package/dist/runtime/app/pages/admin.vue +152 -233
- package/dist/runtime/app/pages/form.vue +47 -45
- package/dist/runtime/i18n/lang/da.json +31 -10
- package/dist/runtime/i18n/lang/en.json +31 -10
- package/dist/runtime/menus.js +0 -11
- package/package.json +17 -17
|
@@ -3,10 +3,13 @@ import { formDefinitionSchema } from "../../utils";
|
|
|
3
3
|
const t = useModuleT("forms");
|
|
4
4
|
const route = useRoute();
|
|
5
5
|
const paths = useFormsPaths();
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const actions = useFormActions();
|
|
7
|
+
const { busy: saving, run } = useAction();
|
|
8
8
|
const id = computed(() => String(route.params.id));
|
|
9
|
-
const { data: form, error, refresh } = await useFetch(() => `/api/forms/admin/forms/${id.value}
|
|
9
|
+
const { data: form, error, refresh } = await useFetch(() => `/api/forms/admin/forms/${id.value}`, {
|
|
10
|
+
key: `forms:admin:form:${id.value}`
|
|
11
|
+
});
|
|
12
|
+
const notFound = computed(() => error.value?.statusCode === 404);
|
|
10
13
|
const draft = reactive({ name: form.value?.name ?? "", slug: form.value?.slug ?? "", status: form.value?.status ?? "draft" });
|
|
11
14
|
const definition = ref(structuredClone(toRaw(form.value?.definition ?? { mode: "single", storage: "db", steps: [] })));
|
|
12
15
|
const tab = ref("builder");
|
|
@@ -40,95 +43,81 @@ const tabs = computed(() => [
|
|
|
40
43
|
{ value: "json", label: t("admin.json", "JSON"), icon: "i-lucide-braces" }
|
|
41
44
|
]);
|
|
42
45
|
const previewKey = ref(0);
|
|
43
|
-
const saving = ref(false);
|
|
44
46
|
const statuses = ["draft", "published", "archived"];
|
|
45
47
|
async function save() {
|
|
46
48
|
if (!checked.value.definition) return;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
+
await run(async () => {
|
|
49
50
|
await $fetch(`/api/forms/admin/forms/${id.value}`, { method: "PATCH", body: { ...draft, definition: checked.value.definition } });
|
|
50
51
|
await refresh();
|
|
51
52
|
previewKey.value++;
|
|
52
|
-
|
|
53
|
-
} catch (cause) {
|
|
54
|
-
toast.add({ title: cause.statusMessage ?? t("error.generic", "Something went wrong. Please try again."), color: "error" });
|
|
55
|
-
} finally {
|
|
56
|
-
saving.value = false;
|
|
57
|
-
}
|
|
53
|
+
}, { success: t("admin.saved", "Saved.") });
|
|
58
54
|
}
|
|
55
|
+
const listPath = paths("admin");
|
|
56
|
+
const menu = computed(() => form.value ? actions.menu({ ...form.value, storage: form.value.definition.storage }, { onRemoved: () => navigateTo(listPath) }) : []);
|
|
59
57
|
</script>
|
|
60
58
|
|
|
61
59
|
<template>
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
variant="subtle"
|
|
67
|
-
:title="t('admin.forbidden', 'You are not allowed to manage forms.')"
|
|
68
|
-
/>
|
|
69
|
-
|
|
70
|
-
<template v-else>
|
|
71
|
-
<AdminPageHeader
|
|
60
|
+
<div class="flex flex-col gap-4">
|
|
61
|
+
<template v-if="form">
|
|
62
|
+
<PageHeading
|
|
63
|
+
:breadcrumb="[{ label: t('admin.title', 'Forms'), to: listPath }, { label: form.name }]"
|
|
72
64
|
:title="form.name"
|
|
73
|
-
:back="{ label: t('admin.title', 'Forms'), to: paths('admin') }"
|
|
74
65
|
>
|
|
75
66
|
<template #title>
|
|
76
|
-
{{ form.name }} <span class="text-base font-normal
|
|
67
|
+
{{ form.name }} <span class="text-muted text-base font-normal">v{{ form.version }}</span>
|
|
77
68
|
</template>
|
|
78
|
-
<template #
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
icon="i-lucide-link"
|
|
84
|
-
:label="t('admin.copyLink', 'Copy link')"
|
|
85
|
-
@click="link.copy(form.slug)"
|
|
86
|
-
/>
|
|
87
|
-
<UButton
|
|
88
|
-
v-if="form.definition.storage === 'db' || (form.submissionCount ?? 0) > 0"
|
|
89
|
-
color="neutral"
|
|
90
|
-
variant="outline"
|
|
91
|
-
:to="paths('adminSubmissions', { id: form.id })"
|
|
92
|
-
:label="t('admin.submissionsTitle', 'Submissions')"
|
|
69
|
+
<template #badge>
|
|
70
|
+
<UBadge
|
|
71
|
+
:color="statusColor(form.status, { archived: 'warning' })"
|
|
72
|
+
variant="subtle"
|
|
73
|
+
:label="t(`status.${form.status}`, form.status)"
|
|
93
74
|
/>
|
|
75
|
+
</template>
|
|
76
|
+
<template #actions>
|
|
94
77
|
<UButton
|
|
95
78
|
:loading="saving"
|
|
96
79
|
:disabled="!checked.definition"
|
|
97
|
-
:label="t('admin.save', 'Save')"
|
|
80
|
+
:label="t('admin.save', 'Save form')"
|
|
98
81
|
@click="save"
|
|
99
82
|
/>
|
|
83
|
+
<RowMenu
|
|
84
|
+
:items="menu"
|
|
85
|
+
:subject="form.name"
|
|
86
|
+
variant="outline"
|
|
87
|
+
/>
|
|
100
88
|
</template>
|
|
101
|
-
</
|
|
89
|
+
</PageHeading>
|
|
102
90
|
|
|
103
|
-
<
|
|
104
|
-
<
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
91
|
+
<SectionCard :title="t('admin.details', 'Details')">
|
|
92
|
+
<div class="grid gap-4 sm:grid-cols-3">
|
|
93
|
+
<UFormField :label="t('admin.name', 'Name')">
|
|
94
|
+
<UInput
|
|
95
|
+
v-model="draft.name"
|
|
96
|
+
class="w-full"
|
|
97
|
+
/>
|
|
98
|
+
</UFormField>
|
|
99
|
+
<UFormField :label="t('admin.slug', 'Slug')">
|
|
100
|
+
<UInput
|
|
101
|
+
v-model="draft.slug"
|
|
102
|
+
class="w-full"
|
|
103
|
+
/>
|
|
104
|
+
</UFormField>
|
|
105
|
+
<UFormField :label="t('admin.status', 'Status')">
|
|
106
|
+
<USelect
|
|
107
|
+
v-model="draft.status"
|
|
108
|
+
:items="statuses.map((value) => ({ value, label: t(`status.${value}`, value) }))"
|
|
109
|
+
class="w-full"
|
|
110
|
+
/>
|
|
111
|
+
</UFormField>
|
|
112
|
+
</div>
|
|
113
|
+
</SectionCard>
|
|
124
114
|
|
|
125
|
-
<
|
|
115
|
+
<PageTabs
|
|
126
116
|
v-model="tab"
|
|
127
117
|
:items="tabs"
|
|
128
|
-
:content="false"
|
|
129
|
-
class="w-full"
|
|
130
118
|
/>
|
|
131
119
|
|
|
120
|
+
<!-- The builder has its own chrome (canvas and settings panel), so it is not framed again. -->
|
|
132
121
|
<FormBuilder
|
|
133
122
|
v-if="tab === 'builder'"
|
|
134
123
|
v-model="definition"
|
|
@@ -144,32 +133,29 @@ async function save() {
|
|
|
144
133
|
</template>
|
|
145
134
|
</FormBuilder>
|
|
146
135
|
|
|
147
|
-
<
|
|
148
|
-
v-
|
|
149
|
-
|
|
150
|
-
/>
|
|
136
|
+
<SectionCard v-else-if="tab === 'settings'">
|
|
137
|
+
<FormBuilderSettings v-model="definition" />
|
|
138
|
+
</SectionCard>
|
|
151
139
|
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
140
|
+
<SectionCard v-else-if="tab === 'access'">
|
|
141
|
+
<FormBuilderAccess
|
|
142
|
+
v-model="definition"
|
|
143
|
+
:form-id="form.id"
|
|
144
|
+
:slug="form.slug"
|
|
145
|
+
/>
|
|
146
|
+
</SectionCard>
|
|
158
147
|
|
|
159
|
-
<
|
|
148
|
+
<SectionCard
|
|
160
149
|
v-else-if="tab === 'json'"
|
|
161
|
-
|
|
150
|
+
:title="t('admin.definition', 'Definition (JSON)')"
|
|
162
151
|
>
|
|
163
|
-
<h2 class="font-semibold">
|
|
164
|
-
{{ t("admin.definition", "Definition (JSON)") }}
|
|
165
|
-
</h2>
|
|
166
152
|
<UTextarea
|
|
167
153
|
:model-value="json"
|
|
168
154
|
:rows="30"
|
|
169
155
|
class="w-full font-mono text-xs"
|
|
170
156
|
@update:model-value="onJson(String($event))"
|
|
171
157
|
/>
|
|
172
|
-
</
|
|
158
|
+
</SectionCard>
|
|
173
159
|
|
|
174
160
|
<UAlert
|
|
175
161
|
v-if="checked.issues.length"
|
|
@@ -189,5 +175,21 @@ async function save() {
|
|
|
189
175
|
</template>
|
|
190
176
|
</UAlert>
|
|
191
177
|
</template>
|
|
192
|
-
|
|
178
|
+
|
|
179
|
+
<UEmpty
|
|
180
|
+
v-else
|
|
181
|
+
:icon="notFound ? 'i-lucide-clipboard-x' : 'i-lucide-lock'"
|
|
182
|
+
:title="notFound ? t('admin.notFound', 'This form does not exist.') : t('admin.forbidden', 'You are not allowed to manage forms.')"
|
|
183
|
+
>
|
|
184
|
+
<template #actions>
|
|
185
|
+
<UButton
|
|
186
|
+
color="neutral"
|
|
187
|
+
variant="outline"
|
|
188
|
+
icon="i-lucide-arrow-left"
|
|
189
|
+
:to="listPath"
|
|
190
|
+
:label="t('admin.allForms', 'All forms')"
|
|
191
|
+
/>
|
|
192
|
+
</template>
|
|
193
|
+
</UEmpty>
|
|
194
|
+
</div>
|
|
193
195
|
</template>
|
|
@@ -2,49 +2,80 @@
|
|
|
2
2
|
const t = useModuleT("forms");
|
|
3
3
|
const route = useRoute();
|
|
4
4
|
const paths = useFormsPaths();
|
|
5
|
-
const
|
|
5
|
+
const format = useFormat();
|
|
6
|
+
const actions = useFormActions();
|
|
6
7
|
const id = computed(() => String(route.params.id));
|
|
7
8
|
const submissionId = computed(() => String(route.params.submissionId));
|
|
9
|
+
const { data: form } = await useFetch(() => `/api/forms/admin/forms/${id.value}`, { key: `forms:admin:form:${id.value}` });
|
|
8
10
|
const { data: submission, error, refresh } = await useFetch(() => `/api/forms/admin/submissions/${submissionId.value}`);
|
|
9
|
-
const redelivering = ref(false);
|
|
10
11
|
async function redeliver() {
|
|
11
|
-
|
|
12
|
-
try {
|
|
13
|
-
await $fetch(`/api/forms/admin/submissions/${submissionId.value}/redeliver`, { method: "POST" });
|
|
14
|
-
await refresh();
|
|
15
|
-
} catch (cause) {
|
|
16
|
-
toast.add({ title: cause.statusMessage ?? t("error.generic", "Something went wrong. Please try again."), color: "error" });
|
|
17
|
-
} finally {
|
|
18
|
-
redelivering.value = false;
|
|
19
|
-
}
|
|
12
|
+
if (await actions.redeliver(submissionId.value)) await refresh();
|
|
20
13
|
}
|
|
14
|
+
const breadcrumb = computed(() => [
|
|
15
|
+
{ label: t("admin.title", "Forms"), to: paths("admin") },
|
|
16
|
+
{ label: form.value?.name ?? id.value, to: paths("adminForm", { id: id.value }) },
|
|
17
|
+
{ label: t("admin.submissionsTitle", "Submissions"), to: paths("adminSubmissions", { id: id.value }) },
|
|
18
|
+
{ label: `#${submissionId.value}` }
|
|
19
|
+
]);
|
|
20
|
+
const menu = computed(() => [[{
|
|
21
|
+
label: t("admin.redeliver", "Deliver again"),
|
|
22
|
+
icon: "i-lucide-send",
|
|
23
|
+
onSelect: redeliver
|
|
24
|
+
}]]);
|
|
25
|
+
const notFound = computed(() => error.value?.statusCode === 404);
|
|
21
26
|
</script>
|
|
22
27
|
|
|
23
28
|
<template>
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
variant="subtle"
|
|
29
|
-
:title="t('admin.forbidden', 'You are not allowed to manage forms.')"
|
|
30
|
-
/>
|
|
31
|
-
|
|
32
|
-
<template v-else>
|
|
33
|
-
<AdminPageHeader
|
|
34
|
-
:title="new Date(submission.createdAt).toLocaleString()"
|
|
29
|
+
<div class="flex flex-col gap-4">
|
|
30
|
+
<template v-if="submission">
|
|
31
|
+
<PageHeading
|
|
32
|
+
:title="format.dateTime(submission.createdAt)"
|
|
35
33
|
:description="`${submission.formSlug} \xB7 v${submission.formVersion} \xB7 ${submission.locale}`"
|
|
36
|
-
:
|
|
37
|
-
|
|
34
|
+
:breadcrumb="breadcrumb"
|
|
35
|
+
>
|
|
36
|
+
<template #badge>
|
|
37
|
+
<UBadge
|
|
38
|
+
:color="statusColor(submission.deliveryStatus)"
|
|
39
|
+
variant="subtle"
|
|
40
|
+
:label="t(`delivery.${submission.deliveryStatus}`, submission.deliveryStatus)"
|
|
41
|
+
/>
|
|
42
|
+
</template>
|
|
43
|
+
<template #actions>
|
|
44
|
+
<RowMenu
|
|
45
|
+
:items="menu"
|
|
46
|
+
:subject="format.dateTime(submission.createdAt)"
|
|
47
|
+
variant="outline"
|
|
48
|
+
/>
|
|
49
|
+
</template>
|
|
50
|
+
</PageHeading>
|
|
38
51
|
|
|
39
52
|
<UAlert
|
|
40
|
-
|
|
53
|
+
v-if="submission.deliveryError"
|
|
54
|
+
color="error"
|
|
41
55
|
variant="subtle"
|
|
42
56
|
:title="t(`delivery.${submission.deliveryStatus}`, submission.deliveryStatus)"
|
|
43
|
-
:description="submission.deliveryError
|
|
44
|
-
:actions="[{ label: t('admin.redeliver', 'Deliver again'), loading: redelivering, onClick: redeliver }]"
|
|
57
|
+
:description="submission.deliveryError"
|
|
45
58
|
/>
|
|
46
59
|
|
|
47
|
-
<
|
|
60
|
+
<SectionCard :title="t('admin.answers', 'Answers')">
|
|
61
|
+
<FormSubmissionView :submission="submission" />
|
|
62
|
+
</SectionCard>
|
|
48
63
|
</template>
|
|
49
|
-
|
|
64
|
+
|
|
65
|
+
<UEmpty
|
|
66
|
+
v-else
|
|
67
|
+
:icon="notFound ? 'i-lucide-inbox' : 'i-lucide-lock'"
|
|
68
|
+
:title="notFound ? t('admin.submissionNotFound', 'This submission does not exist.') : t('admin.forbidden', 'You are not allowed to manage forms.')"
|
|
69
|
+
>
|
|
70
|
+
<template #actions>
|
|
71
|
+
<UButton
|
|
72
|
+
color="neutral"
|
|
73
|
+
variant="outline"
|
|
74
|
+
icon="i-lucide-arrow-left"
|
|
75
|
+
:to="paths('adminSubmissions', { id })"
|
|
76
|
+
:label="t('admin.submissionsTitle', 'Submissions')"
|
|
77
|
+
/>
|
|
78
|
+
</template>
|
|
79
|
+
</UEmpty>
|
|
80
|
+
</div>
|
|
50
81
|
</template>
|
|
@@ -2,65 +2,99 @@
|
|
|
2
2
|
const t = useModuleT("forms");
|
|
3
3
|
const route = useRoute();
|
|
4
4
|
const paths = useFormsPaths();
|
|
5
|
+
const format = useFormat();
|
|
6
|
+
const actions = useFormActions();
|
|
5
7
|
const id = computed(() => String(route.params.id));
|
|
6
|
-
const { data:
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const { data: form } = await useFetch(() => `/api/forms/admin/forms/${id.value}`, { key: `forms:admin:form:${id.value}` });
|
|
9
|
+
const { data: submissions, error, status, refresh } = await useFetch(() => `/api/forms/admin/forms/${id.value}/submissions`);
|
|
10
|
+
const { sorting, header } = useTableSort({ id: "createdAt", desc: true });
|
|
11
|
+
const columns = computed(() => [
|
|
12
|
+
{ accessorKey: "createdAt", header: header("createdAt", t("admin.submittedAt", "Submitted")) },
|
|
10
13
|
{ id: "preview", header: t("admin.answers", "Answers") },
|
|
11
|
-
{ accessorKey: "deliveryStatus", header: t("admin.delivery", "Delivery") }
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
{ accessorKey: "deliveryStatus", header: header("deliveryStatus", t("admin.delivery", "Delivery")) },
|
|
15
|
+
{ id: "actions", header: "", meta: { class: { th: "w-0", td: "w-0" } } }
|
|
16
|
+
]);
|
|
17
|
+
const submissionPath = (submission) => paths("adminSubmission", { id: id.value, submissionId: submission.id });
|
|
18
|
+
const menu = (submission) => [[
|
|
19
|
+
{ label: t("admin.open", "Open"), icon: "i-lucide-arrow-right", to: submissionPath(submission) },
|
|
20
|
+
{
|
|
21
|
+
label: t("admin.redeliver", "Deliver again"),
|
|
22
|
+
icon: "i-lucide-send",
|
|
23
|
+
onSelect: async () => {
|
|
24
|
+
if (await actions.redeliver(submission.id)) await refresh();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]];
|
|
28
|
+
const breadcrumb = computed(() => [
|
|
29
|
+
{ label: t("admin.title", "Forms"), to: paths("admin") },
|
|
30
|
+
{ label: form.value?.name ?? id.value, to: paths("adminForm", { id: id.value }) },
|
|
31
|
+
{ label: t("admin.submissionsTitle", "Submissions") }
|
|
32
|
+
]);
|
|
14
33
|
</script>
|
|
15
34
|
|
|
16
35
|
<template>
|
|
17
|
-
<
|
|
18
|
-
<
|
|
36
|
+
<div class="flex flex-col gap-4">
|
|
37
|
+
<PageHeading
|
|
19
38
|
:title="t('admin.submissionsTitle', 'Submissions')"
|
|
20
|
-
:
|
|
39
|
+
:breadcrumb="breadcrumb"
|
|
21
40
|
/>
|
|
22
41
|
|
|
23
|
-
<
|
|
42
|
+
<UEmpty
|
|
24
43
|
v-if="error"
|
|
25
|
-
|
|
26
|
-
variant="subtle"
|
|
44
|
+
icon="i-lucide-lock"
|
|
27
45
|
:title="t('admin.forbidden', 'You are not allowed to manage forms.')"
|
|
28
46
|
/>
|
|
29
|
-
|
|
47
|
+
|
|
48
|
+
<TableCard
|
|
30
49
|
v-else
|
|
31
|
-
:
|
|
32
|
-
:
|
|
33
|
-
@select="(_, row2) => navigateTo(paths('adminSubmission', { id, submissionId: row2.original.id }))"
|
|
50
|
+
:loading="status === 'pending' && !submissions"
|
|
51
|
+
:refreshing="status === 'pending' && !!submissions"
|
|
34
52
|
>
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
>
|
|
53
|
+
<UTable
|
|
54
|
+
v-model:sorting="sorting"
|
|
55
|
+
:data="submissions ?? []"
|
|
56
|
+
:columns="columns"
|
|
57
|
+
:ui="tableUi()"
|
|
58
|
+
@select="(_, row2) => navigateTo(submissionPath(row2.original))"
|
|
59
|
+
>
|
|
60
|
+
<template #createdAt-cell="{ row }">
|
|
61
|
+
<span class="tabular-nums">{{ format.dateTime(row.original.createdAt) }}</span>
|
|
62
|
+
</template>
|
|
63
|
+
<template #preview-cell="{ row }">
|
|
64
|
+
<p class="max-w-md truncate">
|
|
44
65
|
<span
|
|
45
|
-
v-
|
|
46
|
-
|
|
47
|
-
>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
v-for="(answer, index) in row.original.preview"
|
|
67
|
+
:key="answer.label"
|
|
68
|
+
>
|
|
69
|
+
<span
|
|
70
|
+
v-if="index"
|
|
71
|
+
class="text-muted"
|
|
72
|
+
> · </span>{{ answer.value }}
|
|
73
|
+
</span>
|
|
74
|
+
</p>
|
|
75
|
+
</template>
|
|
76
|
+
<template #deliveryStatus-cell="{ row }">
|
|
77
|
+
<UBadge
|
|
78
|
+
:color="statusColor(row.original.deliveryStatus)"
|
|
79
|
+
variant="subtle"
|
|
80
|
+
:label="t(`delivery.${row.original.deliveryStatus}`, row.original.deliveryStatus)"
|
|
81
|
+
/>
|
|
82
|
+
</template>
|
|
83
|
+
<template #actions-cell="{ row }">
|
|
84
|
+
<RowMenu
|
|
85
|
+
:items="menu(row.original)"
|
|
86
|
+
:subject="format.dateTime(row.original.createdAt)"
|
|
87
|
+
/>
|
|
88
|
+
</template>
|
|
89
|
+
<template #empty>
|
|
90
|
+
<UEmpty
|
|
91
|
+
variant="naked"
|
|
92
|
+
icon="i-lucide-inbox"
|
|
93
|
+
:title="t('admin.noSubmissions', 'No submissions yet.')"
|
|
94
|
+
:description="t('admin.noSubmissionsHint', 'Answers appear here once someone submits the form.')"
|
|
95
|
+
/>
|
|
96
|
+
</template>
|
|
97
|
+
</UTable>
|
|
98
|
+
</TableCard>
|
|
99
|
+
</div>
|
|
66
100
|
</template>
|