@frigear-nu/forms 0.0.0-stage → 0.0.1
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/README.md +22 -2
- package/dist/module.d.mts +281 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +102 -0
- package/dist/runtime/app/components/FormField.d.vue.ts +16 -0
- package/dist/runtime/app/components/FormField.vue +80 -0
- package/dist/runtime/app/components/FormField.vue.d.ts +16 -0
- package/dist/runtime/app/components/FormRenderer.d.vue.ts +19 -0
- package/dist/runtime/app/components/FormRenderer.vue +145 -0
- package/dist/runtime/app/components/FormRenderer.vue.d.ts +19 -0
- package/dist/runtime/app/components/FormSubmissionView.d.vue.ts +7 -0
- package/dist/runtime/app/components/FormSubmissionView.vue +77 -0
- package/dist/runtime/app/components/FormSubmissionView.vue.d.ts +7 -0
- package/dist/runtime/app/composables/index.d.ts +2 -0
- package/dist/runtime/app/composables/index.js +2 -0
- package/dist/runtime/app/composables/useFormStepper.d.ts +21 -0
- package/dist/runtime/app/composables/useFormStepper.js +33 -0
- package/dist/runtime/app/composables/useForms.d.ts +21 -0
- package/dist/runtime/app/composables/useForms.js +52 -0
- package/dist/runtime/app/pages/admin-form.d.vue.ts +3 -0
- package/dist/runtime/app/pages/admin-form.vue +150 -0
- package/dist/runtime/app/pages/admin-form.vue.d.ts +3 -0
- package/dist/runtime/app/pages/admin-submission.d.vue.ts +3 -0
- package/dist/runtime/app/pages/admin-submission.vue +59 -0
- package/dist/runtime/app/pages/admin-submission.vue.d.ts +3 -0
- package/dist/runtime/app/pages/admin-submissions.d.vue.ts +3 -0
- package/dist/runtime/app/pages/admin-submissions.vue +71 -0
- package/dist/runtime/app/pages/admin-submissions.vue.d.ts +3 -0
- package/dist/runtime/app/pages/admin.d.vue.ts +3 -0
- package/dist/runtime/app/pages/admin.vue +194 -0
- package/dist/runtime/app/pages/admin.vue.d.ts +3 -0
- package/dist/runtime/app/pages/form.d.vue.ts +3 -0
- package/dist/runtime/app/pages/form.vue +18 -0
- package/dist/runtime/app/pages/form.vue.d.ts +3 -0
- package/dist/runtime/i18n/lang/da.json +84 -0
- package/dist/runtime/i18n/lang/en.json +84 -0
- package/dist/runtime/menus.d.ts +2 -0
- package/dist/runtime/menus.js +15 -0
- package/dist/runtime/server/db/schema.sqlite.d.ts +504 -0
- package/dist/runtime/server/db/schema.sqlite.js +46 -0
- package/dist/runtime/server/plugins/email-delivery.d.ts +2 -0
- package/dist/runtime/server/plugins/email-delivery.js +21 -0
- package/dist/runtime/server/plugins/seed.d.ts +2 -0
- package/dist/runtime/server/plugins/seed.js +11 -0
- package/dist/runtime/server/routes/admin/form.delete.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form.delete.js +8 -0
- package/dist/runtime/server/routes/admin/form.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form.get.js +4 -0
- package/dist/runtime/server/routes/admin/form.patch.d.ts +2 -0
- package/dist/runtime/server/routes/admin/form.patch.js +17 -0
- package/dist/runtime/server/routes/admin/forms.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/forms.get.js +19 -0
- package/dist/runtime/server/routes/admin/forms.post.d.ts +2 -0
- package/dist/runtime/server/routes/admin/forms.post.js +13 -0
- package/dist/runtime/server/routes/admin/import.post.d.ts +2 -0
- package/dist/runtime/server/routes/admin/import.post.js +6 -0
- package/dist/runtime/server/routes/admin/submission-file.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/submission-file.get.js +17 -0
- package/dist/runtime/server/routes/admin/submission-redeliver.post.d.ts +2 -0
- package/dist/runtime/server/routes/admin/submission-redeliver.post.js +29 -0
- package/dist/runtime/server/routes/admin/submission.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/submission.get.js +22 -0
- package/dist/runtime/server/routes/admin/submissions.get.d.ts +2 -0
- package/dist/runtime/server/routes/admin/submissions.get.js +16 -0
- package/dist/runtime/server/routes/public/form.get.d.ts +2 -0
- package/dist/runtime/server/routes/public/form.get.js +3 -0
- package/dist/runtime/server/routes/public/submit.post.d.ts +2 -0
- package/dist/runtime/server/routes/public/submit.post.js +7 -0
- package/dist/runtime/server/utils/admin-schemas.d.ts +291 -0
- package/dist/runtime/server/utils/admin-schemas.js +14 -0
- package/dist/runtime/server/utils/auth.d.ts +11 -0
- package/dist/runtime/server/utils/auth.js +7 -0
- package/dist/runtime/server/utils/forms.d.ts +9 -0
- package/dist/runtime/server/utils/forms.js +30 -0
- package/dist/runtime/server/utils/rate-limit.d.ts +6 -0
- package/dist/runtime/server/utils/rate-limit.js +14 -0
- package/dist/runtime/server/utils/render.d.ts +11 -0
- package/dist/runtime/server/utils/render.js +26 -0
- package/dist/runtime/server/utils/seed.d.ts +9 -0
- package/dist/runtime/server/utils/seed.js +22 -0
- package/dist/runtime/server/utils/submit.d.ts +9 -0
- package/dist/runtime/server/utils/submit.js +120 -0
- package/dist/runtime/utils/conditions.d.ts +19 -0
- package/dist/runtime/utils/conditions.js +36 -0
- package/dist/runtime/utils/definition.d.ts +91 -0
- package/dist/runtime/utils/definition.js +103 -0
- package/dist/runtime/utils/index.d.ts +13 -0
- package/dist/runtime/utils/index.js +8 -0
- package/dist/runtime/utils/markdown.d.ts +5 -0
- package/dist/runtime/utils/markdown.js +5 -0
- package/dist/runtime/utils/schema.d.ts +27 -0
- package/dist/runtime/utils/schema.js +95 -0
- package/dist/runtime/utils/text.d.ts +12 -0
- package/dist/runtime/utils/text.js +12 -0
- package/dist/types.d.mts +3 -0
- package/package.json +86 -3
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const t = useModuleT("forms");
|
|
3
|
+
const route = useRoute();
|
|
4
|
+
const paths = useFormsPaths();
|
|
5
|
+
const id = computed(() => String(route.params.id));
|
|
6
|
+
const { data: submissions, error } = await useFetch(() => `/api/forms/admin/forms/${id.value}/submissions`);
|
|
7
|
+
const color = { none: "neutral", pending: "warning", delivered: "success", failed: "error" };
|
|
8
|
+
const format = (iso) => new Date(iso).toLocaleString();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<UContainer class="space-y-6 py-10">
|
|
13
|
+
<div>
|
|
14
|
+
<NuxtLink
|
|
15
|
+
:to="paths('adminForm', { id })"
|
|
16
|
+
class="text-sm text-muted hover:underline"
|
|
17
|
+
>
|
|
18
|
+
← {{ t("admin.edit", "Edit") }}
|
|
19
|
+
</NuxtLink>
|
|
20
|
+
<h1 class="text-3xl font-bold">
|
|
21
|
+
{{ t("admin.submissionsTitle", "Submissions") }}
|
|
22
|
+
</h1>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<UAlert
|
|
26
|
+
v-if="error"
|
|
27
|
+
color="error"
|
|
28
|
+
variant="subtle"
|
|
29
|
+
:title="t('admin.forbidden', 'You are not allowed to manage forms.')"
|
|
30
|
+
/>
|
|
31
|
+
<UEmpty
|
|
32
|
+
v-else-if="!submissions?.length"
|
|
33
|
+
icon="i-lucide-inbox"
|
|
34
|
+
:title="t('admin.noSubmissions', 'No submissions yet.')"
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
<div
|
|
38
|
+
v-else
|
|
39
|
+
class="divide-y divide-default rounded-lg border border-default"
|
|
40
|
+
>
|
|
41
|
+
<NuxtLink
|
|
42
|
+
v-for="submission in submissions"
|
|
43
|
+
:key="submission.id"
|
|
44
|
+
:to="paths('adminSubmission', { id, submissionId: submission.id })"
|
|
45
|
+
class="flex flex-wrap items-center justify-between gap-3 p-4 hover:bg-elevated"
|
|
46
|
+
>
|
|
47
|
+
<div class="min-w-0 space-y-1">
|
|
48
|
+
<p class="text-sm text-muted">
|
|
49
|
+
{{ format(submission.createdAt) }}
|
|
50
|
+
</p>
|
|
51
|
+
<p class="truncate">
|
|
52
|
+
<span
|
|
53
|
+
v-for="(answer, index) in submission.preview"
|
|
54
|
+
:key="answer.label"
|
|
55
|
+
>
|
|
56
|
+
<span
|
|
57
|
+
v-if="index"
|
|
58
|
+
class="text-muted"
|
|
59
|
+
> · </span>{{ answer.value }}
|
|
60
|
+
</span>
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
<UBadge
|
|
64
|
+
:color="color[submission.deliveryStatus]"
|
|
65
|
+
variant="subtle"
|
|
66
|
+
:label="t(`delivery.${submission.deliveryStatus}`, submission.deliveryStatus)"
|
|
67
|
+
/>
|
|
68
|
+
</NuxtLink>
|
|
69
|
+
</div>
|
|
70
|
+
</UContainer>
|
|
71
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { defineForm } from "../../utils";
|
|
3
|
+
const t = useModuleT("forms");
|
|
4
|
+
const paths = useFormsPaths();
|
|
5
|
+
const toast = useToast();
|
|
6
|
+
const { data: forms, error, refresh } = await useFetch("/api/forms/admin/forms");
|
|
7
|
+
const starter = defineForm({
|
|
8
|
+
mode: "single",
|
|
9
|
+
storage: "db",
|
|
10
|
+
steps: [{
|
|
11
|
+
id: "main",
|
|
12
|
+
fields: [
|
|
13
|
+
{ key: "name", type: "text", label: "Name", required: true },
|
|
14
|
+
{ key: "message", type: "textarea", label: "Message", required: true }
|
|
15
|
+
]
|
|
16
|
+
}]
|
|
17
|
+
});
|
|
18
|
+
const creating = ref(false);
|
|
19
|
+
const draft = reactive({ name: "", slug: "" });
|
|
20
|
+
const importing = ref(false);
|
|
21
|
+
const importJson = ref("");
|
|
22
|
+
const slugify = (value) => value.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
23
|
+
watch(() => draft.name, (name, previous) => {
|
|
24
|
+
if (draft.slug === slugify(previous)) draft.slug = slugify(name);
|
|
25
|
+
});
|
|
26
|
+
async function create() {
|
|
27
|
+
try {
|
|
28
|
+
const created = await $fetch("/api/forms/admin/forms", { method: "POST", body: { ...draft, definition: starter } });
|
|
29
|
+
await navigateTo(paths("adminForm", { id: created.id }));
|
|
30
|
+
} catch (cause) {
|
|
31
|
+
toast.add({ title: cause.statusMessage ?? t("error.generic", "Something went wrong. Please try again."), color: "error" });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function runImport() {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(importJson.value);
|
|
37
|
+
const result = await $fetch("/api/forms/admin/import", {
|
|
38
|
+
method: "POST",
|
|
39
|
+
body: { forms: Array.isArray(parsed) ? parsed : [parsed] }
|
|
40
|
+
});
|
|
41
|
+
toast.add({ title: t("admin.imported", "Imported {inserted}, skipped {skipped} existing.", { inserted: result.inserted.length, skipped: result.skipped.length }), color: "success" });
|
|
42
|
+
importing.value = false;
|
|
43
|
+
importJson.value = "";
|
|
44
|
+
await refresh();
|
|
45
|
+
} catch (cause) {
|
|
46
|
+
toast.add({ title: cause.statusMessage ?? t("admin.invalidJson", "The JSON is not valid."), color: "error" });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const statusColor = { draft: "neutral", published: "success", archived: "warning" };
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<UContainer class="space-y-6 py-10">
|
|
54
|
+
<div class="flex items-center justify-between gap-2">
|
|
55
|
+
<h1 class="text-3xl font-bold">
|
|
56
|
+
{{ t("admin.title", "Forms") }}
|
|
57
|
+
</h1>
|
|
58
|
+
<div class="flex gap-2">
|
|
59
|
+
<UButton
|
|
60
|
+
color="neutral"
|
|
61
|
+
variant="outline"
|
|
62
|
+
icon="i-lucide-upload"
|
|
63
|
+
:label="t('admin.import', 'Import')"
|
|
64
|
+
@click="importing = true"
|
|
65
|
+
/>
|
|
66
|
+
<UButton
|
|
67
|
+
icon="i-lucide-plus"
|
|
68
|
+
:label="t('admin.new', 'New form')"
|
|
69
|
+
@click="creating = true"
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<UAlert
|
|
75
|
+
v-if="error"
|
|
76
|
+
color="error"
|
|
77
|
+
variant="subtle"
|
|
78
|
+
:title="t('admin.forbidden', 'You are not allowed to manage forms.')"
|
|
79
|
+
/>
|
|
80
|
+
|
|
81
|
+
<UEmpty
|
|
82
|
+
v-else-if="!forms?.length"
|
|
83
|
+
icon="i-lucide-clipboard-list"
|
|
84
|
+
:title="t('admin.empty', 'No forms yet.')"
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<div
|
|
88
|
+
v-else
|
|
89
|
+
class="divide-y divide-default rounded-lg border border-default"
|
|
90
|
+
>
|
|
91
|
+
<div
|
|
92
|
+
v-for="form in forms"
|
|
93
|
+
:key="form.id"
|
|
94
|
+
class="flex flex-wrap items-center justify-between gap-3 p-4"
|
|
95
|
+
>
|
|
96
|
+
<div class="space-y-1">
|
|
97
|
+
<NuxtLink
|
|
98
|
+
:to="paths('adminForm', { id: form.id })"
|
|
99
|
+
class="font-medium hover:underline"
|
|
100
|
+
>
|
|
101
|
+
{{ form.name }}
|
|
102
|
+
</NuxtLink>
|
|
103
|
+
<p class="text-sm text-muted">
|
|
104
|
+
/{{ form.slug }} · {{ form.mode === "stepped" ? t("admin.stepped", "Stepped") : t("admin.single", "Single page") }}
|
|
105
|
+
· v{{ form.version }}
|
|
106
|
+
<template v-if="form.storage === 'none'">
|
|
107
|
+
· {{ t("admin.notStored", "Not stored") }}
|
|
108
|
+
</template>
|
|
109
|
+
</p>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="flex items-center gap-3">
|
|
112
|
+
<UBadge
|
|
113
|
+
:color="statusColor[form.status]"
|
|
114
|
+
variant="subtle"
|
|
115
|
+
:label="t(`status.${form.status}`, form.status)"
|
|
116
|
+
/>
|
|
117
|
+
<UButton
|
|
118
|
+
v-if="form.storage === 'db'"
|
|
119
|
+
color="neutral"
|
|
120
|
+
variant="outline"
|
|
121
|
+
size="sm"
|
|
122
|
+
:to="paths('adminSubmissions', { id: form.id })"
|
|
123
|
+
:label="t('admin.submissions', 'Submissions ({count})', { count: form.submissionCount })"
|
|
124
|
+
/>
|
|
125
|
+
<UButton
|
|
126
|
+
color="neutral"
|
|
127
|
+
variant="ghost"
|
|
128
|
+
size="sm"
|
|
129
|
+
icon="i-lucide-pencil"
|
|
130
|
+
:to="paths('adminForm', { id: form.id })"
|
|
131
|
+
:aria-label="t('admin.edit', 'Edit')"
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<UModal
|
|
138
|
+
v-model:open="creating"
|
|
139
|
+
:title="t('admin.new', 'New form')"
|
|
140
|
+
>
|
|
141
|
+
<template #body>
|
|
142
|
+
<form
|
|
143
|
+
class="space-y-4"
|
|
144
|
+
@submit.prevent="create"
|
|
145
|
+
>
|
|
146
|
+
<UFormField :label="t('admin.name', 'Name')">
|
|
147
|
+
<UInput
|
|
148
|
+
v-model="draft.name"
|
|
149
|
+
class="w-full"
|
|
150
|
+
autofocus
|
|
151
|
+
/>
|
|
152
|
+
</UFormField>
|
|
153
|
+
<UFormField
|
|
154
|
+
:label="t('admin.slug', 'Slug')"
|
|
155
|
+
:description="t('admin.slugHint', 'Used in the public address.')"
|
|
156
|
+
>
|
|
157
|
+
<UInput
|
|
158
|
+
v-model="draft.slug"
|
|
159
|
+
class="w-full"
|
|
160
|
+
/>
|
|
161
|
+
</UFormField>
|
|
162
|
+
<UButton
|
|
163
|
+
type="submit"
|
|
164
|
+
:label="t('admin.create', 'Create')"
|
|
165
|
+
:disabled="!draft.name || !draft.slug"
|
|
166
|
+
/>
|
|
167
|
+
</form>
|
|
168
|
+
</template>
|
|
169
|
+
</UModal>
|
|
170
|
+
|
|
171
|
+
<UModal
|
|
172
|
+
v-model:open="importing"
|
|
173
|
+
:title="t('admin.import', 'Import')"
|
|
174
|
+
>
|
|
175
|
+
<template #body>
|
|
176
|
+
<div class="space-y-4">
|
|
177
|
+
<p class="text-sm text-muted">
|
|
178
|
+
{{ t("admin.importHint", "Paste one form or a list of forms (each with slug, name and definition). Forms whose slug already exists are skipped.") }}
|
|
179
|
+
</p>
|
|
180
|
+
<UTextarea
|
|
181
|
+
v-model="importJson"
|
|
182
|
+
:rows="12"
|
|
183
|
+
class="w-full font-mono text-xs"
|
|
184
|
+
/>
|
|
185
|
+
<UButton
|
|
186
|
+
:label="t('admin.import', 'Import')"
|
|
187
|
+
:disabled="!importJson.trim()"
|
|
188
|
+
@click="runImport"
|
|
189
|
+
/>
|
|
190
|
+
</div>
|
|
191
|
+
</template>
|
|
192
|
+
</UModal>
|
|
193
|
+
</UContainer>
|
|
194
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const route = useRoute();
|
|
3
|
+
const slug = computed(() => String(route.params.slug));
|
|
4
|
+
const { data: form } = await useForm(slug);
|
|
5
|
+
useHead({ title: computed(() => form.value?.name ?? "") });
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<UContainer class="max-w-2xl py-10">
|
|
10
|
+
<h1
|
|
11
|
+
v-if="form"
|
|
12
|
+
class="mb-6 text-3xl font-bold"
|
|
13
|
+
>
|
|
14
|
+
{{ form.name }}
|
|
15
|
+
</h1>
|
|
16
|
+
<FormRenderer :slug="slug" />
|
|
17
|
+
</UContainer>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"forms": {
|
|
3
|
+
"actions": {
|
|
4
|
+
"next": "Næste",
|
|
5
|
+
"back": "Tilbage",
|
|
6
|
+
"submit": "Send",
|
|
7
|
+
"sendAnother": "Send en ny"
|
|
8
|
+
},
|
|
9
|
+
"step": {
|
|
10
|
+
"progress": "Trin {current} af {total}"
|
|
11
|
+
},
|
|
12
|
+
"success": {
|
|
13
|
+
"title": "Tak!",
|
|
14
|
+
"description": "Dine svar er sendt."
|
|
15
|
+
},
|
|
16
|
+
"error": {
|
|
17
|
+
"generic": "Noget gik galt. Prøv igen.",
|
|
18
|
+
"invalid": "Tjek de markerede felter.",
|
|
19
|
+
"notFound": "Formularen blev ikke fundet."
|
|
20
|
+
},
|
|
21
|
+
"preview": {
|
|
22
|
+
"notSent": "Forhåndsvisning: intet blev sendt."
|
|
23
|
+
},
|
|
24
|
+
"value": {
|
|
25
|
+
"yes": "Ja",
|
|
26
|
+
"no": "Nej"
|
|
27
|
+
},
|
|
28
|
+
"status": {
|
|
29
|
+
"draft": "Kladde",
|
|
30
|
+
"published": "Udgivet",
|
|
31
|
+
"archived": "Arkiveret"
|
|
32
|
+
},
|
|
33
|
+
"delivery": {
|
|
34
|
+
"none": "Ikke leveret",
|
|
35
|
+
"pending": "Afventer",
|
|
36
|
+
"delivered": "Leveret",
|
|
37
|
+
"failed": "Levering fejlede"
|
|
38
|
+
},
|
|
39
|
+
"validation": {
|
|
40
|
+
"required": "Feltet er påkrævet.",
|
|
41
|
+
"email": "Indtast en gyldig e-mailadresse.",
|
|
42
|
+
"tel": "Indtast et gyldigt telefonnummer.",
|
|
43
|
+
"date": "Indtast en gyldig dato.",
|
|
44
|
+
"option": "Vælg en af mulighederne.",
|
|
45
|
+
"min": "Skal være mindst {min}.",
|
|
46
|
+
"max": "Må højst være {max}.",
|
|
47
|
+
"minLength": "Indtast mindst {min} tegn.",
|
|
48
|
+
"maxLength": "Indtast højst {max} tegn.",
|
|
49
|
+
"pattern": "Værdien har et ugyldigt format.",
|
|
50
|
+
"maxFiles": "Upload højst {max} fil(er).",
|
|
51
|
+
"maxSize": "{name} er større end {max} MB.",
|
|
52
|
+
"accept": "{name} er ikke en tilladt filtype."
|
|
53
|
+
},
|
|
54
|
+
"admin": {
|
|
55
|
+
"title": "Formularer",
|
|
56
|
+
"new": "Ny formular",
|
|
57
|
+
"import": "Importér",
|
|
58
|
+
"edit": "Rediger",
|
|
59
|
+
"create": "Opret",
|
|
60
|
+
"save": "Gem",
|
|
61
|
+
"saved": "Gemt.",
|
|
62
|
+
"name": "Navn",
|
|
63
|
+
"slug": "Slug",
|
|
64
|
+
"slugHint": "Bruges i den offentlige adresse.",
|
|
65
|
+
"status": "Status",
|
|
66
|
+
"empty": "Ingen formularer endnu.",
|
|
67
|
+
"stepped": "Flere trin",
|
|
68
|
+
"single": "Én side",
|
|
69
|
+
"notStored": "Gemmes ikke",
|
|
70
|
+
"submissions": "Besvarelser ({count})",
|
|
71
|
+
"submissionsTitle": "Besvarelser",
|
|
72
|
+
"noSubmissions": "Ingen besvarelser endnu.",
|
|
73
|
+
"redeliver": "Lever igen",
|
|
74
|
+
"forbidden": "Du har ikke adgang til at administrere formularer.",
|
|
75
|
+
"imported": "Importerede {inserted}, sprang {skipped} eksisterende over.",
|
|
76
|
+
"invalidJson": "JSON er ugyldig.",
|
|
77
|
+
"importHint": "Indsæt én formular eller en liste af formularer (hver med slug, name og definition). Formularer med et eksisterende slug springes over.",
|
|
78
|
+
"definition": "Definition (JSON)",
|
|
79
|
+
"invalidDefinition": "Definitionen har fejl",
|
|
80
|
+
"preview": "Forhåndsvisning",
|
|
81
|
+
"previewUnavailable": "Ret definitionen for at se en forhåndsvisning."
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"forms": {
|
|
3
|
+
"actions": {
|
|
4
|
+
"next": "Next",
|
|
5
|
+
"back": "Back",
|
|
6
|
+
"submit": "Submit",
|
|
7
|
+
"sendAnother": "Send another"
|
|
8
|
+
},
|
|
9
|
+
"step": {
|
|
10
|
+
"progress": "Step {current} of {total}"
|
|
11
|
+
},
|
|
12
|
+
"success": {
|
|
13
|
+
"title": "Thank you!",
|
|
14
|
+
"description": "Your answers have been sent."
|
|
15
|
+
},
|
|
16
|
+
"error": {
|
|
17
|
+
"generic": "Something went wrong. Please try again.",
|
|
18
|
+
"invalid": "Please check the highlighted fields.",
|
|
19
|
+
"notFound": "This form could not be found."
|
|
20
|
+
},
|
|
21
|
+
"preview": {
|
|
22
|
+
"notSent": "Preview: nothing was sent."
|
|
23
|
+
},
|
|
24
|
+
"value": {
|
|
25
|
+
"yes": "Yes",
|
|
26
|
+
"no": "No"
|
|
27
|
+
},
|
|
28
|
+
"status": {
|
|
29
|
+
"draft": "Draft",
|
|
30
|
+
"published": "Published",
|
|
31
|
+
"archived": "Archived"
|
|
32
|
+
},
|
|
33
|
+
"delivery": {
|
|
34
|
+
"none": "Not delivered",
|
|
35
|
+
"pending": "Pending",
|
|
36
|
+
"delivered": "Delivered",
|
|
37
|
+
"failed": "Delivery failed"
|
|
38
|
+
},
|
|
39
|
+
"validation": {
|
|
40
|
+
"required": "This field is required.",
|
|
41
|
+
"email": "Enter a valid email address.",
|
|
42
|
+
"tel": "Enter a valid phone number.",
|
|
43
|
+
"date": "Enter a valid date.",
|
|
44
|
+
"option": "Choose one of the available options.",
|
|
45
|
+
"min": "Must be at least {min}.",
|
|
46
|
+
"max": "Must be at most {max}.",
|
|
47
|
+
"minLength": "Enter at least {min} characters.",
|
|
48
|
+
"maxLength": "Enter at most {max} characters.",
|
|
49
|
+
"pattern": "The value has an invalid format.",
|
|
50
|
+
"maxFiles": "Upload at most {max} file(s).",
|
|
51
|
+
"maxSize": "{name} is larger than {max} MB.",
|
|
52
|
+
"accept": "{name} is not an accepted file type."
|
|
53
|
+
},
|
|
54
|
+
"admin": {
|
|
55
|
+
"title": "Forms",
|
|
56
|
+
"new": "New form",
|
|
57
|
+
"import": "Import",
|
|
58
|
+
"edit": "Edit",
|
|
59
|
+
"create": "Create",
|
|
60
|
+
"save": "Save",
|
|
61
|
+
"saved": "Saved.",
|
|
62
|
+
"name": "Name",
|
|
63
|
+
"slug": "Slug",
|
|
64
|
+
"slugHint": "Used in the public address.",
|
|
65
|
+
"status": "Status",
|
|
66
|
+
"empty": "No forms yet.",
|
|
67
|
+
"stepped": "Stepped",
|
|
68
|
+
"single": "Single page",
|
|
69
|
+
"notStored": "Not stored",
|
|
70
|
+
"submissions": "Submissions ({count})",
|
|
71
|
+
"submissionsTitle": "Submissions",
|
|
72
|
+
"noSubmissions": "No submissions yet.",
|
|
73
|
+
"redeliver": "Deliver again",
|
|
74
|
+
"forbidden": "You are not allowed to manage forms.",
|
|
75
|
+
"imported": "Imported {inserted}, skipped {skipped} existing.",
|
|
76
|
+
"invalidJson": "The JSON is not valid.",
|
|
77
|
+
"importHint": "Paste one form or a list of forms (each with slug, name and definition). Forms whose slug already exists are skipped.",
|
|
78
|
+
"definition": "Definition (JSON)",
|
|
79
|
+
"invalidDefinition": "The definition has problems",
|
|
80
|
+
"preview": "Preview",
|
|
81
|
+
"previewUnavailable": "Fix the definition to see a preview."
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineMenuItem, defineMenuSource, extendMenuItem } from "@frigear-nu/menu/utils";
|
|
2
|
+
export default defineMenuSource(() => {
|
|
3
|
+
const config = useRuntimeConfig().public.forms;
|
|
4
|
+
if (config?.pages?.admin) {
|
|
5
|
+
extendMenuItem("dashboard.account", defineMenuItem({
|
|
6
|
+
id: "forms-admin",
|
|
7
|
+
label: "Forms",
|
|
8
|
+
description: "Manage forms and their submissions.",
|
|
9
|
+
icon: "i-lucide-clipboard-list",
|
|
10
|
+
to: config.pages.admin,
|
|
11
|
+
tags: ["admin"],
|
|
12
|
+
order: 90
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
});
|