@webx-ui/module-inbox 0.9.0 → 0.10.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/{FormEditorPage-HsbIZcG_.js → FormEditorPage-CXYnpKAh.js} +261 -248
- package/dist/FormEditorPage-CXYnpKAh.js.map +1 -0
- package/dist/{InboxPage-Dd_8TGGg.js → InboxPage-PAtQ48A2.js} +2 -2
- package/dist/{InboxPage-Dd_8TGGg.js.map → InboxPage-PAtQ48A2.js.map} +1 -1
- package/dist/{StatusesPage-DKJmpZoG.js → StatusesPage-xVeRPm4u.js} +2 -2
- package/dist/{StatusesPage-DKJmpZoG.js.map → StatusesPage-xVeRPm4u.js.map} +1 -1
- package/dist/{SubmissionPage-BoE9jEhj.js → SubmissionPage-DI8jAaud.js} +2 -2
- package/dist/{SubmissionPage-BoE9jEhj.js.map → SubmissionPage-DI8jAaud.js.map} +1 -1
- package/dist/{index-Dxbb2lUM.js → index-BJ-mLCee.js} +668 -652
- package/dist/index-BJ-mLCee.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/dist/types.d.ts +2 -0
- package/package.json +3 -3
- package/dist/FormEditorPage-HsbIZcG_.js.map +0 -1
- package/dist/index-Dxbb2lUM.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FormEditorPage-HsbIZcG_.js","sources":["../src/FormAntispam.vue","../src/FormEmbed.vue","../src/FormGeneral.vue","../src/recipients.ts","../src/FormNotifications.vue","../src/FormEditorPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport {\n WxAlert,\n WxCard,\n WxFormItem,\n WxInputNumber,\n WxSelect,\n WxSwitch,\n WxText,\n} from '@webx-ui/core'\nimport { option } from './options'\nimport type { CaptchaProvider, CaptchaSite, FormOptions } from './types'\n\n/**\n * The four layers in front of the door (§7), as the three anybody would want to change.\n *\n * The origin check is not here: it is not a dial, it is a fact about where the site is served\n * from, and it lives in the configuration with the rest of the deployment. The captcha keys\n * are not here either — they are the site's, one pair for every form, and a secret repeated\n * in each form's settings is a secret scattered over rows (§5).\n *\n * What is here is what the site has, under the select: a reCAPTCHA key of the wrong kind is\n * the widget's \"Invalid key type\" and nothing else, and the select alone gives an editor no\n * way of knowing which kind the site expects.\n */\nconst options = defineModel<FormOptions>({ required: true })\n\nconst props = defineProps<{\n /** From the form as the server sent it; absent on a server older than this. */\n site?: Record<CaptchaProvider, CaptchaSite>\n}>()\n\nconst t = useTranslate('webx-inbox')\n\nconst honeypot = option<boolean>(options, 'antispam.honeypot', true)\nconst seconds = option<number>(options, 'antispam.min_seconds', 3)\nconst throttle = option<number>(options, 'antispam.throttle', 5)\nconst captcha = option<string>(options, 'antispam.captcha', 'off')\n\nconst captchas = computed(() => [\n { value: 'off', label: t('panel.captcha-off') },\n { value: 'recaptcha', label: t('panel.captcha-recaptcha') },\n { value: 'turnstile', label: t('panel.captcha-turnstile') },\n])\n\n/** The provider chosen and what the site has for it, or null for none. */\nconst chosen = computed(() => {\n const provider = captcha.value\n\n if (provider !== 'recaptcha' && provider !== 'turnstile') return null\n\n return { provider, site: props.site?.[provider] ?? null }\n})\n\nconst kind = computed(() => {\n const site = chosen.value?.site\n\n return site ? t(`panel.captcha-${chosen.value!.provider}-${site.type}`) : null\n})\n</script>\n\n<template>\n <wx-card>\n <wx-form-item :help=\"t('panel.honeypot-help')\">\n <wx-switch v-model=\"honeypot\" :label=\"t('panel.honeypot')\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.min-seconds')\" :help=\"t('panel.min-seconds-help')\">\n <wx-input-number v-model=\"seconds\" :min=\"0\" :max=\"120\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.throttle')\" :help=\"t('panel.throttle-help')\">\n <wx-input-number v-model=\"throttle\" :min=\"0\" :max=\"120\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.captcha')\" :help=\"t('panel.captcha-keys')\">\n <wx-select v-model=\"captcha\" :options=\"captchas\" />\n </wx-form-item>\n\n <wx-text v-if=\"kind\" size=\"sm\" tone=\"muted\">{{ kind }}</wx-text>\n\n <wx-alert\n v-if=\"chosen?.site && !chosen.site.configured\"\n type=\"warning\"\n variant=\"soft\"\n :closable=\"false\"\n :description=\"t('panel.captcha-unconfigured')\"\n />\n </wx-card>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { toast, WxAction, WxCard, WxTable, WxText, type TableColumn } from '@webx-ui/core'\nimport { useInboxMessages } from './i18n'\nimport type { InboxField, InboxForm } from './types'\n\n/**\n * How the form gets onto a page.\n *\n * Two answers and both are one line, because the module's whole public half is one tag (§10):\n * a template prints it, and a block type prints the same thing so that an editor never opens\n * a template at all.\n *\n * Under them, the names. A hidden field is filled in by the page it stands on — the address\n * somebody wrote from, the product they were looking at, the campaign that brought them — and\n * the only way to fill one in is to know what it is called (§2.6).\n *\n * Where a block's template lives depends on the site: with `module-blocks` it is written in\n * the panel's Blocks section, and a hint pointing at `resources/views/blocks/…` sent people\n * looking for a file that is not there. Without it, a block type is the site's own view.\n */\nconst props = defineProps<{ form: InboxForm; slug: string; fields: InboxField[] }>()\n\nuseInboxMessages()\nconst t = useTranslate('webx-inbox')\nconst admin = useAdmin()\n\nconst hasBlocks = computed(\n () => admin.state.manifest?.modules.some((module) => module.id === 'blocks') ?? false,\n)\n\nconst blade = computed(() => `<x-webx-inbox::form slug=\"${props.slug}\" />`)\n\nconst block = computed(() =>\n hasBlocks.value\n ? blade.value\n : `{{-- resources/views/blocks/contact.blade.php --}}\\n<x-webx-inbox::form slug=\"${props.slug}\" />`,\n)\n\n/** The first hidden field, so the example fills a name this form actually has. */\nconst hidden = computed(\n () => props.fields.find((field) => field.type === 'hidden' && field.is_enabled)?.key ?? 'page',\n)\n\nconst extras = computed(\n () =>\n `<x-webx-inbox::form slug=\"${props.slug}\" :values=\"['${hidden.value}' => url()->current()]\" />\\n` +\n `<x-webx-inbox::form slug=\"${props.slug}\" placement=\"footer\" />`,\n)\n\nconst columns = computed<TableColumn<InboxField>[]>(() => [\n { key: 'key', label: t('fields.name') },\n { key: 'title', label: t('fields.title') },\n { key: 'type', label: t('fields.type'), hideBelow: 520 },\n])\n\n/** Only the ones a page can actually fill in or a visitor can actually answer. */\nconst listed = computed(() => props.fields.filter((field) => field.is_enabled))\n\nasync function copy(text: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(text)\n toast.success(t('panel.copied'))\n } catch {\n // A browser that refuses the clipboard — an insecure origin, a denied permission — still\n // shows the line, and selecting it by hand is what anybody does next anyway.\n }\n}\n</script>\n\n<template>\n <div class=\"wx-inbox-embed\">\n <wx-card :title=\"t('panel.embed-blade')\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('panel.embed-blade-help') }}</wx-text>\n\n <div class=\"wx-inbox-embed__snippet\">\n <code>{{ blade }}</code>\n <wx-action icon=\"copy\" :title=\"t('panel.copy')\" @click=\"copy(blade)\" />\n </div>\n </wx-card>\n\n <wx-card :title=\"t('panel.embed-block')\">\n <wx-text size=\"sm\" tone=\"muted\">\n {{ t(hasBlocks ? 'panel.embed-block-panel-help' : 'panel.embed-block-help') }}\n </wx-text>\n\n <div class=\"wx-inbox-embed__snippet\">\n <code>{{ block }}</code>\n <wx-action icon=\"copy\" :title=\"t('panel.copy')\" @click=\"copy(block)\" />\n </div>\n </wx-card>\n\n <wx-card :title=\"t('panel.embed-extras')\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('panel.embed-extras-help') }}</wx-text>\n\n <div class=\"wx-inbox-embed__snippet\">\n <code>{{ extras }}</code>\n <wx-action icon=\"copy\" :title=\"t('panel.copy')\" @click=\"copy(extras)\" />\n </div>\n </wx-card>\n\n <wx-card :title=\"t('panel.embed-names')\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('panel.embed-names-help') }}</wx-text>\n\n <wx-table\n :data=\"listed\"\n :columns=\"columns\"\n row-key=\"id\"\n flush\n :empty-text=\"t('fields.no-fields')\"\n >\n <template #cell-key=\"{ row }\">\n <wx-text mono size=\"sm\">fields[{{ row.key }}]</wx-text>\n </template>\n\n <template #cell-title=\"{ row }\">\n {{ Object.values(row.title)[0] ?? row.key }}\n </template>\n\n <template #cell-type=\"{ row }\">\n {{ t(`fields.type-${row.type}`) }}\n </template>\n </wx-table>\n </wx-card>\n </div>\n</template>\n\n<style scoped>\n.wx-inbox-embed {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n}\n\n.wx-inbox-embed__snippet {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n margin-top: var(--wx-space-12);\n padding: var(--wx-space-10) var(--wx-space-12);\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-md);\n background: var(--wx-bg-subtle);\n}\n\n.wx-inbox-embed__snippet code {\n flex: 1 1 auto;\n min-width: 0;\n overflow-x: auto;\n white-space: pre;\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-sm);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { emptyLink, useTranslate, WxLinkPicker, type LinkValue } from '@webx-ui/module-admin'\nimport { WxCard, WxFormItem, WxInput, WxSwitch, type LocalizedValue } from '@webx-ui/core'\nimport LocalizedRichText from './LocalizedRichText.vue'\nimport { option } from './options'\nimport type { FormOptions } from './types'\n\n/**\n * What the form is, and what it says once it has been sent.\n *\n * The two halves are on two cards because they are read at two different times: the first is\n * set up once, the second is the sentence a visitor sees and is rewritten whenever the\n * marketing does.\n */\ndefineProps<{ errors: Record<string, string[]> }>()\n\nconst settings = defineModel<{ slug: string; title: LocalizedValue; is_enabled: boolean }>(\n 'settings',\n { required: true },\n)\n\nconst options = defineModel<FormOptions>('options', { required: true })\n\nconst t = useTranslate('webx-inbox')\n\nconst heading = option<LocalizedValue>(options, 'thank-you.heading', {})\nconst text = option<LocalizedValue>(options, 'thank-you.text', {})\nconst submit = option<LocalizedValue>(options, 'design.submit-text', {})\n/*\n * Where the visitor goes instead of the thank-you: a link like a menu item's — a record of the\n * site, which the server opens in the visitor's language, or an address. Nothing chosen is\n * \"Nowhere\", the thank-you in place of the form. A plain string is what a form saved before the\n * choice holds, and it is an address.\n */\nconst stored = option<LinkValue | string | null>(options, 'redirect', null)\n\nconst redirect = computed<LinkValue | null>({\n get: (): LinkValue => {\n const value = stored.value\n\n if (typeof value === 'string') return { ...emptyLink(), target: 'url', url: value }\n\n return value ?? { ...emptyLink(), target: 'none' }\n },\n set: (value: LinkValue | null) => {\n stored.value = value === null || value.target === 'none' ? null : value\n },\n})\n</script>\n\n<template>\n <div class=\"wx-inbox-general\">\n <wx-card>\n <wx-form-item :label=\"t('panel.title')\" :error=\"errors.title?.[0]\" required>\n <wx-input v-model=\"settings.title\" localized />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('panel.slug')\"\n :help=\"t('panel.slug-help')\"\n :error=\"errors.slug?.[0]\"\n required\n >\n <wx-input v-model=\"settings.slug\" placeholder=\"contact\" />\n </wx-form-item>\n\n <wx-form-item :help=\"t('panel.is-enabled-help')\">\n <wx-switch v-model=\"settings.is_enabled\" :label=\"t('panel.is-enabled')\" />\n </wx-form-item>\n </wx-card>\n\n <wx-card :title=\"t('panel.after-sending')\">\n <wx-form-item :label=\"t('panel.submit-text')\">\n <wx-input v-model=\"submit\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.thank-you-heading')\">\n <wx-input v-model=\"heading\" localized />\n </wx-form-item>\n\n <!-- Printed raw on the site, which is why it is written where formatting is possible. -->\n <wx-form-item :label=\"t('panel.thank-you-text')\">\n <localized-rich-text v-model=\"text\" />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('panel.redirect')\"\n :help=\"t('panel.redirect-help')\"\n :error=\"errors['options.redirect']?.[0]\"\n >\n <!-- A redirect is not clicked: a new tab and rel mean nothing for it. -->\n <wx-link-picker v-model=\"redirect\" :attributes=\"false\" />\n </wx-form-item>\n </wx-card>\n </div>\n</template>\n\n<style scoped>\n.wx-inbox-general {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-16);\n}\n</style>\n","import type { Recipient, RecipientProblem, RecipientState } from './types'\n\n/**\n * Whether a letter would reach somebody, judged on the recipients as they stand in the editor.\n *\n * The server says it for what was saved (`InboxForm.recipients`): who is deleted, who is\n * switched off. The editor has to say it before a save too, so this reads the list being edited\n * and borrows the server's verdict only for the administrators it already knows about — one\n * picked a moment ago from the list of people who may read the section is somebody who exists.\n */\n\n/** Loose on purpose: the server checks the address for real, this only spots an empty line. */\nconst ADDRESS = /^[^\\s@]+@[^\\s@]+$/\n\nexport function isAdmin(recipient: Recipient): recipient is { admin_id: number } {\n return 'admin_id' in recipient\n}\n\n/** Why this recipient would not get a letter, or null when it would. */\nexport function problemOf(\n recipient: Recipient,\n reported: RecipientState[] = [],\n): RecipientProblem | null {\n if (isAdmin(recipient)) {\n const known = reported.find(\n (one) => one.type === 'admin' && one.admin_id === recipient.admin_id,\n )\n\n return known?.problem ?? null\n }\n\n return ADDRESS.test((recipient.email ?? '').trim()) ? null : 'invalid_email'\n}\n\n/** Whether anybody on the list would be written to. */\nexport function reachesAnybody(\n recipients: Recipient[] | undefined,\n reported: RecipientState[] = [],\n): boolean {\n return (recipients ?? []).some((recipient) => problemOf(recipient, reported) === null)\n}\n","<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { useAdmin, useTranslate, WxRelationsField } from '@webx-ui/module-admin'\nimport {\n confirm,\n WxAction,\n WxAlert,\n WxButton,\n WxIcon,\n WxCard,\n WxFormItem,\n WxInput,\n WxLink,\n WxRadioGroup,\n WxSelect,\n WxSpace,\n WxText,\n} from '@webx-ui/core'\nimport { createInboxApi } from './api'\nimport { option } from './options'\nimport { isAdmin, problemOf } from './recipients'\nimport type {\n FormOptions,\n InboxField,\n InboxRecipient,\n NotifyVia,\n Recipient,\n RecipientState,\n} from './types'\n\n/**\n * Who hears that something arrived (§9) — one card, and one way of hearing it.\n *\n * On top, the field whose answer is the visitor's address: both ways use it — the letter as its\n * Reply-To, a channel as `{{reply_to}}`. Below it the choice of how, when the notifications\n * module is installed, and only the half that was chosen. A submission goes one way: a letter to\n * the addresses and a message in a chat about the same thing was the same news twice, and a form\n * set up for one way kept sending the other because nobody had emptied it. The half not chosen is\n * kept as it is on the form, so switching back finds it where it was.\n *\n * Two kinds of recipient of a letter, and they are not interchangeable. An administrator is\n * written to at the address on their account and in the language they keep the panel in; a typed\n * address is written to in the language the submission came in. The administrators offered are\n * the ones who may open a submission — a notification is a link, and sending one to somebody who\n * gets a 403 looks like the panel is broken rather than like the form is.\n *\n * The channels are asked of the event's target on the server (NOTIFICATIONS §15.2), so this\n * package needs nothing of that module's code; a site without it sees the letter and nothing else.\n */\nconst props = defineProps<{\n fields: InboxField[]\n errors: Record<string, string[]>\n /** What the server said about the saved list: who is deleted or switched off. */\n reported?: RecipientState[]\n /** Whether the notifications module is installed — the server says so on the form. */\n notifications?: boolean\n /** How the form tells, as the server worked it out for a form saved before the choice. */\n via?: NotifyVia\n}>()\n\nconst options = defineModel<FormOptions>({ required: true })\n\nconst context = useAdmin()\nconst api = createInboxApi(context)\nconst t = useTranslate('webx-inbox')\n\nconst admins = ref<InboxRecipient[]>([])\n\nconst recipients = option<Recipient[]>(options, 'recipients', [])\nconst emailField = option<string>(options, 'email_field', '')\nconst channels = option<number[]>(options, 'notify_channels', [])\n\n/*\n * The form's own choice, or what the server worked out for one saved before there was a choice —\n * written into the options the moment somebody picks, so a form that loses its channels later\n * does not quietly change its way with them.\n */\nconst via = computed<NotifyVia>({\n get: () => options.value.notify_via ?? props.via ?? 'email',\n set: (value) => {\n options.value = { ...options.value, notify_via: value }\n },\n})\n\nconst viaOptions = computed(() => [\n { value: 'email', label: t('panel.via-email') },\n { value: 'channels', label: t('panel.via-channels') },\n])\n\n/* Without the module there is no choice to make, and the letter is what goes. */\nconst byLetter = computed(() => !props.notifications || via.value === 'email')\n\nconst router = useRouter()\n\n/* The channels are set up in their own section; only somebody who may do it gets the way there. */\nconst canSetUp = computed(() => context.can('notifications.manage'))\nconst channelsLink = computed(() => `${context.basePath}/notifications`.replace(/\\/{2,}/g, '/'))\n\n/* Inside the panel, without loading it again: the address is for a middle click. */\nfunction setUpChannels(event: MouseEvent): void {\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.button !== 0) return\n\n event.preventDefault()\n void router.push('/notifications')\n}\n\nonMounted(async () => {\n try {\n admins.value = await api.recipients()\n } catch {\n // Only the shortcut is gone: an address can still be typed, and that is the half of this\n // that has to work.\n }\n})\n\nconst adminOptions = computed(() =>\n admins.value.map((admin) => ({ value: admin.id, label: `${admin.name} · ${admin.email}` })),\n)\n\n/**\n * The fields that could hold the visitor's address, plus the option of naming none — by their\n * label, which is what the editor wrote; the machine name is beside it only to tell two alike.\n */\nconst emailOptions = computed(() => [\n { value: '', label: t('panel.no-email-field') },\n ...props.fields\n .filter((field) => field.type === 'email')\n .map((field) => ({ value: field.key, label: fieldLabel(field) })),\n])\n\nfunction fieldLabel(field: InboxField): string {\n const title = Object.values(field.title ?? {}).find(\n (line): line is string => typeof line === 'string' && line.trim() !== '',\n )\n\n if (title === undefined) return field.name ?? field.key\n\n return field.name ? `${title} · ${field.name}` : title\n}\n\n/*\n * Only the administrators' problems are said beside the row: an address being typed is not\n * wrong yet, and the server refuses a bad one on save in words of its own.\n */\nfunction adminProblem(recipient: Recipient): string | null {\n if (!isAdmin(recipient)) return null\n\n const problem = problemOf(recipient, props.reported)\n\n return problem === null ? null : t(`panel.recipient-${problem}`)\n}\n\nfunction add(recipient: Recipient): void {\n recipients.value = [...recipients.value, recipient]\n}\n\n/**\n * A recipient goes behind a question, unless there is nothing there to lose.\n *\n * The row that was just added and never filled in is a blank line, and asking about a blank\n * line teaches the reader to click through the question without reading it — which is the\n * one thing a confirmation must not do.\n */\nasync function remove(index: number): Promise<void> {\n const recipient = recipients.value[index]\n const written =\n recipient !== undefined && (isAdmin(recipient) || (recipient.email ?? '').trim() !== '')\n\n if (written) {\n const agreed = await confirm({\n title: t('panel.remove-recipient-title'),\n message: t('panel.remove-recipient-text'),\n confirmText: t('panel.remove'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n }\n\n recipients.value = recipients.value.filter((_, at) => at !== index)\n}\n\nfunction setAdmin(index: number, id: number): void {\n recipients.value = recipients.value.map((one, at) => (at === index ? { admin_id: id } : one))\n}\n\nfunction setEmail(index: number, email: string): void {\n recipients.value = recipients.value.map((one, at) => (at === index ? { email } : one))\n}\n</script>\n\n<template>\n <wx-card\n class=\"wx-inbox-notifications\"\n :title=\"props.notifications ? undefined : t('panel.recipients')\"\n >\n <wx-form-item\n :label=\"t('panel.email-field')\"\n :help=\"\n props.notifications ? t('panel.email-field-help-channels') : t('panel.email-field-help')\n \"\n :error=\"errors['options.email_field']?.[0]\"\n >\n <wx-select v-model=\"emailField\" :options=\"emailOptions\" />\n </wx-form-item>\n\n <wx-form-item v-if=\"props.notifications\" :label=\"t('panel.notify-via')\">\n <wx-radio-group v-model=\"via\" :options=\"viaOptions\" inline />\n </wx-form-item>\n\n <section v-if=\"byLetter\" class=\"wx-inbox-notifications__way\" data-way=\"email\">\n <wx-text v-if=\"props.notifications\" weight=\"medium\">{{ t('panel.recipients') }}</wx-text>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('panel.recipients-help') }}</wx-text>\n\n <wx-alert\n v-if=\"errors['options.recipients']\"\n type=\"danger\"\n variant=\"soft\"\n :description=\"errors['options.recipients']?.[0]\"\n />\n\n <div class=\"wx-inbox-recipients\">\n <wx-text v-if=\"recipients.length === 0\" size=\"sm\" tone=\"placeholder\">\n {{ t('panel.no-recipients') }}\n </wx-text>\n\n <div\n v-for=\"(recipient, index) in recipients\"\n :key=\"index\"\n class=\"wx-inbox-recipients__item\"\n >\n <div class=\"wx-inbox-recipients__row\">\n <wx-select\n v-if=\"isAdmin(recipient)\"\n :model-value=\"recipient.admin_id\"\n :options=\"adminOptions\"\n @update:model-value=\"(id) => setAdmin(index, Number(id))\"\n />\n <wx-input\n v-else\n :model-value=\"recipient.email\"\n type=\"email\"\n placeholder=\"sales@example.com\"\n @update:model-value=\"(email) => setEmail(index, String(email))\"\n />\n\n <!-- Red, like every other way of taking something away in the panel: the colour is\n what tells the two buttons of a row apart before either is read. -->\n <wx-action\n icon=\"trash\"\n tone=\"danger\"\n :title=\"t('panel.remove')\"\n @click=\"remove(index)\"\n />\n </div>\n <wx-text v-if=\"adminProblem(recipient)\" size=\"sm\" tone=\"danger\">\n {{ adminProblem(recipient) }}\n </wx-text>\n </div>\n </div>\n\n <wx-space size=\"sm\">\n <wx-button\n variant=\"outline\"\n icon=\"user\"\n :disabled=\"adminOptions.length === 0\"\n @click=\"add({ admin_id: Number(adminOptions[0]?.value) })\"\n >\n {{ t('panel.add-admin') }}\n </wx-button>\n <wx-button variant=\"outline\" @click=\"add({ email: '' })\">\n <template #icon><wx-icon name=\"mail\" /></template>\n {{ t('panel.add-email') }}\n </wx-button>\n </wx-space>\n </section>\n\n <section v-else class=\"wx-inbox-notifications__way\" data-way=\"channels\">\n <wx-text weight=\"medium\">{{ t('panel.notify-through') }}</wx-text>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('panel.notify-through-help') }}</wx-text>\n\n <div class=\"wx-inbox-channels\">\n <wx-relations-field\n v-model=\"channels\"\n target=\"notify-inbox-submission\"\n :sortable=\"false\"\n :add-text=\"t('panel.pick-channels')\"\n :empty-text=\"t('panel.no-channels')\"\n :none-text=\"t('panel.no-channels-yet')\"\n />\n <wx-link v-if=\"canSetUp\" :href=\"channelsLink\" size=\"sm\" @click=\"setUpChannels\">{{\n t('panel.set-up-channels')\n }}</wx-link>\n </div>\n </section>\n </wx-card>\n</template>\n\n<style scoped>\n/*\n * One way of telling under the shared field and the choice: its title, its help, its list. The\n * rows of a way sit closer than the form items above them — they are one answer, not three.\n */\n.wx-inbox-notifications__way {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n}\n\n/*\n * As wide as a field, because that is what each row is: an address is typed into it and read\n * back off it. The rows are not form items — one of them is a select, the next an input, and\n * both carry a bin — so the cap that a form item applies has to be said here.\n */\n.wx-inbox-recipients {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n margin-block: var(--wx-space-8) var(--wx-space-12);\n max-width: var(--wx-field-max-width, 640px);\n}\n\n.wx-inbox-channels {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--wx-space-8);\n margin-block-start: var(--wx-space-8);\n max-width: var(--wx-field-max-width, 640px);\n}\n\n.wx-inbox-channels > :first-child {\n align-self: stretch;\n}\n\n.wx-inbox-recipients__item {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n}\n\n.wx-inbox-recipients__row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n}\n\n.wx-inbox-recipients__row > :first-child {\n flex: 1 1 auto;\n min-width: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, reactive, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { useAdmin, useErrorText, useTranslate, WxScreenHead } from '@webx-ui/module-admin'\nimport {\n localizedValue,\n toast,\n useLocales,\n WxActionBar,\n WxAlert,\n WxBadge,\n WxButton,\n WxCard,\n WxSkeleton,\n WxTab,\n WxTabs,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport FieldList from './FieldList.vue'\nimport FormAntispam from './FormAntispam.vue'\nimport FormEmbed from './FormEmbed.vue'\nimport FormGeneral from './FormGeneral.vue'\nimport FormNotifications from './FormNotifications.vue'\nimport { createInboxApi } from './api'\nimport { reachesAnybody } from './recipients'\nimport { useInboxMessages } from './i18n'\nimport type { FormOptions, InboxField, InboxForm, Recipient } from './types'\n\n/**\n * One form: what it is called, what it asks, who hears about it, and how it is put on a page.\n *\n * Five tabs and one save. The fields are the exception — a question is written in its own\n * dialog and saved on its own, because it is a row with an identity (§2.1) and because a\n * dragged order that waited for a save button would be an order nobody trusts.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/inbox' })\n\nconst context = useAdmin()\nconst api = createInboxApi(context)\nconst route = useRoute()\nconst router = useRouter()\nconst locales = useLocales()\nuseInboxMessages()\n\nconst t = useTranslate('webx-inbox')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst form = ref<InboxForm | null>(null)\nconst fields = ref<InboxField[]>([])\nconst loading = ref(true)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst settings = reactive<{ slug: string; title: LocalizedValue; is_enabled: boolean }>({\n slug: '',\n title: {},\n is_enabled: true,\n})\n\nconst options = ref<FormOptions>({})\n\nconst TABS = ['general', 'fields', 'notifications', 'antispam', 'embed']\n\n/* Opened on the tab the address names, so the audit's \"open\" lands on the recipients. */\nconst tab = ref(TABS.includes(String(route.query.tab)) ? String(route.query.tab) : 'general')\n\n/*\n * Said above the tabs and not only inside Notifications: a form that tells nobody works in\n * every way somebody can see — the site thanks the visitor, the panel keeps the enquiry — so\n * the only place the gap shows is a line nobody went looking for.\n */\nconst silent = computed(\n () =>\n settings.is_enabled &&\n !reachesAnybody(options.value.recipients as Recipient[] | undefined, form.value?.recipients) &&\n !channelsSend.value,\n)\n\n/*\n * A channel chosen and switched on tells somebody too. One chosen since the form was loaded is\n * taken at its word until the save says otherwise: the server names only what was saved.\n */\nconst channelsSend = computed(() => {\n if (form.value?.notifications !== true) return false\n\n const reported = form.value.channels ?? []\n\n return (options.value.notify_channels ?? []).some(\n (id) => reported.find((channel) => channel.id === id)?.sends ?? true,\n )\n})\n\nconst canManage = computed(() => context.can('inbox.manage'))\n\nconst id = computed(() => Number(route.params.id))\n\nconst name = computed(() =>\n form.value === null ? '' : localizedValue(settings.title, locales.active.value, settings.slug),\n)\n\n/** Where a submission would come back to: the section, with this form already chosen. */\nconst backTo = computed(() => `${props.base}?form=${id.value}`)\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n const loaded = await api.form(id.value)\n\n form.value = loaded\n fields.value = loaded.fields ?? []\n settings.slug = loaded.slug\n settings.title = { ...loaded.title }\n settings.is_enabled = loaded.is_enabled\n options.value = { ...loaded.options }\n } catch (error) {\n toast.danger(message(error))\n void router.replace(props.base)\n } finally {\n loading.value = false\n }\n}\n\nwatch(id, () => void load(), { immediate: true })\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n try {\n const saved = await api.saveForm(id.value, {\n slug: settings.slug.trim(),\n title: settings.title,\n is_enabled: settings.is_enabled,\n options: options.value,\n })\n\n form.value = saved\n toast.success(t('panel.saved'))\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('panel.failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <!--\n Not a screen that fills its column, whatever the first draft said. `data-wx-fill` makes the\n screen exactly as tall as the window, and nothing inside these tabs scrolls on its own — so\n a form longer than the window grew straight through the box, and the save bar, which is the\n next thing in the column, was drawn across the middle of it with fields still going on\n underneath. What this screen wants is the ordinary thing: the page scrolls, the bar sticks\n to the bottom of it, and `WxMain` gives the screen a floor to push that bar down to.\n -->\n <div class=\"wx-inbox-editor\">\n <!-- On a card, like everything else on this screen: a bare skeleton flush against the\n page is a shape the form that follows it never takes, so the screen jumps twice —\n once when the card appears around it, once when the fields land inside. -->\n <wx-card v-if=\"loading\">\n <wx-skeleton title :rows=\"8\" />\n </wx-card>\n\n <template v-else-if=\"form\">\n <wx-screen-head\n class=\"wx-inbox-editor__head\"\n :title=\"name\"\n :subtitle=\"form.slug\"\n :back=\"backTo\"\n :back-label=\"t('panel.forms')\"\n >\n <template #title-after>\n <wx-badge v-if=\"!settings.is_enabled\" type=\"default\">{{ t('panel.off') }}</wx-badge>\n </template>\n </wx-screen-head>\n\n <!--\n Every tab is mounted at once and the hidden ones keep their state: somebody who wrote\n a thank-you, went to look at the fields and came back should find it still written.\n -->\n <wx-alert\n v-if=\"silent\"\n class=\"wx-inbox-editor__silent\"\n type=\"warning\"\n variant=\"soft\"\n :closable=\"false\"\n :title=\"t('panel.notifies-nobody')\"\n :description=\"t('panel.notifies-nobody-text')\"\n />\n\n <wx-tabs v-model=\"tab\" class=\"wx-inbox-editor__tabs\" keep-alive>\n <wx-tab value=\"general\" :label=\"t('panel.tab-general')\">\n <form-general v-model:settings=\"settings\" v-model:options=\"options\" :errors=\"errors\" />\n </wx-tab>\n\n <wx-tab value=\"fields\" :label=\"t('panel.tab-fields')\">\n <field-list v-model=\"fields\" :form=\"form\" :can-manage=\"canManage\" />\n </wx-tab>\n\n <wx-tab value=\"notifications\" :label=\"t('panel.tab-notifications')\">\n <form-notifications\n v-model=\"options\"\n :fields=\"fields\"\n :errors=\"errors\"\n :reported=\"form.recipients\"\n :notifications=\"form.notifications\"\n :via=\"form.notify_via\"\n />\n </wx-tab>\n\n <wx-tab value=\"antispam\" :label=\"t('panel.tab-antispam')\">\n <form-antispam v-model=\"options\" :site=\"form.captcha\" />\n </wx-tab>\n\n <wx-tab value=\"embed\" :label=\"t('panel.tab-embed')\">\n <form-embed :form=\"form\" :slug=\"settings.slug\" :fields=\"fields\" />\n </wx-tab>\n </wx-tabs>\n\n <wx-action-bar v-if=\"canManage\">\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">{{ t('panel.save') }}</wx-button>\n </wx-action-bar>\n </template>\n </div>\n</template>\n\n<style scoped>\n.wx-inbox-editor {\n display: flex;\n flex-direction: column;\n /* The panel's own step, which is smaller on a phone than on a desktop. */\n gap: var(--wx-gap, var(--wx-space-16));\n}\n\n/* The tabs take what is left of the column, so the save bar under them is at its bottom\n rather than under the last field. Nothing inside them scrolls on its own — the page does. */\n/* The address it posts to, in the type an address is written in. */\n.wx-inbox-editor__head :deep(.wx-screen-head__subtitle) {\n font-family: var(--wx-font-family-mono);\n}\n\n.wx-inbox-editor__tabs {\n flex: 1 1 auto;\n}\n</style>\n"],"names":["options","_useModel","__props","props","t","useTranslate","honeypot","option","seconds","throttle","captcha","captchas","computed","chosen","provider","kind","site","_createBlock","_unref","WxCard","_createVNode","WxFormItem","WxSwitch","$event","WxInputNumber","WxSelect","WxText","WxAlert","useInboxMessages","admin","useAdmin","hasBlocks","module","blade","block","hidden","field","extras","columns","listed","copy","text","toast","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","WxAction","_cache","_createTextVNode","_toDisplayString","_hoisted_3","_hoisted_4","WxTable","_withCtx","row","settings","heading","submit","stored","redirect","value","emptyLink","WxInput","LocalizedRichText","WxLinkPicker","ADDRESS","isAdmin","recipient","problemOf","reported","one","reachesAnybody","recipients","context","api","createInboxApi","admins","ref","emailField","channels","via","viaOptions","byLetter","router","useRouter","canSetUp","channelsLink","setUpChannels","event","onMounted","adminOptions","emailOptions","fieldLabel","title","line","adminProblem","problem","add","remove","index","confirm","_","at","setAdmin","id","setEmail","email","WxRadioGroup","_Fragment","_renderList","WxSpace","WxButton","WxIcon","_hoisted_5","WxRelationsField","WxLink","route","useRoute","locales","useLocales","message","useErrorText","form","fields","loading","saving","errors","reactive","tab","silent","channelsSend","channel","canManage","name","localizedValue","backTo","load","loaded","error","watch","save","saved","body","WxSkeleton","WxScreenHead","WxBadge","WxTabs","WxTab","FormGeneral","FieldList","FormNotifications","FormAntispam","FormEmbed","WxActionBar"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-Dxbb2lUM.js","sources":["../src/messages.ts","../src/i18n.ts","../src/FormField.vue","../src/module.ts","../src/api.ts","../src/options.ts","../src/SubmissionCreateDialog.vue","../src/SubmissionList.vue","../src/LocalizedRichText.vue","../src/types.ts","../src/FieldDialog.vue","../src/FieldList.vue","../src/index.ts"],"sourcesContent":["import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * What this package says, in English — the same keys `webx-ui/module-inbox` ships as\n * `lang/en/*.php`, so a key never reaches the screen while the dictionary is still on its way.\n *\n * The server's dictionary wins over these. There is no second dictionary inside this package:\n * a module translated into ten languages with an English dialog in the middle of it is worse\n * than one that is not translated at all, and the only way to notice that gap is to compare\n * the two lists — which `messages.test.ts` does.\n *\n * Placeholders are Laravel's, `:like-this`: the same line is read from a PHP file and from\n * here, and only one of the two spellings can be right.\n */\nexport const inboxMessages: Record<string, Messages> = {\n module: {\n title: 'Inbox',\n },\n\n panel: {\n forms: 'Forms',\n 'new-form': 'New form',\n 'no-forms': 'No forms yet.',\n 'no-forms-help': 'A form is a few questions and a list of who hears the answers.',\n unread: 'Unread',\n submissions: 'Submissions in all',\n off: 'Off',\n settings: 'Settings',\n duplicate: 'Duplicate',\n delete: 'Delete',\n 'delete-form-title': 'Delete “:form”?',\n 'delete-form-text': 'The form and its questions go. This cannot be undone.',\n cancel: 'Cancel',\n save: 'Save',\n saved: 'Saved.',\n deleted: 'Deleted.',\n duplicated: 'A copy was made.',\n failed: 'Some values were not accepted. Check the highlighted fields.',\n 'reorder-failed': 'The new order could not be saved.',\n reorder: 'Reorder',\n 'new-form-title': 'New form',\n title: 'Title',\n slug: 'Address',\n 'slug-help': 'What the form is called on the site and where it posts.',\n 'is-enabled': 'Active',\n 'is-enabled-help': 'A form that is off is neither drawn nor accepted.',\n 'tab-general': 'General',\n 'tab-fields': 'Fields',\n 'tab-notifications': 'Notifications',\n 'tab-antispam': 'Antispam',\n 'tab-embed': 'Embedding',\n 'after-sending': 'After sending',\n 'thank-you-heading': 'Heading',\n 'thank-you-text': 'Text',\n 'submit-text': 'Text on the button',\n redirect: 'Instead of the thank-you, go to',\n 'redirect-help':\n 'A page of the site opens in the language the form was sent in, or in the main one when it has no translation. Nowhere — the thank-you takes the form’s place.',\n recipients: 'Who is written to',\n 'recipients-help': 'Nobody here means nobody is told. The submission is kept either way.',\n 'no-recipients': 'Nobody yet.',\n 'add-admin': 'Add an administrator',\n 'add-email': 'Add an address',\n remove: 'Remove',\n 'remove-recipient-title': 'Remove this recipient?',\n 'remove-recipient-text': 'They stop being told when something arrives.',\n 'email-field': 'Field with the visitor’s address',\n 'email-field-help': 'A reply to the letter goes to this address.',\n 'email-field-help-channels':\n 'A reply to the letter goes to this address; in channels it is {{reply_to}}.',\n 'notify-via': 'How to tell about a submission',\n 'via-email': 'By letter to addresses',\n 'via-channels': 'Through notification channels',\n 'notify-through': 'Notify through',\n 'notify-through-help':\n 'Channels of the Notifications section: Telegram, e-mail and others. A submission from the site goes to each of them by its own template.',\n 'pick-channels': 'Add a channel',\n 'no-channels': 'No channels chosen.',\n 'no-channels-yet': 'No channels yet.',\n 'set-up-channels': 'Set up channels',\n channels: 'Channels',\n 'channels-none': 'Nothing was sent through channels.',\n 'channels-failed': 'A channel did not deliver the notification',\n 'no-email-field': 'None',\n honeypot: 'Hidden field',\n 'honeypot-help': 'A field nobody sees and a robot fills in.',\n 'min-seconds': 'Fastest a person could send it, in seconds',\n 'min-seconds-help': 'Zero switches the check off.',\n throttle: 'Sends a minute from one address',\n 'throttle-help': 'Zero switches the limit off.',\n captcha: 'Captcha',\n 'captcha-keys':\n 'The keys are not set in the panel but in the site’s .env: WEBX_INBOX_RECAPTCHA_KEY / WEBX_INBOX_RECAPTCHA_SECRET for reCAPTCHA, WEBX_INBOX_TURNSTILE_KEY / WEBX_INBOX_TURNSTILE_SECRET for Turnstile.',\n 'captcha-off': 'Off',\n 'captcha-recaptcha': 'reCAPTCHA',\n 'captcha-turnstile': 'Turnstile',\n 'captcha-unconfigured':\n 'The site has no keys for this captcha yet, so the form refuses every submission until they are in its .env.',\n 'captcha-recaptcha-checkbox':\n 'This site draws reCAPTCHA as the “I’m not a robot” checkbox, so its keys must be v2 Checkbox keys. To run it invisibly, create v2 Invisible keys and set WEBX_INBOX_RECAPTCHA_TYPE=invisible.',\n 'captcha-recaptcha-invisible':\n 'This site runs reCAPTCHA invisibly when the form is sent, so its keys must be v2 Invisible keys (WEBX_INBOX_RECAPTCHA_TYPE=invisible).',\n 'captcha-recaptcha-v3':\n 'This site runs reCAPTCHA v3: no widget, only a score checked when the form is sent, so its keys must be v3 keys (WEBX_INBOX_RECAPTCHA_TYPE=v3).',\n 'captcha-turnstile-checkbox':\n 'This site draws Turnstile when the page opens, and a Managed widget shows visitors “Verify you are human”. To run it invisibly, set WEBX_INBOX_TURNSTILE_MODE=invisible and the widget mode to Invisible in Cloudflare.',\n 'captcha-turnstile-invisible':\n 'This site runs Turnstile invisibly when the form is sent. Set the widget mode to Invisible in Cloudflare: a Managed widget still shows a challenge when it decides one is needed.',\n 'embed-blade': 'On a page of the site',\n 'embed-blade-help':\n 'Put the tag where the form belongs. The markup is published and rewritten by the site.',\n 'embed-block': 'In the block builder',\n 'embed-block-help':\n 'A block type prints the same tag, so an editor drops the form in without touching a template.',\n 'embed-block-panel-help':\n 'On this site block templates are written in the panel, in Blocks: put the same tag into the template of a block there.',\n 'embed-extras': 'Filled in and placed',\n 'embed-extras-help':\n ':values fills the hidden fields by their name; placement names where the form stands, so the submission remembers it and the site can style each place.',\n loading: 'Loading…',\n 'embed-names': 'Names of the fields',\n 'embed-names-help':\n 'A hidden field is filled in by the page: the address it was sent from, a product, a campaign.',\n copy: 'Copy',\n copied: 'Copied.',\n statuses: 'Statuses',\n 'new-status': 'New status',\n 'status-key': 'Key',\n 'status-color': 'Colour',\n 'status-default': 'Given to a new submission',\n 'status-spam': 'Spam',\n 'status-closed': 'Nothing further expected',\n 'in-use': 'In use',\n 'delete-status-title': 'Delete “:status”?',\n 'delete-status-text': 'Only a status nothing is in can go.',\n 'no-statuses': 'No statuses yet.',\n 'color-default': 'Grey',\n 'color-primary': 'Blue',\n 'color-success': 'Green',\n 'color-warning': 'Amber',\n 'color-info': 'Cyan',\n 'color-danger': 'Red',\n\n // The submissions: the list on the right of the section, and one of them opened.\n id: 'ID',\n received: 'Received',\n status: 'Status',\n assignee: 'Assignee',\n unassigned: 'Nobody',\n 'submissions-empty': 'Nothing has come in through this form yet.',\n 'search-submissions': 'Search the answers',\n 'view-all': 'All',\n 'view-unread': 'Unread',\n export: 'Export',\n 'new-submission': 'New submission',\n 'new-submission-title': 'A submission by hand',\n 'new-submission-help':\n 'For one that came by telephone or on paper. It is written the same way and lands in the same list.',\n created: 'Added.',\n submission: 'Submission :id',\n previous: 'Previous',\n next: 'Next',\n 'mark-read': 'Mark as read',\n 'mark-unread': 'Mark as unread',\n 'delete-submission-title': 'Delete this submission?',\n 'delete-submission-text': 'It goes with its attachments. This cannot be undone.',\n selected: ':count selected',\n 'move-to': 'Move to',\n 'delete-selected-title': 'Delete :count submissions?',\n answers: 'Answers',\n attachments: 'Attachments',\n details: 'Details',\n 'meta-page': 'Page',\n 'meta-placement': 'Placement',\n 'any-placement': 'Any placement',\n 'placement-none': 'Not specified',\n 'meta-referrer': 'Came from',\n 'meta-ip': 'Address',\n 'meta-agent': 'Browser',\n 'meta-locale': 'Language',\n 'meta-source': 'How it arrived',\n 'source-web': 'From the site',\n 'source-panel': 'Typed in',\n notified: 'The notification went out',\n 'not-notified': 'No notification was sent',\n 'notify-failed': 'The notification could not be sent',\n reply: 'Reply by mail',\n log: 'What has happened',\n 'event-created': 'Arrived',\n 'event-status': 'Status: :to',\n 'event-assignee': 'Given to :to',\n 'event-unassigned': 'Left to nobody',\n 'event-note': 'A note was added',\n 'event-notified': 'The notification went out',\n 'event-handled': 'Handed to :name',\n 'event-handler-error': ':name failed: :error',\n system: 'System',\n 'no-answers': 'Nothing was filled in.',\n download: 'Download',\n 'no-files-by-hand':\n 'A file cannot be attached by hand: the files a submission carries are what was posted with it.',\n 'submissions-none': 'Nothing here under this filter.',\n 'notifies-nobody': 'Nobody is told about this form',\n 'notifies-nobody-text':\n 'Every submission is kept here, but no letter goes out. Add a recipient on the Notifications tab.',\n 'notifies-nobody-short': 'Nobody is told',\n 'recipient-admin_deleted': 'This administrator no longer exists and gets no letter.',\n 'recipient-admin_inactive': 'This administrator is switched off and gets no letter.',\n 'recipient-invalid_email': 'This is not an e-mail address.',\n 'event-no-recipients': 'Nobody was told: the form names no recipients',\n 'notified-nobody': 'No notification: the form names nobody to tell',\n 'notify-queued': 'The notification is waiting in the queue',\n 'notify-again': 'Send the notification again',\n 'notify-sent-again': 'The notification has been sent again',\n 'notify-recipient-queued': 'waiting',\n 'notify-recipient-delivered': 'sent',\n 'notify-recipient-failed': 'failed',\n 'event-notify-queued': 'The notification was queued',\n 'event-notify-failed': 'The notification to :to failed',\n 'event-value': 'Answer “:field” corrected: “:from” → “:to”',\n },\n\n fields: {\n field: 'Field',\n 'new-field': 'New field',\n 'edit-field': 'Field',\n edit: 'Edit',\n 'no-fields': 'No questions yet.',\n 'no-fields-help': 'A form with no fields draws nothing on the page.',\n 'save-first': 'Save the form first, then write its questions.',\n delete: 'Delete',\n 'delete-title': 'Delete “:field”?',\n 'delete-text': 'It stops being asked. Answers already given keep it.',\n cancel: 'Cancel',\n save: 'Save',\n name: 'Name',\n 'name-help':\n 'Needed for variables in notifications — {{field.:name}} — and for integrations. Usually there is no need to change it.',\n 'name-renamed':\n 'Templates of notification channels that use {{field.:old}} stop working — change them to {{field.:new}}. Answers already received stay with the field.',\n advanced: 'Advanced',\n type: 'Type',\n title: 'Label',\n placeholder: 'Placeholder',\n help: 'Hint under the field',\n 'is-enabled': 'Shown',\n 'is-required': 'Required',\n 'is-fullsize': 'Full width',\n 'in-table': 'A column in the list',\n choices: 'Choices',\n 'choice-value': 'Value',\n 'choice-label': 'Label',\n 'add-choice': 'Add a choice',\n 'no-choices': 'No choices yet. A field with none accepts nothing.',\n maxlength: 'Longest answer, in characters',\n rows: 'Height, in lines',\n pattern: 'Pattern the answer has to match',\n 'pattern-help': 'The browser’s own, written without delimiters: [0-9+ ()-]{6,}',\n earliest: 'Earliest date',\n latest: 'Latest date',\n 'min-choices': 'Fewest ticks',\n 'max-choices': 'Most ticks',\n 'consent-text': 'The sentence beside the tick',\n 'consent-text-help': 'A link is allowed here: it is printed as written.',\n 'max-size': 'Largest file, in kilobytes',\n extensions: 'Extensions allowed',\n 'extensions-help': 'Comma separated. Empty means the list the site allows.',\n multiple: 'Several files at once',\n 'type-text': 'Text',\n 'type-email': 'E-mail',\n 'type-tel': 'Telephone',\n 'type-textarea': 'Long text',\n 'type-select': 'Drop-down',\n 'type-radio': 'One of several',\n 'type-checkbox': 'Several of several',\n 'type-consent': 'Consent',\n 'type-date': 'Date',\n 'type-file': 'File',\n 'type-hidden': 'Hidden',\n },\n\n relations: {\n form: 'Form',\n 'field-search': 'Find a form by its name or address',\n 'field-empty': 'No form',\n 'field-remove': 'Remove the form',\n 'field-missing': 'No such form',\n 'field-off': 'Switched off',\n 'field-nothing': 'No form matches',\n 'field-searching': 'Searching…',\n unknown: 'The Inbox has no form “:slug”. It has: :forms.',\n 'unknown-none': 'The Inbox has no form “:slug” — it has no forms yet.',\n },\n}\n","import { inject } from 'vue'\nimport { localizedValue, useLocales } from '@webx-ui/core'\nimport { i18nKey, useAdmin } from '@webx-ui/module-admin'\nimport { inboxMessages } from './messages'\nimport type { InboxStatus } from './types'\n\nconst seeded = new WeakSet<object>()\n\n/** Puts this package's English under the panel's dictionary, once per panel. */\nexport function useInboxMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-inbox', inboxMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n\n/**\n * A status's name in the language the panel is drawn in.\n *\n * A status is a panel word — \"New\", \"In progress\", \"Done\" — read by whoever works the inbox,\n * never by a visitor, so it follows the interface and not the content language picked for\n * editing; that one only stands in when the status has no line in the panel's language. Then\n * any line it has, then its key.\n */\nexport function useStatusName(): (status: InboxStatus) => string {\n const panel = inject(i18nKey, null)\n const locales = useLocales()\n\n return (status) => {\n const locale = panel?.state.locale\n const own = locale === undefined ? undefined : status.title[locale]\n\n return own || localizedValue(status.title, locales.active.value, status.key)\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\nimport { WxAction, WxAutocomplete, WxBadge, type AutocompleteOption } from '@webx-ui/core'\nimport { createRelationsApi, useAdmin, useTranslate } from '@webx-ui/module-admin'\nimport { useInboxMessages } from './i18n'\nimport type { FormCandidate } from './types'\n\n/**\n * `wx-inbox-form`: one form of the Inbox, chosen by name and kept by its slug — the value\n * `<x-webx-inbox::form slug=\"…\" />` takes and the blocks that print a form have always held as\n * typed text. So a block written before this field opens with its form already chosen.\n *\n * The forms come from the panel's relation target `inbox-form` (`GET /api/cms/relations/inbox-form`),\n * the one a vacancy picks its application form from: no inbox permission is needed to choose a\n * form, only to read what it receives. The target searches the slug as well as the title and puts\n * the exact slug first, which is how a stored slug is named when the field opens.\n *\n * A slug no form has stays in the field, marked: dropping it silently would change the block\n * without anybody touching it, and the server refuses such a slug only when it is written anew.\n */\ndefineOptions({ name: 'WxInboxFormField' })\n\nconst props = withDefaults(\n defineProps<{\n disabled?: boolean\n placeholder?: string\n emptyText?: string\n }>(),\n { disabled: false, placeholder: undefined, emptyText: undefined },\n)\n\nconst value = defineModel<string | null>({ default: null })\n\nuseInboxMessages()\n\nconst admin = useAdmin()\nconst t = useTranslate('webx-inbox')\nconst api = createRelationsApi(admin, 'inbox-form')\n\nconst slug = computed(() => (typeof value.value === 'string' ? value.value.trim() : ''))\n\n/** Forms the server described, by slug, kept for the life of the field. */\nconst known = ref(new Map<string, FormCandidate>())\n/** Slugs the server was asked about and has no form for. */\nconst missing = ref(new Set<string>())\n\nconst candidates = ref<FormCandidate[]>([])\nconst searching = ref(false)\nconst term = ref('')\n\nfunction remember(rows: FormCandidate[]): void {\n const next = new Map(known.value)\n\n for (const row of rows) if (row.slug) next.set(row.slug, row)\n\n known.value = next\n}\n\nasync function name(wanted: string): Promise<void> {\n if (wanted === '' || known.value.has(wanted) || missing.value.has(wanted)) return\n\n try {\n const rows = (await api.search(wanted)) as FormCandidate[]\n remember(rows)\n\n if (!rows.some((row) => row.slug === wanted))\n missing.value = new Set([...missing.value, wanted])\n } catch {\n // A request that did not come back is not an answer: the slug shows as it is until the next try.\n }\n}\n\nwatch(slug, (wanted) => void name(wanted), { immediate: true })\n\nconst chosen = computed(() => (slug.value === '' ? null : (known.value.get(slug.value) ?? null)))\n\n/** A mark for the chosen form: the server has none by that slug, or the site does not print it. */\nconst mark = computed(() => {\n if (slug.value === '') return null\n if (chosen.value === null)\n return missing.value.has(slug.value) ? t('relations.field-missing') : null\n\n return chosen.value.visible ? null : t('relations.field-off')\n})\n\nconst options = computed<AutocompleteOption[]>(() =>\n candidates.value\n .filter((one) => one.slug && one.slug !== slug.value)\n .map((one) => ({\n value: one.title,\n description: [one.slug, one.visible ? null : t('relations.field-off')]\n .filter((part): part is string => typeof part === 'string' && part !== '')\n .join(' · '),\n id: one.slug,\n })),\n)\n\nasync function search(query: string): Promise<void> {\n searching.value = true\n\n try {\n const found = (await api.search(query)) as FormCandidate[]\n candidates.value = found\n remember(found)\n } catch {\n candidates.value = []\n } finally {\n searching.value = false\n }\n}\n\n/** A site has a handful of forms: the list is there on the first click, before anything is typed. */\nfunction opened(): void {\n if (candidates.value.length === 0 && !searching.value) void search(term.value)\n}\n\nfunction pick(option: AutocompleteOption): void {\n if (typeof option.id !== 'string' || option.id === '') return\n\n value.value = option.id\n\n // The box writes the picked title into itself after this handler; emptied on the next tick it\n // reads as a search box again rather than as a second copy of the choice above it.\n void nextTick(() => {\n term.value = ''\n })\n}\n\nfunction clear(): void {\n value.value = null\n}\n\nconst placeholder = computed(() => props.placeholder ?? t('relations.field-search'))\n</script>\n\n<template>\n <div class=\"wx-inbox-form-field\">\n <div v-if=\"slug !== ''\" class=\"wx-inbox-form-field__chosen\">\n <span class=\"wx-inbox-form-field__text\">\n <span class=\"wx-inbox-form-field__title\">{{ chosen?.title ?? slug }}</span>\n <span v-if=\"chosen\" class=\"wx-inbox-form-field__slug\">{{ slug }}</span>\n </span>\n <wx-badge v-if=\"mark\" type=\"warning\">{{ mark }}</wx-badge>\n <wx-action\n v-if=\"!props.disabled\"\n type=\"remove\"\n size=\"sm\"\n :title=\"t('relations.field-remove')\"\n @click=\"clear\"\n />\n </div>\n <p v-else class=\"wx-inbox-form-field__empty\">\n {{ props.emptyText ?? t('relations.field-empty') }}\n </p>\n\n <!-- A wrapper rather than a class on the box: its root is a fragment, and a scoped rule on it\n would reach nothing. -->\n <div class=\"wx-inbox-form-field__search\">\n <wx-autocomplete\n v-model=\"term\"\n :options=\"options\"\n remote\n teleport\n :loading=\"searching\"\n :loading-text=\"t('relations.field-searching')\"\n :empty-text=\"t('relations.field-nothing')\"\n :placeholder=\"placeholder\"\n :aria-label=\"placeholder\"\n :disabled=\"props.disabled\"\n @search=\"search\"\n @open=\"opened\"\n @select=\"pick\"\n />\n </div>\n </div>\n</template>\n\n<style scoped>\n.wx-inbox-form-field {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n min-width: 0;\n}\n\n.wx-inbox-form-field__chosen {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n min-width: 0;\n padding: var(--wx-space-6) var(--wx-space-8) var(--wx-space-6) var(--wx-space-12);\n border: 1px solid var(--wx-border-default);\n border-radius: var(--wx-radius-control);\n background: var(--wx-bg-surface);\n}\n\n.wx-inbox-form-field__text {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.wx-inbox-form-field__title,\n.wx-inbox-form-field__slug {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.wx-inbox-form-field__slug,\n.wx-inbox-form-field__empty {\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n}\n\n.wx-inbox-form-field__empty {\n margin: 0;\n}\n\n.wx-inbox-form-field :deep(.wx-badge) {\n flex: none;\n}\n</style>\n","import type { AdminModule } from '@webx-ui/module-admin'\nimport FormField from './FormField.vue'\n\nexport interface InboxOptions {\n /** Where the section lives inside the panel. */\n path?: string\n /**\n * Whether the panel opens on this section.\n *\n * On by default, and that is the point of the module: a panel with an inbox is a panel\n * somebody opens in the morning to see what came in overnight, and the pages, the blocks\n * and the settings are what they do afterwards (§2.18). A panel whose front page is\n * something else says so here.\n */\n landing?: boolean\n}\n\n/**\n * Forms and submissions as a section of the panel.\n *\n * The id matches the module the server reports, which is what makes the entry appear in the\n * navigation: the section shows up when both halves are installed, and stays out of the way\n * when only one is.\n */\nexport function inbox(options: InboxOptions = {}): AdminModule {\n const path = options.path ?? '/inbox'\n\n return {\n id: 'inbox',\n path,\n landing: options.landing ?? true,\n routes: [\n // The section's own path travels as a prop, so a panel that mounted it somewhere else\n // still links between its screens correctly.\n {\n path,\n name: 'webx.inbox',\n component: () => import('./InboxPage.vue'),\n props: { base: path },\n },\n {\n path: `${path}/statuses`,\n name: 'webx.inbox.statuses',\n component: () => import('./StatusesPage.vue'),\n props: { base: path },\n },\n {\n path: `${path}/forms/:id(\\\\d+)`,\n name: 'webx.inbox.form',\n component: () => import('./FormEditorPage.vue'),\n props: { base: path },\n },\n // A submission has an address of its own, not a dialog over the list (§11): it goes in\n // the mail to whoever deals with it, it gets pasted into a chat, an agent links to it.\n {\n path: `${path}/submissions/:id(\\\\d+)`,\n name: 'webx.inbox.submission',\n component: () => import('./SubmissionPage.vue'),\n props: { base: path },\n },\n ],\n // A form of the Inbox as a field of any screen or block — the contact block's form — chosen\n // by name and kept by slug. Here and not in the panel: what it lists is this section's.\n types: {\n 'wx-inbox-form': { component: FormField, kind: 'field' },\n },\n }\n}\n","import type { AdminContext } from '@webx-ui/module-admin'\nimport type {\n FieldInput,\n FormInput,\n InboxField,\n InboxForm,\n InboxRecipient,\n InboxStatus,\n InboxSubmission,\n StatusInput,\n SubmissionInput,\n SubmissionMassInput,\n SubmissionQuery,\n SubmissionsPage,\n} from './types'\n\nexport interface InboxApi {\n /** Every form, in the order of the column, with what is waiting in each. */\n forms(): Promise<InboxForm[]>\n /** One form with its fields — what the editor opens. */\n form(id: number): Promise<InboxForm>\n createForm(input: FormInput): Promise<InboxForm>\n saveForm(id: number, input: FormInput): Promise<InboxForm>\n /** Refused with a 422 when the form has submissions: it is switched off, not deleted. */\n removeForm(id: number): Promise<void>\n /** A copy, switched off, with the questions and none of the answers. */\n duplicateForm(id: number): Promise<InboxForm>\n sortForms(ids: number[]): Promise<void>\n\n fields(formId: number): Promise<InboxField[]>\n createField(formId: number, input: FieldInput): Promise<InboxField>\n saveField(id: number, input: FieldInput): Promise<InboxField>\n /** Softly: the answers already given through it keep reading. */\n removeField(id: number): Promise<void>\n sortFields(formId: number, ids: number[]): Promise<void>\n\n statuses(): Promise<InboxStatus[]>\n createStatus(input: StatusInput): Promise<InboxStatus>\n saveStatus(id: number, input: StatusInput): Promise<InboxStatus>\n /** Refused with a 422 while submissions are still in it. */\n removeStatus(id: number): Promise<void>\n sortStatuses(ids: number[]): Promise<void>\n\n /** The administrators a form can be told to write to: the ones who may read the section. */\n recipients(): Promise<InboxRecipient[]>\n\n /** One form's submissions, with the columns of the list and the counts of its tabs. */\n submissions(formId: number, query?: SubmissionQuery): Promise<SubmissionsPage>\n /**\n * One of them, open. Opening it marks it read.\n *\n * The filters travel with it so that the neighbours it reports are the ones either side of\n * it in the list somebody was actually looking at.\n */\n submission(id: number, query?: SubmissionQuery): Promise<InboxSubmission>\n /** Typed in by hand — a call that came by telephone. `fields` is by machine name. */\n createSubmission(formId: number, fields: Record<string, unknown>): Promise<InboxSubmission>\n saveSubmission(id: number, input: SubmissionInput): Promise<InboxSubmission>\n removeSubmission(id: number): Promise<void>\n /** The notification once more, to whoever the form names now. Refused for a form naming nobody. */\n notifySubmission(id: number): Promise<InboxSubmission>\n /** A pile moved, marked or thrown away at once. */\n massSubmissions(input: SubmissionMassInput): Promise<{ count: number }>\n /**\n * Where the spreadsheet is. An address rather than a request: a download is the browser's\n * own job, and fetching a file into memory to hand it back to the browser is work for\n * nothing.\n */\n exportUrl(formId: number, query?: SubmissionQuery): string\n}\n\n/**\n * The statuses, asked once for everything on screen.\n *\n * The list of submissions and the one open beside it each want them, and each made its own\n * request — the section opened with `GET /statuses` twice. Every component makes its own api\n * object, so the answer is kept here — per HTTP client, which is one per panel — until the\n * statuses themselves are changed through this api or the request fails. Each caller gets its\n * own copy of the list: a page that reorders its statuses must not reorder everybody else's.\n */\nconst statusRequests = new WeakMap<object, Promise<InboxStatus[]>>()\n\n/** Everything under `/inbox`, below the panel's API path. */\nexport function createInboxApi(admin: AdminContext): InboxApi {\n const base = `${admin.apiPath}/inbox`\n const data = <T>(body: { data: T }): T => body.data\n const nothing = (): void => undefined\n const forgetStatuses = <T>(result: T): T => {\n statusRequests.delete(admin.http)\n\n return result\n }\n\n function statuses(): Promise<InboxStatus[]> {\n const known = statusRequests.get(admin.http)\n\n if (known !== undefined) {\n return known.then((list) => [...list])\n }\n\n const asked = admin.http.get<{ data: InboxStatus[] }>(`${base}/statuses`).then(data)\n\n statusRequests.set(admin.http, asked)\n asked.catch(() => statusRequests.delete(admin.http))\n\n return asked.then((list) => [...list])\n }\n\n return {\n forms: () => admin.http.get<{ data: InboxForm[] }>(`${base}/forms`).then(data),\n form: (id) => admin.http.get<{ data: InboxForm }>(`${base}/forms/${id}`).then(data),\n createForm: (input) => admin.http.post<{ data: InboxForm }>(`${base}/forms`, input).then(data),\n saveForm: (id, input) =>\n admin.http.put<{ data: InboxForm }>(`${base}/forms/${id}`, input).then(data),\n removeForm: (id) => admin.http.delete(`${base}/forms/${id}`).then(nothing),\n duplicateForm: (id) =>\n admin.http.post<{ data: InboxForm }>(`${base}/forms/${id}/duplicate`, {}).then(data),\n sortForms: (ids) => admin.http.post(`${base}/forms/sorting`, { ids }).then(nothing),\n\n fields: (formId) =>\n admin.http.get<{ data: InboxField[] }>(`${base}/forms/${formId}/fields`).then(data),\n createField: (formId, input) =>\n admin.http.post<{ data: InboxField }>(`${base}/forms/${formId}/fields`, input).then(data),\n saveField: (id, input) =>\n admin.http.put<{ data: InboxField }>(`${base}/fields/${id}`, input).then(data),\n removeField: (id) => admin.http.delete(`${base}/fields/${id}`).then(nothing),\n sortFields: (formId, ids) =>\n admin.http.post(`${base}/forms/${formId}/fields/sorting`, { ids }).then(nothing),\n\n statuses,\n createStatus: (input) =>\n admin.http\n .post<{ data: InboxStatus }>(`${base}/statuses`, input)\n .then(data)\n .then(forgetStatuses),\n saveStatus: (id, input) =>\n admin.http\n .put<{ data: InboxStatus }>(`${base}/statuses/${id}`, input)\n .then(data)\n .then(forgetStatuses),\n removeStatus: (id) =>\n admin.http.delete(`${base}/statuses/${id}`).then(nothing).then(forgetStatuses),\n sortStatuses: (ids) =>\n admin.http.post(`${base}/statuses/sorting`, { ids }).then(nothing).then(forgetStatuses),\n\n recipients: () => admin.http.get<{ data: InboxRecipient[] }>(`${base}/recipients`).then(data),\n\n submissions: (formId, query = {}) =>\n admin.http\n .get<{\n data: SubmissionsPage['data']\n meta: Omit<SubmissionsPage, 'data' | 'columns' | 'counts' | 'placements'>\n columns: SubmissionsPage['columns']\n counts: SubmissionsPage['counts']\n placements?: SubmissionsPage['placements']\n }>(`${base}/forms/${formId}/submissions`, { query: listQuery(query) })\n .then((body) => ({\n ...body.meta,\n data: body.data,\n columns: body.columns,\n counts: body.counts,\n // Optional on the wire: a server from before placements answers without them.\n placements: body.placements ?? [],\n })),\n\n submission: (id, query = {}) =>\n admin.http\n .get<{ data: InboxSubmission }>(`${base}/submissions/${id}`, { query: listQuery(query) })\n .then(data),\n\n createSubmission: (formId, fields) =>\n admin.http\n .post<{ data: InboxSubmission }>(`${base}/forms/${formId}/submissions`, { fields })\n .then(data),\n\n saveSubmission: (id, input) =>\n admin.http.put<{ data: InboxSubmission }>(`${base}/submissions/${id}`, input).then(data),\n\n removeSubmission: (id) => admin.http.delete(`${base}/submissions/${id}`).then(nothing),\n\n notifySubmission: (id) =>\n admin.http.post<{ data: InboxSubmission }>(`${base}/submissions/${id}/notify`).then(data),\n\n massSubmissions: (input) =>\n admin.http.post<{ data: { count: number } }>(`${base}/submissions/mass`, input).then(data),\n\n exportUrl: (formId, query = {}) => {\n const search = new URLSearchParams()\n\n for (const [key, value] of Object.entries(listQuery(query))) {\n if (value !== undefined && value !== null && value !== '') {\n search.set(key, String(value))\n }\n }\n\n const suffix = search.toString()\n\n return `${base}/forms/${formId}/submissions/export${suffix === '' ? '' : `?${suffix}`}`\n },\n }\n}\n\n/**\n * The filters as query parameters, with the empty ones left out.\n *\n * `page` and `per_page` go too, because the same shape is what the export is asked for — the\n * server ignores them there, and a second almost-identical builder is a second thing to get\n * out of step.\n */\nfunction listQuery(query: SubmissionQuery): Record<string, string | number | undefined> {\n return {\n view: query.view === undefined || query.view === '' ? undefined : query.view,\n search: query.search === undefined || query.search === '' ? undefined : query.search,\n assignee: query.assignee ?? undefined,\n placement: query.placement ?? undefined,\n sort: query.sort ?? undefined,\n page: query.page,\n per_page: query.per_page,\n }\n}\n","import { computed, type Ref, type WritableComputedRef } from 'vue'\nimport type { FormOptions } from './types'\n\n/**\n * One setting of a form, as something a control can be bound to.\n *\n * The keys are literal and several of them have dots in them — `thank-you.heading` is one key\n * (§5) — so nothing here splits on a dot, and a whole new object is written on every change\n * rather than a property being set in place: the editor holds the options as one value, and a\n * mutation underneath it is a change nothing is told about.\n *\n * An empty value is not written as an empty value: it is taken out. The server does the same\n * when it saves, and a form whose settings are twelve blank strings reads as configured when\n * it is not.\n */\nexport function option<T>(\n options: Ref<FormOptions>,\n key: string,\n fallback: T,\n): WritableComputedRef<T> {\n return computed({\n get: () => (options.value[key] ?? fallback) as T,\n set: (value: T) => {\n const next = { ...options.value }\n\n if (value === '' || value === null || value === undefined) {\n delete next[key]\n } else {\n next[key] = value\n }\n\n options.value = next\n },\n })\n}\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n localizedValue,\n toast,\n useLocales,\n useModal,\n WxAlert,\n WxButton,\n WxCheckbox,\n WxCheckboxGroup,\n WxDatePicker,\n WxDialog,\n WxFormItem,\n WxInput,\n WxRadioGroup,\n WxSelect,\n WxSkeleton,\n WxSpace,\n WxTextarea,\n} from '@webx-ui/core'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport type { InboxField, InboxForm, InboxSubmission } from './types'\n\n/**\n * A submission typed in by hand — a call that came by telephone, a form filled in on paper.\n *\n * The same questions the site asks, drawn from the same fields, and sent to the same intake:\n * the rules are the form's own, so a required field is required here too and a refusal reads\n * the same way. The one thing the panel cannot do is attach a visitor's file, because there is\n * no visitor — the files a submission carries are what was posted with it.\n */\nconst props = defineProps<{ form: InboxForm }>()\n\nconst { resolve, dismiss, open } = useModal<InboxSubmission>()\n\nconst context = useAdmin()\nconst api = createInboxApi(context)\nconst locales = useLocales()\nuseInboxMessages()\n\nconst t = useTranslate('webx-inbox')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst fields = ref<InboxField[]>([])\nconst loading = ref(true)\nconst saving = ref(false)\nconst values = ref<Record<string, unknown>>({})\nconst errors = ref<Record<string, string[]>>({})\n\n/** Everything the form actually asks, files excepted. */\nconst asked = computed(() => fields.value.filter((field) => field.type !== 'file'))\n\nconst hasFiles = computed(() => fields.value.some((field) => field.type === 'file'))\n\nfunction errorOf(field: InboxField): string | undefined {\n return errors.value[`fields.${field.key}`]?.[0]\n}\n\nfunction label(field: InboxField): string {\n return localizedValue(field.title, locales.active.value, field.key)\n}\n\nfunction text(value: unknown, fallback = ''): string {\n return localizedValue(\n value as Parameters<typeof localizedValue>[0],\n locales.active.value,\n fallback,\n )\n}\n\n/** The written-down answers of a `select`, `radio` or `checkbox`, in the panel's language. */\nfunction choices(field: InboxField): { value: string; label: string }[] {\n return (field.options.choices ?? []).map((choice) => ({\n value: choice.value,\n label: text(choice.label, choice.value),\n }))\n}\n\nasync function load(): Promise<void> {\n try {\n const form = await api.form(props.form.id)\n\n fields.value = (form.fields ?? []).filter((field) => field.is_enabled)\n\n for (const field of fields.value) {\n values.value[field.key] =\n field.type === 'checkbox' ? [] : field.type === 'consent' ? false : ''\n }\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid load()\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n try {\n resolve(await api.createSubmission(props.form.id, values.value))\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('panel.failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('panel.new-submission-title')\" :width=\"560\">\n <div class=\"wx-inbox-by-hand\">\n <wx-alert type=\"info\" :closable=\"false\">{{ t('panel.new-submission-help') }}</wx-alert>\n\n <wx-skeleton v-if=\"loading\" :rows=\"4\" />\n\n <template v-else>\n <wx-form-item\n v-for=\"field in asked\"\n :key=\"field.id\"\n :label=\"label(field)\"\n :help=\"text(field.help)\"\n :error=\"errorOf(field)\"\n :required=\"field.is_required\"\n >\n <wx-textarea\n v-if=\"field.type === 'textarea'\"\n v-model=\"values[field.key] as string\"\n :rows=\"field.options.rows ?? 4\"\n :placeholder=\"text(field.placeholder)\"\n />\n <wx-select\n v-else-if=\"field.type === 'select'\"\n v-model=\"values[field.key] as string\"\n :options=\"choices(field)\"\n clearable\n :placeholder=\"text(field.placeholder)\"\n />\n <wx-radio-group\n v-else-if=\"field.type === 'radio'\"\n v-model=\"values[field.key] as string\"\n :options=\"choices(field)\"\n />\n <wx-checkbox-group\n v-else-if=\"field.type === 'checkbox'\"\n v-model=\"values[field.key] as string[]\"\n :options=\"choices(field)\"\n />\n <!-- The sentence beside the tick is the form's own and may carry a link, so it is\n the field's label rather than text written here. -->\n <wx-checkbox\n v-else-if=\"field.type === 'consent'\"\n v-model=\"values[field.key] as boolean\"\n :label=\"text(field.options.text, label(field))\"\n />\n <wx-date-picker\n v-else-if=\"field.type === 'date'\"\n v-model=\"values[field.key] as string\"\n :placeholder=\"text(field.placeholder)\"\n />\n <wx-input\n v-else\n v-model=\"values[field.key] as string\"\n :type=\"field.type === 'email' ? 'email' : field.type === 'tel' ? 'tel' : 'text'\"\n :placeholder=\"text(field.placeholder)\"\n :maxlength=\"field.options.maxlength\"\n />\n </wx-form-item>\n\n <!-- Said rather than silently left out: a form whose point is the attachment would\n otherwise look like it had lost a field. -->\n <wx-alert v-if=\"hasFiles\" type=\"warning\" :closable=\"false\">\n {{ t('panel.no-files-by-hand') }}\n </wx-alert>\n </template>\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('panel.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" :disabled=\"loading\" @click=\"save\">\n {{ t('panel.save') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-inbox-by-hand {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef, watch } from 'vue'\nimport { useRoute, useRouter, type LocationQueryRaw } from 'vue-router'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n rowMenuWidth,\n WxFilterChips,\n WxRowMenu,\n WxScreenHead,\n type AppliedFilter,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n localizedValue,\n toast,\n useLocales,\n WxAvatar,\n WxBadge,\n WxButton,\n WxIcon,\n WxSelect,\n WxTable,\n WxTabs,\n WxText,\n WxEntityCard,\n WxFormItem,\n WxTooltip,\n useElementWidth,\n type TabItem,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport SubmissionCreateDialog from './SubmissionCreateDialog.vue'\nimport { createInboxApi } from './api'\nimport { useInboxMessages, useStatusName } from './i18n'\nimport type {\n InboxForm,\n InboxStatus,\n InboxSubmission,\n SubmissionRow,\n SubmissionsPage,\n} from './types'\n\n/**\n * What has come in through the chosen form — the right-hand side of the section (§11).\n *\n * Its columns are the form's own `in_table` fields and arrive with the rows, so this never has\n * to fetch the form to know what it is drawing. Which of them survive a narrow pane is decided\n * here and not by the form: the first answer identifies the row and stays at any width, and\n * each one after it goes a step earlier than the last. Pairing two of them into one cell — the\n * e-mail under the name — would mean guessing which fields mean what, and the panel does not\n * know that about somebody else's form.\n *\n * The state of the list lives in the address: coming back from a submission has to land on the\n * same form, the same tab and the same page, or the screen costs exactly that over the dialog\n * it was chosen instead of (§11).\n */\nconst props = withDefaults(\n defineProps<{\n form: InboxForm\n /** Where the section is mounted. */\n base?: string\n }>(),\n { base: '/inbox' },\n)\n\nconst emit = defineEmits<{\n /** Something changed that the column of forms counts. */\n changed: []\n}>()\n\nconst context = useAdmin()\nconst api = createInboxApi(context)\nconst route = useRoute()\nconst router = useRouter()\nconst locales = useLocales()\nuseInboxMessages()\n\n/** Where a row stops being a row. */\nconst CARDS = 640\n\nconst root = useTemplateRef<HTMLElement>('root')\nconst width = useElementWidth(root)\nconst asCards = computed(() => width.value > 0 && width.value < CARDS)\n\n/**\n * One decision about it, not two.\n *\n * The pane measures itself and the table measures itself, and between the two stands the\n * pane's own step — so the same number meant two different widths, and in the band between\n * them the table drew cards out of the full set of columns: five lines of \"Label: value\" for\n * one enquiry, which is exactly what the card exists instead of. The table is told rather\n * than left to work it out: never, or always, and by the same measurement the columns use.\n */\nconst cardsBelow = computed(() => (asCards.value ? Number.POSITIVE_INFINITY : 0))\n\nconst t = useTranslate('webx-inbox')\nconst panel = useTranslate('webx-admin')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst page = ref<SubmissionsPage | null>(null)\nconst statuses = ref<InboxStatus[]>([])\nconst loading = ref(false)\nconst selected = ref<SubmissionRow[]>([])\nconst moving = ref(false)\n\nconst canUpdate = computed(() => context.can('inbox.update'))\n\nconst add = createModal<InboxSubmission, { form: InboxForm }>(SubmissionCreateDialog)\n\n/** The tab, kept in the address so the way back lands on it. */\nconst view = computed({\n get: () =>\n typeof route.query.view === 'string' && route.query.view !== '' ? route.query.view : 'all',\n set: (value: string) => {\n // A new tab is a new list: the page it was on belongs to the tab it was on.\n void router.replace({\n query: { ...route.query, view: value === 'all' ? undefined : value, page: undefined },\n })\n },\n})\n\n/** Everything the list is looking at, as it travels to the server and into the address. */\nconst query = computed(() => ({\n view: view.value,\n search: typeof route.query.search === 'string' ? route.query.search : '',\n assignee: typeof route.query.assignee === 'string' ? route.query.assignee : null,\n placement: typeof route.query.placement === 'string' ? route.query.placement : null,\n sort: typeof route.query.sort === 'string' ? route.query.sort : null,\n page: Number(route.query.page ?? 1) || 1,\n}))\n\n/*\n * Where on the site the form has been sent from — the footer, an article (layout regions §11).\n *\n * The list carries the places with its rows, over the whole form rather than the filter, so the\n * filter costs no request of its own and its choices never shrink to the one already picked. It\n * is offered only when there is more than one place: one form in one place is nothing to filter.\n * And it stays while a placement is in the address, or a filter that arrived by link could not be\n * taken off.\n */\nconst placementOptions = computed(() =>\n (page.value?.placements ?? []).map((placement) =>\n placement === null\n ? { value: 'none', label: t('panel.placement-none') }\n : { value: placement, label: placement },\n ),\n)\n\nconst hasPlacements = computed(\n () => placementOptions.value.length > 1 || query.value.placement !== null,\n)\n\nconst applied = computed<AppliedFilter[]>(() => {\n const placement = query.value.placement\n\n if (placement === null) return []\n\n const label =\n placementOptions.value.find((option) => option.value === placement)?.label ??\n (placement === 'none' ? t('panel.placement-none') : placement)\n\n return [\n {\n key: 'placement',\n label: `${t('panel.meta-placement')}: ${label}`,\n clear: () => narrow(undefined),\n },\n ]\n})\n\nfunction narrow(placement: unknown): void {\n void router.replace({\n query: {\n ...route.query,\n placement: typeof placement === 'string' && placement !== '' ? placement : undefined,\n page: undefined,\n },\n })\n}\n\nconst views = computed<TabItem[]>(() => {\n const counts = page.value?.counts\n\n const items: TabItem[] = [\n { value: 'all', label: t('panel.view-all'), badge: counts?.all || undefined },\n { value: 'unread', label: t('panel.view-unread'), badge: counts?.unread || undefined },\n ]\n\n for (const status of statuses.value) {\n items.push({\n value: status.key,\n label: name(status),\n badge: counts?.statuses[status.key] || undefined,\n })\n }\n\n return items\n})\n\n/**\n * The n-th answer of a submission, for the card that has no columns to put them in.\n *\n * By position rather than by name, because a form's fields are the form's own: the first answer\n * is what the row is recognised by wherever it came from, and the second is the next most likely\n * way to reach whoever wrote it.\n */\nfunction answer(row: SubmissionRow, index: number): string {\n const column = page.value?.columns?.[index]\n\n return column ? (row.values[column.key] ?? '') : ''\n}\n\nconst columns = computed<TableColumn<SubmissionRow>[]>(() => {\n const answers = page.value?.columns ?? []\n\n /*\n * Narrow, a submission is one entity rather than five labelled lines: who wrote it, the next\n * answer under that, and the state of it beside the date. Five lines of \"Label: value\" is four\n * hundred pixels for one enquiry, and an inbox is read in a list.\n */\n if (asCards.value) return [{ key: 'card', label: '' }]\n\n return [\n ...answers.map((column, index) => ({\n key: `values.${column.key}`,\n label: column.label,\n sortable: true,\n // The first answer is what the row is recognised by and stays at any width; every one\n // after it is worth less than the room it takes, so it goes a step sooner. The steps are\n // wide — 640, 960 — because an answer is a sentence and not a number: two of them plus\n // the status and the date is already what a 755px pane holds without scrolling sideways,\n // which is what the pane beside the forms actually is on a 1280 screen (§11).\n hideBelow: index === 0 ? undefined : 320 + index * 320,\n })),\n // A floor rather than a width: the cell holds a badge, sometimes an avatar and sometimes\n // a paperclip, and a fixed width that any of them overflows makes the whole table scroll\n // sideways by two pixels.\n { key: 'status', label: t('panel.status'), minWidth: 100 },\n {\n key: 'created_at',\n label: t('panel.received'),\n sortable: true,\n width: 120,\n // \"today at 16:22\" is three words the table will break over two lines given half a\n // chance, and a date read down a column has to be one line to be read at all.\n cellClass: 'wx-submissions__when',\n hideBelow: 520,\n // Kept on the card, unlike most dates: \"when did this come in\" is one of the two\n // questions a submission is looked at for, and the other one is who sent it.\n },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right', hideOnCards: true },\n ]\n})\n\n/**\n * Two different nothings, said differently.\n *\n * \"Nothing has ever come in through this form\" is a fact about the form; \"nothing matches\" is\n * a fact about the tab somebody is standing on. Saying the first one on a filtered tab tells a\n * reader their form is dead when it is not.\n */\nconst emptyText = computed(() =>\n query.value.view === 'all' &&\n query.value.search === '' &&\n query.value.assignee === null &&\n query.value.placement === null\n ? t('panel.submissions-empty')\n : t('panel.submissions-none'),\n)\n\nconst statusOptions = computed(() =>\n statuses.value.map((status) => ({ value: status.id, label: name(status) })),\n)\n\n/* In the panel's language, not the content's: a status is a word of the interface. */\nconst name = useStatusName()\n\nfunction formName(): string {\n return localizedValue(props.form.title, locales.active.value, props.form.slug)\n}\n\nasync function load(state?: TableState): Promise<void> {\n loading.value = true\n\n try {\n page.value = await api.submissions(props.form.id, {\n view: query.value.view,\n search: state?.search ?? query.value.search,\n assignee: query.value.assignee,\n placement: query.value.placement,\n sort: state?.sort ? `${state.sort.order === 'desc' ? '-' : ''}${state.sort.key}` : null,\n page: state?.page ?? query.value.page,\n per_page: state?.perPage,\n })\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\n/**\n * The table reports everything it knows in one event, and that is the one place the address is\n * written from — so a page, a sort and a search all survive opening a submission and coming\n * back, and none of them has its own half of the bookkeeping.\n */\nfunction onState(state: TableState): void {\n const wanted: LocationQueryRaw = {\n ...route.query,\n search: state.search === '' ? undefined : state.search,\n sort:\n state.sort === null\n ? undefined\n : `${state.sort.order === 'desc' ? '-' : ''}${state.sort.key}`,\n page: state.page === 1 ? undefined : String(state.page),\n }\n\n void router.replace({ query: wanted })\n void load(state)\n}\n\n/* The form changes under the pane — the left column is a list, not a navigation — so the list\n has to notice rather than go on showing the previous form's submissions. One source per value,\n not a getter returning an array: that array is new on every change of the address, so a page\n turn would clear the selection and ask a second time, racing the request `onState` sent. */\nwatch([() => props.form.id, view, () => query.value.assignee, () => query.value.placement], () => {\n selected.value = []\n void load()\n})\n\n/* No `load()` of its own here: the table reports its state once it is mounted, and that\n report is the first request — with the page size the table draws. Asking here as well sent\n the list twice on every opening, the first time without `per_page`. */\nvoid statusList()\n\nasync function statusList(): Promise<void> {\n try {\n statuses.value = await api.statuses()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/** Opening one carries the whole filter along, so its arrows walk the list it came from. */\nfunction open(row: SubmissionRow): void {\n void router.push({ path: `${props.base}/submissions/${row.id}`, query: { ...route.query } })\n}\n\nfunction actionsFor(row: SubmissionRow): RowAction[] {\n if (!canUpdate.value) return []\n\n return [\n {\n key: 'read',\n icon: row.is_read ? 'eye-off' : 'eye',\n label: row.is_read ? t('panel.mark-unread') : t('panel.mark-read'),\n run: () => void mass([row.id], row.is_read ? 'unread' : 'read'),\n },\n {\n key: 'delete',\n icon: 'trash',\n label: t('panel.delete'),\n danger: true,\n run: () => void remove([row.id]),\n },\n ]\n}\n\nasync function moveTo(statusId: unknown): Promise<void> {\n if (typeof statusId !== 'number') return\n\n moving.value = true\n await mass(\n selected.value.map((row) => row.id),\n 'status',\n statusId,\n )\n moving.value = false\n}\n\nasync function remove(ids: number[]): Promise<void> {\n const agreed = await confirm({\n title:\n ids.length === 1\n ? t('panel.delete-submission-title')\n : t('panel.delete-selected-title', { count: ids.length }),\n message: t('panel.delete-submission-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n await mass(ids, 'delete')\n}\n\nasync function mass(\n ids: number[],\n action: 'status' | 'read' | 'unread' | 'delete',\n statusId?: number,\n): Promise<void> {\n if (ids.length === 0) return\n\n try {\n await api.massSubmissions({ ids, action, status_id: statusId })\n selected.value = []\n await load()\n emit('changed')\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function byHand(): Promise<void> {\n const made = await add({ form: props.form })\n\n if (!made) return\n\n toast.success(t('panel.created'))\n await load()\n emit('changed')\n void router.push({ path: `${props.base}/submissions/${made.id}`, query: { ...route.query } })\n}\n\n/**\n * A plain link and not a fetch: the browser downloads files, and pulling a spreadsheet into\n * memory to hand it straight back is work for nothing. It carries the filter that is on, so\n * what comes out is what is on screen.\n */\nconst exportHref = computed(() =>\n api.exportUrl(props.form.id, {\n view: query.value.view,\n search: query.value.search,\n assignee: query.value.assignee,\n placement: query.value.placement,\n sort: query.value.sort,\n }),\n)\n\n/*\n * The tools of the pane are what is done to this list of answers, and that is one thing:\n * take them away as a file.\n *\n * What the form is — its fields, its letters, its address — belongs to the form's own ··· in\n * the list of forms. And writing a submission is the section's own action, so it stands in\n * the head of the section beside its name (§11); the dialog is still this component's, which\n * is why it is exposed rather than moved: what is created has to land in this list, in this\n * filter, and be counted in these tabs.\n */\nconst actions = computed<ScreenAction[]>(() => [\n { key: 'export', label: t('panel.export'), icon: 'download', href: exportHref.value },\n])\n\ndefineExpose({ create: byHand })\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-submissions\">\n <wx-screen-head\n class=\"wx-submissions__head\"\n :level=\"3\"\n :title=\"formName()\"\n :subtitle=\"form.slug\"\n :actions=\"actions\"\n />\n\n <!--\n `items` and one panel: the table stays mounted while the tab changes, so the search\n somebody typed does not go with it.\n -->\n <wx-tabs\n v-model=\"view\"\n class=\"wx-submissions__views\"\n :items=\"views\"\n :collapse-below=\"560\"\n :aria-label=\"t('panel.status')\"\n >\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n clickable\n hover\n flush\n :loading=\"loading\"\n :loading-text=\"t('panel.loading')\"\n :selectable=\"canUpdate\"\n :select-row-label=\"panel('filters.select-row')\"\n :select-all-label=\"panel('filters.select-all')\"\n :row-class=\"(row: SubmissionRow) => (row.is_read ? undefined : 'is-unread')\"\n :search-placeholder=\"t('panel.search-submissions')\"\n :empty-text=\"emptyText\"\n :cards-below=\"cardsBelow\"\n :filters-count=\"applied.length\"\n @row-click=\"open\"\n @state-change=\"onState\"\n @selection-change=\"(_keys: unknown, rows: SubmissionRow[]) => (selected = rows)\"\n >\n <!-- The pile only offers what can be done to all of it at once; one row's own menu\n keeps the rest. -->\n <template v-if=\"selected.length > 0\" #actions>\n <div class=\"wx-submissions__mass\">\n <wx-text size=\"sm\" weight=\"medium\">\n {{ t('panel.selected', { count: selected.length }) }}\n </wx-text>\n <wx-select\n :model-value=\"null\"\n :options=\"statusOptions\"\n :placeholder=\"t('panel.move-to')\"\n :disabled=\"moving\"\n size=\"sm\"\n style=\"width: 180px\"\n @update:model-value=\"moveTo\"\n />\n <!-- Words only, like the select beside it: the bar reads as one line of text, and an\n icon on just one of its controls made that one look like something else. -->\n <wx-button\n size=\"sm\"\n variant=\"outline\"\n type=\"danger\"\n @click=\"remove(selected.map((row) => row.id))\"\n >\n {{ t('panel.delete') }}\n </wx-button>\n </div>\n </template>\n\n <template v-if=\"hasPlacements\" #filters>\n <wx-form-item :label=\"t('panel.meta-placement')\">\n <wx-select\n :model-value=\"query.placement\"\n :options=\"placementOptions\"\n :placeholder=\"t('panel.any-placement')\"\n clearable\n size=\"sm\"\n @update:model-value=\"narrow\"\n />\n </wx-form-item>\n </template>\n\n <template v-if=\"applied.length > 0\" #applied>\n <wx-filter-chips :filters=\"applied\" />\n </template>\n\n <template #cell-status=\"{ row }\">\n <div class=\"wx-submissions__state\">\n <wx-badge v-if=\"row.status\" :type=\"row.status.color\">{{ name(row.status) }}</wx-badge>\n <!-- The assignee is an avatar beside the status rather than a column: at the\n width this pane has, a column of names costs an answer (§11). -->\n <wx-avatar\n v-if=\"row.assignee\"\n :name=\"row.assignee.name\"\n :title=\"row.assignee.name\"\n size=\"xs\"\n tone=\"auto\"\n />\n <wx-tooltip v-if=\"row.files_count > 0\" :content=\"t('panel.attachments')\">\n <wx-icon name=\"file\" size=\"sm\" />\n </wx-tooltip>\n </div>\n </template>\n\n <template #cell-created_at=\"{ row }\">\n <wx-date :value=\"row.created_at\" compact />\n </template>\n\n <template #cell-card=\"{ row }\">\n <wx-entity-card variant=\"plain\" shape=\"circle\" :title=\"answer(row, 0)\">\n <template #subtitle>{{ answer(row, 1) }}</template>\n\n <template #meta>\n <wx-badge v-if=\"row.status\" :type=\"row.status.color\" size=\"sm\">\n {{ name(row.status) }}\n </wx-badge>\n <wx-avatar\n v-if=\"row.assignee\"\n :name=\"row.assignee.name\"\n :title=\"row.assignee.name\"\n size=\"xs\"\n tone=\"auto\"\n />\n <wx-icon v-if=\"row.files_count > 0\" name=\"file\" size=\"sm\" />\n <wx-date :value=\"row.created_at\" />\n </template>\n </wx-entity-card>\n </template>\n\n <template #card-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"String(row.id)\" />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"String(row.id)\" />\n </template>\n </wx-table>\n </wx-tabs>\n </div>\n</template>\n\n<style scoped>\n/*\n * One step, and everything in the pane keeps it.\n *\n * The panel's own — 8 on a phone and 16 on a desktop — and not a number of this screen's own.\n * Written as 16 here, the pane kept desktop air on a 375px screen: the head, the tabs and the\n * rows each took a line of nothing between them, and four rows fitted where six do now.\n *\n * It is the pane that holds it, so the name of the form, the tabs, the search box and the rows\n * all begin on the same line down the left. The table used to add a step of its own inside\n * this one — measured on a phone: the head at 17 and the search at 33 — and two insets, one\n * for the words and one for the list they are about, read as two panels stacked rather than as\n * one screen.\n */\n.wx-submissions {\n display: flex;\n flex-direction: column;\n gap: var(--wx-gap, var(--wx-space-16));\n padding: var(--wx-gap, var(--wx-space-16));\n min-width: 0;\n}\n\n/* The address the form posts to, in the type an address is written in. */\n.wx-submissions__head :deep(.wx-screen-head__subtitle) {\n font-family: var(--wx-font-family-mono);\n}\n\n/*\n * The tabs and the rows they filter are one thing, so they stand closer than two things do.\n * `WxTabs` leaves the panel's step under the strip, which is right where a card follows it;\n * here what follows is the search box of the same table, and a full step between a filter and\n * what it filters reads as a gap between two screens.\n */\n.wx-submissions__views :deep(.wx-tabs__panels) {\n padding-top: var(--wx-space-8);\n}\n\n.wx-submissions__mass {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n flex-wrap: wrap;\n}\n\n/* `:deep()` because the cell is the table's element and the class is ours — a scoped rule\n would be looking for our attribute on somebody else's markup (CLAUDE.md §4). */\n.wx-submissions :deep(.wx-submissions__when) {\n white-space: nowrap;\n}\n\n.wx-submissions__state {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--wx-space-6);\n min-width: 0;\n}\n\n/*\n * What nobody has opened yet, said on the whole row. `:deep()` because the row is the table's\n * element and the class is ours — a scoped rule would be looking for our attribute on somebody\n * else's markup (CLAUDE.md §4).\n */\n.wx-submissions :deep(.is-unread) {\n font-weight: var(--wx-font-weight-medium);\n}\n\n.wx-submissions :deep(.is-unread) td:first-child {\n box-shadow: inset 2px 0 0 0 var(--wx-color-primary);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useLocales, WxLocales, WxRichText, type LocalizedValue } from '@webx-ui/core'\n\n/**\n * Rich text in every language the site publishes in.\n *\n * `WxRichText` holds one string, the way `WxInput` does without `localized`; what a localized\n * field adds is the picker and the map underneath it. This is that, for the one place in this\n * module that needs it — the thank-you, which is printed raw on the site and is therefore\n * written where formatting is possible.\n *\n * With no content languages at all — a panel assembled by hand, a test — the value stays a\n * plain string, which is exactly what `useLocalized` does in the same situation and what the\n * server accepts for a form written before the site had a second language.\n */\nconst model = defineModel<LocalizedValue | string>({ default: '' })\n\nconst props = withDefaults(defineProps<{ placeholder?: string; minHeight?: string }>(), {\n placeholder: undefined,\n minHeight: '160px',\n})\n\nconst locales = useLocales()\n\nconst active = computed(() => {\n const chosen = locales.active.value\n const known = locales.list.value.some((locale) => locale.code === chosen)\n\n return known ? chosen : (locales.list.value[0]?.code ?? '')\n})\n\nconst text = computed({\n get: () => {\n const value = model.value\n\n if (typeof value === 'string') {\n // A string under a localized field belongs to the first language: that is where the\n // words were written, and hiding them would look like losing them.\n return active.value === '' || active.value === locales.list.value[0]?.code ? value : ''\n }\n\n return value[active.value] ?? ''\n },\n set: (next: string) => {\n if (active.value === '') {\n model.value = next\n\n return\n }\n\n const base = typeof model.value === 'string' ? { [active.value]: model.value } : model.value\n\n model.value = { ...base, [active.value]: next }\n },\n})\n</script>\n\n<template>\n <wx-locales v-if=\"locales.list.value.length > 0\">\n <wx-rich-text v-model=\"text\" :placeholder=\"props.placeholder\" :min-height=\"props.minHeight\" />\n </wx-locales>\n\n <wx-rich-text\n v-else\n v-model=\"text\"\n :placeholder=\"props.placeholder\"\n :min-height=\"props.minHeight\"\n />\n</template>\n","import type { LocalizedValue } from '@webx-ui/core'\nimport type { LinkValue, RelationCandidate } from '@webx-ui/module-admin'\n\n/** The kinds of question a form can ask (§4). Mirrors `WebxUi\\Inbox\\Fields\\FieldType`. */\nexport const FIELD_TYPES = [\n 'text',\n 'email',\n 'tel',\n 'textarea',\n 'select',\n 'radio',\n 'checkbox',\n 'consent',\n 'date',\n 'file',\n 'hidden',\n] as const\n\nexport type FieldType = (typeof FIELD_TYPES)[number]\n\n/** Tones of `WxBadge`, which is what a status is coloured with — never a hex value. */\nexport const STATUS_COLORS = ['default', 'primary', 'success', 'warning', 'info', 'danger'] as const\n\nexport type StatusColor = (typeof STATUS_COLORS)[number]\n\n/** One written-down answer of a `select`, `radio` or `checkbox`. */\nexport interface FieldChoice {\n value: string\n label: LocalizedValue | string\n}\n\n/**\n * What a field may be configured with. Which keys mean anything depends on the type (§4), and\n * the server keeps only the ones that do.\n */\nexport interface FieldOptions {\n maxlength?: number\n rows?: number\n pattern?: string\n min?: number | string\n max?: number | string\n choices?: FieldChoice[]\n text?: LocalizedValue | string\n max_size?: number\n extensions?: string[]\n multiple?: boolean\n [key: string]: unknown\n}\n\nexport interface InboxField {\n id: number\n form_id: number\n /** What somebody typed, or null. */\n name: string | null\n /** What the HTML will actually say — `f17` for a field nobody named. */\n key: string\n type: FieldType\n title: LocalizedValue\n placeholder: LocalizedValue\n help: LocalizedValue\n options: FieldOptions\n is_enabled: boolean\n is_required: boolean\n is_fullsize: boolean\n in_table: boolean\n position: number\n /* A table row, so the index signature `WxTable` asks of what it draws. */\n [key: string]: unknown\n}\n\n/** A field as the dialog saves it. */\nexport interface FieldInput {\n name: string | null\n type: FieldType\n title: LocalizedValue\n placeholder?: LocalizedValue\n help?: LocalizedValue\n options?: FieldOptions\n is_enabled: boolean\n is_required: boolean\n is_fullsize: boolean\n in_table: boolean\n}\n\n/** Who a notification goes to: somebody with an account, or an address typed in. */\nexport type Recipient = { admin_id: number } | { email: string }\n\n/** Why a recipient the form names would not get a letter. */\nexport type RecipientProblem = 'admin_deleted' | 'admin_inactive' | 'invalid_email'\n\n/** A recipient as the server reads it today: who it is, and whether a letter would reach it. */\nexport interface RecipientState {\n type: 'admin' | 'email'\n admin_id?: number\n name?: string | null\n email: string | null\n receives: boolean\n problem: RecipientProblem | null\n}\n\n/** A letter to the form's addresses, or the channels of the notifications module. */\nexport type NotifyVia = 'email' | 'channels'\n\n/** The settings of a form (§5). Every key is literal, dots included. */\nexport interface FormOptions {\n 'thank-you.heading'?: LocalizedValue\n 'thank-you.text'?: LocalizedValue\n 'design.submit-text'?: LocalizedValue\n /**\n * Where the visitor goes instead of the thank-you: a link of the panel. A string is what a\n * form saved before there was a choice holds — an address.\n */\n redirect?: LinkValue | string\n recipients?: Recipient[]\n email_field?: string\n /** How a submission is told about: a letter to `recipients`, or `notify_channels` — not both. */\n notify_via?: NotifyVia\n /** Channels of the notifications module a submission from the site goes to (NOTIFICATIONS §15.2). */\n notify_channels?: number[]\n 'antispam.honeypot'?: boolean\n 'antispam.min_seconds'?: number\n 'antispam.throttle'?: number\n 'antispam.captcha'?: 'off' | 'recaptcha' | 'turnstile'\n [key: string]: unknown\n}\n\nexport interface InboxForm {\n id: number\n slug: string\n title: LocalizedValue\n is_enabled: boolean\n options: FormOptions\n /**\n * Who a submission would be written to, read from `options.recipients` as saved. Optional\n * only because a server older than this field does not send it.\n */\n recipients?: RecipientState[]\n /** False when a submission would be written to nobody at all — no letter and no channel. */\n notifies?: boolean\n /** Whether `webx-ui/module-notifications` is installed: \"Notify through\" shows only then. */\n notifications?: boolean\n /**\n * How the form is set to tell, worked out by the server for a form saved before the choice\n * existed. Optional only because an older server does not send it.\n */\n notify_via?: NotifyVia\n /** The channels `options.notify_channels` holds, as saved, each with whether it sends. */\n channels?: FormChannel[]\n position: number\n /** Only where the query counted them — null on a form loaded on its own. */\n submissions_count: number | null\n /** Unread and not spam. */\n unread_count: number | null\n /** Only on a form asked for by id, or one just written. */\n fields?: InboxField[]\n /**\n * What the site has for each captcha — the keys and how they are run live in its `.env`,\n * not in the form — so the antispam tab can say which kind of key it expects. Optional\n * because a server older than this field does not send it.\n */\n captcha?: Record<CaptchaProvider, CaptchaSite>\n created_at: string | null\n updated_at: string | null\n}\n\n/** A channel a form notifies through, as the server read it. */\nexport interface FormChannel {\n id: number\n /** Null for a channel deleted for good. */\n name: string | null\n provider: string | null\n enabled: boolean\n deleted: boolean\n /** Switched on, not in the bin, and for this event or any. */\n sends: boolean\n}\n\nexport type CaptchaProvider = 'recaptcha' | 'turnstile'\n\n/** How a provider runs on this site, and whether both halves of its keys are there. */\nexport interface CaptchaSite {\n type: 'checkbox' | 'invisible' | 'v3'\n configured: boolean\n}\n\n/** A form as its editor saves it. */\nexport interface FormInput {\n slug: string\n title: LocalizedValue\n is_enabled: boolean\n options: FormOptions\n}\n\nexport interface InboxStatus {\n id: number\n key: string\n title: LocalizedValue\n color: StatusColor\n is_default: boolean\n is_spam: boolean\n is_closed: boolean\n position: number\n submissions_count: number | null\n}\n\nexport interface StatusInput {\n key: string\n title: LocalizedValue\n color: StatusColor\n is_default: boolean\n is_spam: boolean\n is_closed: boolean\n}\n\n/** Somebody with an account who may read the section, for the recipients list. */\nexport interface InboxRecipient {\n id: number\n name: string\n email: string\n}\n\n/** An administrator as a name beside something else — an assignee, the author of a log line. */\nexport interface InboxAdmin {\n id: number\n name: string\n email: string\n}\n\n/** One column of the list: a field of the form that was marked `in_table`. */\nexport interface SubmissionColumn {\n key: string\n label: string\n type: FieldType\n}\n\n/** What each tab above the list would hold, under the filters that are on. */\nexport interface SubmissionCounts {\n all: number\n unread: number\n /** By the status's key, spam included — its own tab has to say how much is in it. */\n statuses: Record<string, number>\n}\n\n/** A page as Laravel's `->paginate()` serialises it, which is what `WxTable` reads. */\nexport interface InboxPage<T> {\n data: T[]\n current_page: number\n last_page: number\n per_page: number\n total: number\n from: number | null\n to: number | null\n}\n\n/** One line of the list. The answers are keyed by the field's machine name. */\nexport interface SubmissionRow {\n id: number\n values: Record<string, string | null>\n status: InboxStatus | null\n assignee: InboxAdmin | null\n is_read: boolean\n source: string\n /** Where on the site the form stood (`footer`, `article`), or null when the page did not say. */\n placement: string | null\n files_count: number\n created_at: string | null\n /* A table row, so the index signature `WxTable` asks of what it draws. */\n [key: string]: unknown\n}\n\n/**\n * The list, and the two things that travel with it: what its columns are, and what each tab\n * above it would hold.\n */\nexport interface SubmissionsPage extends InboxPage<SubmissionRow> {\n columns: SubmissionColumn[]\n counts: SubmissionCounts\n /**\n * Every place on the site the form has been sent from, `null` for the page that did not say —\n * over the whole form rather than the filter, so the placement filter can always be undone.\n * More than one is what makes that filter worth showing.\n */\n placements: (string | null)[]\n}\n\n/** One answer, with the question as it was asked at the time (§2.2). */\nexport interface SubmissionValue {\n id: number\n field_id: number | null\n name: string\n label: string | null\n type: string\n value: string | null\n payload: unknown\n}\n\nexport interface SubmissionAttachment {\n id: number\n field_id: number | null\n name: string\n size: number\n mime: string | null\n /** Through the panel, behind the same permission as the submission (§8). */\n url: string\n}\n\n/** One line of what has happened to a submission. Never edited, never deleted. */\nexport interface SubmissionEvent {\n id: number\n type:\n | 'created'\n | 'status'\n | 'assignee'\n | 'note'\n | 'notified'\n | 'handled'\n | 'handler_error'\n | 'no_recipients'\n | 'notify_queued'\n | 'notify_failed'\n | 'value'\n | string\n /** The answer a `value` line is about, by machine name; null on every other line. */\n field?: string | null\n from: string | null\n to: string | null\n /** Null is the system — the submission arriving, the notification going out. */\n author: { id: number; name: string | null } | null\n created_at: string | null\n}\n\n/**\n * Whether the letter about a submission left (§9). `queued` is handed to the site's queue and\n * not sent yet — on a site with a queue, sending is a worker's job, and a worker can be missing.\n */\nexport type NotifyState = 'none' | 'queued' | 'delivered' | 'failed'\n\nexport interface SubmissionNotification {\n state: NotifyState\n error: string | null\n queued_at: string | null\n delivered_at: string | null\n /** Empty on a submission from before the letters were watched one by one. */\n recipients: {\n address: string\n state: 'queued' | 'delivered' | 'failed'\n error: string | null\n at: string\n }[]\n}\n\n/** One row of the notifications module's log about a submission (NOTIFICATIONS §15.3). */\nexport interface ChannelDelivery {\n id: number\n channel_id: number | null\n channel: string\n provider: string\n recipient: string\n recipient_label: string | null\n status: 'queued' | 'sent' | 'failed' | 'skipped'\n /** The status and the reason in the notifications module's own words. */\n status_label: string\n reason: string | null\n reason_label: string | null\n error: string | null\n attempts: number\n retryable: boolean\n created_at: string | null\n sent_at: string | null\n}\n\n/** What the intake saw around the submission: the page, the language, the campaign. */\nexport interface SubmissionMeta {\n ip?: string\n user_agent?: string\n page?: string\n referrer?: string\n utm?: Record<string, string>\n locale?: string\n [key: string]: unknown\n}\n\nexport interface InboxSubmission {\n id: number\n form: { id: number; slug: string | null; title: LocalizedValue | null }\n status: InboxStatus | null\n assignee: InboxAdmin | null\n values: SubmissionValue[]\n files: SubmissionAttachment[]\n meta: SubmissionMeta\n events: SubmissionEvent[]\n is_read: boolean\n source: string\n /** Where on the site the form stood (`footer`, `article`), or null when the page did not say. */\n placement: string | null\n notified_at: string | null\n notify_error: string | null\n /** The same two read as a state, with how each letter went. */\n notification: SubmissionNotification\n /**\n * What the form's channels made of it, a row per recipient, newest first; null on a site\n * without the notifications module, absent from a server older than it.\n */\n channels?: ChannelDelivery[] | null\n /** The neighbours in the list this was opened from, so the arrows walk the same pile. */\n previous_id: number | null\n next_id: number | null\n created_at: string | null\n updated_at: string | null\n}\n\n/** Everything the list asks the server for. The tab is `view`, the rest are filters. */\nexport interface SubmissionQuery {\n view?: string\n search?: string\n /** An administrator's id, or `none` — the pile nobody has picked up. */\n assignee?: string | null\n /** A placement, or `none` — the submissions whose page did not say. */\n placement?: string | null\n sort?: string | null\n page?: number\n per_page?: number\n}\n\n/** A submission changed by hand: the status, the assignee, a typo in an answer. */\nexport interface SubmissionInput {\n status_id?: number\n assignee_id?: number | null\n /** Machine name → the corrected text. The snapshot beside it is never rewritten. */\n values?: Record<string, string | null>\n}\n\n/** A pile dealt with at once. */\nexport interface SubmissionMassInput {\n ids: number[]\n action: 'status' | 'read' | 'unread' | 'delete'\n status_id?: number\n}\n\n/**\n * A form as the panel's relation target `inbox-form` describes it, with the slug under its own\n * name — what `wx-inbox-form` keeps (`FormTarget::row()` on the server).\n */\nexport interface FormCandidate extends RelationCandidate {\n slug?: string\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { slugify, useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxAction,\n WxAlert,\n WxButton,\n WxIcon,\n WxCheckbox,\n WxDialog,\n WxFormItem,\n WxInput,\n WxInputNumber,\n WxSelect,\n WxSpace,\n WxDatePicker,\n WxTagsInput,\n WxText,\n useLocales,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport LocalizedRichText from './LocalizedRichText.vue'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport { FIELD_TYPES, type FieldChoice, type FieldOptions, type FieldType } from './types'\nimport type { InboxField, InboxForm } from './types'\n\n/**\n * One question, written.\n *\n * The settings shown are the ones the chosen type has and no others (§4): a `select` has\n * choices, a `textarea` has a height, a `file` has a size and a list of extensions. Changing\n * the type changes what is asked for, and the server keeps only what belongs — a leftover\n * `choices` array on a text field would be a column of empty strings in an export a year from\n * now.\n */\nconst props = withDefaults(\n defineProps<{\n field: InboxField | null\n form: InboxForm\n /** The names the form's other fields answer to. */\n taken?: string[]\n }>(),\n { taken: () => [] },\n)\n\nconst { resolve, dismiss, open } = useModal<InboxField>()\n\nconst context = useAdmin()\nconst api = createInboxApi(context)\nuseInboxMessages()\n\nconst t = useTranslate('webx-inbox')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst name = ref('')\nconst type = ref<FieldType>('text')\nconst title = ref<LocalizedValue>({})\nconst placeholder = ref<LocalizedValue>({})\nconst help = ref<LocalizedValue>({})\nconst options = ref<FieldOptions>({})\n/*\n * The name follows the label of a new field until somebody writes one: \"Your phone\" is\n * `your_phone`, which is what `{{field.your_phone}}` in a channel's template and an integration\n * read. Written by hand — or a field that already has one — it stays as it is.\n */\nconst named = ref(false)\nconst locales = useLocales()\nconst isEnabled = ref(true)\nconst isRequired = ref(false)\nconst isFullsize = ref(true)\nconst inTable = ref(false)\n\nwatch(\n () => props.field,\n (field) => {\n name.value = field?.name ?? ''\n type.value = field?.type ?? 'text'\n title.value = { ...(field?.title ?? {}) }\n placeholder.value = { ...(field?.placeholder ?? {}) }\n help.value = { ...(field?.help ?? {}) }\n options.value = { ...(field?.options ?? {}) }\n isEnabled.value = field?.is_enabled ?? true\n isRequired.value = field?.is_required ?? false\n isFullsize.value = field?.is_fullsize ?? true\n inTable.value = field?.in_table ?? false\n named.value = field !== null\n errors.value = {}\n },\n { immediate: true },\n)\n\n/** The label in the language being edited, or the first one written. */\nfunction label(value: LocalizedValue): string {\n const map = (value ?? {}) as Record<string, string>\n const active = map[locales.active.value]\n\n if (typeof active === 'string' && active.trim() !== '') return active\n\n return Object.values(map).find((line) => typeof line === 'string' && line.trim() !== '') ?? ''\n}\n\n/** A machine name out of a label: Latin, `_` between words, a letter first, `_2` when taken. */\nfunction nameOf(words: string): string {\n let base = slugify(words, '_').slice(0, 60).replace(/_+$/, '')\n\n if (base === '') return ''\n if (!/^[a-z]/.test(base)) base = `field_${base}`\n\n let candidate = base\n\n for (let suffix = 2; props.taken.includes(candidate); suffix++) candidate = `${base}_${suffix}`\n\n return candidate\n}\n\nwatch(\n () => label(title.value),\n (words) => {\n if (!named.value) name.value = nameOf(words)\n },\n)\n\nfunction writeName(value: string): void {\n name.value = value\n // Emptied, it goes back to following the label; anything else is somebody's choice.\n named.value = value.trim() !== ''\n if (!named.value) name.value = nameOf(label(title.value))\n}\n\n/** The name the field had, when it had one and is being given another. */\nconst renamedFrom = computed(() => {\n const was = props.field?.name ?? ''\n const now = name.value.trim()\n\n return was !== '' && now !== '' && now !== was ? was : null\n})\n\nconst types = computed(() =>\n FIELD_TYPES.map((one) => ({ value: one, label: t(`fields.type-${one}`) })),\n)\n\nconst hasChoices = computed(() => ['select', 'radio', 'checkbox'].includes(type.value))\n\nconst choices = computed<FieldChoice[]>(() =>\n Array.isArray(options.value.choices) ? options.value.choices : [],\n)\n\n/** Extensions are typed as a list of words, which is what they are — not a sentence. */\nconst extensions = computed({\n get: () => (Array.isArray(options.value.extensions) ? options.value.extensions : []),\n set: (value: string[]) =>\n set(\n 'extensions',\n value.map((one) => one.replace(/^\\./, '')),\n ),\n})\n\nfunction set(key: string, value: unknown): void {\n options.value = { ...options.value, [key]: value }\n}\n\nfunction addChoice(): void {\n set('choices', [...choices.value, { value: '', label: {} }])\n}\n\nfunction removeChoice(index: number): void {\n set(\n 'choices',\n choices.value.filter((_, at) => at !== index),\n )\n}\n\nfunction setChoice(index: number, patch: Partial<FieldChoice>): void {\n set(\n 'choices',\n choices.value.map((choice, at) => (at === index ? { ...choice, ...patch } : choice)),\n )\n}\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n const input = {\n name: name.value.trim() === '' ? null : name.value.trim(),\n type: type.value,\n title: title.value,\n placeholder: placeholder.value,\n help: help.value,\n options: options.value,\n is_enabled: isEnabled.value,\n is_required: isRequired.value,\n is_fullsize: isFullsize.value,\n in_table: inTable.value,\n }\n\n try {\n resolve(\n props.field === null\n ? await api.createField(props.form.id, input)\n : await api.saveField(props.field.id, input),\n )\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n toast.danger(t('panel.failed'))\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"field ? t('fields.edit-field') : t('fields.new-field')\"\n :width=\"720\"\n >\n <div class=\"wx-inbox-field-form\">\n <wx-form-item :label=\"t('fields.type')\">\n <wx-select v-model=\"type\" :options=\"types\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('fields.title')\" :error=\"errorOf('title')\" required>\n <wx-input v-model=\"title\" localized />\n </wx-form-item>\n\n <!-- A hidden field has neither: nobody reads a placeholder they cannot see. -->\n <template v-if=\"type !== 'hidden'\">\n <wx-form-item v-if=\"type !== 'consent'\" :label=\"t('fields.placeholder')\">\n <wx-input v-model=\"placeholder\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('fields.help')\">\n <wx-input v-model=\"help\" localized />\n </wx-form-item>\n </template>\n\n <!-- The sentence beside the tick, which is printed raw on the site: it is a sentence\n with a link in it, not a caption. -->\n <wx-form-item\n v-if=\"type === 'consent'\"\n :label=\"t('fields.consent-text')\"\n :help=\"t('fields.consent-text-help')\"\n >\n <localized-rich-text\n :model-value=\"(options.text as LocalizedValue) ?? {}\"\n min-height=\"100px\"\n @update:model-value=\"(value) => set('text', value)\"\n />\n </wx-form-item>\n\n <div v-if=\"hasChoices\" class=\"wx-inbox-choices\">\n <wx-text size=\"sm\" weight=\"medium\">{{ t('fields.choices') }}</wx-text>\n\n <wx-text v-if=\"choices.length === 0\" size=\"sm\" tone=\"placeholder\">\n {{ t('fields.no-choices') }}\n </wx-text>\n\n <div v-for=\"(choice, index) in choices\" :key=\"index\" class=\"wx-inbox-choices__row\">\n <wx-input\n :model-value=\"choice.value\"\n :placeholder=\"t('fields.choice-value')\"\n @update:model-value=\"(value) => setChoice(index, { value: String(value) })\"\n />\n <wx-input\n :model-value=\"choice.label\"\n localized\n :placeholder=\"t('fields.choice-label')\"\n @update:model-value=\"(label) => setChoice(index, { label: label as LocalizedValue })\"\n />\n <wx-action icon=\"trash\" :title=\"t('fields.delete')\" @click=\"removeChoice(index)\" />\n </div>\n\n <wx-button variant=\"outline\" size=\"sm\" @click=\"addChoice\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('fields.add-choice') }}\n </wx-button>\n\n <!-- A list field with nothing to pick is refused by the server: it could never be sent. -->\n <wx-text v-if=\"errorOf('options.choices')\" size=\"sm\" tone=\"danger\">\n {{ errorOf('options.choices') }}\n </wx-text>\n </div>\n\n <div class=\"wx-inbox-field-form__row\">\n <wx-form-item v-if=\"type === 'textarea'\" :label=\"t('fields.rows')\">\n <wx-input-number\n :model-value=\"(options.rows as number) ?? 5\"\n :min=\"2\"\n :max=\"30\"\n @update:model-value=\"(value) => set('rows', value)\"\n />\n </wx-form-item>\n\n <wx-form-item\n v-if=\"['text', 'email', 'textarea'].includes(type)\"\n :label=\"t('fields.maxlength')\"\n >\n <wx-input-number\n :model-value=\"(options.maxlength as number) ?? null\"\n :min=\"1\"\n :max=\"20000\"\n @update:model-value=\"(value) => set('maxlength', value)\"\n />\n </wx-form-item>\n\n <wx-form-item\n v-if=\"type === 'tel'\"\n :label=\"t('fields.pattern')\"\n :help=\"t('fields.pattern-help')\"\n >\n <wx-input\n :model-value=\"(options.pattern as string) ?? ''\"\n @update:model-value=\"(value) => set('pattern', value)\"\n />\n </wx-form-item>\n\n <template v-if=\"type === 'date'\">\n <wx-form-item :label=\"t('fields.earliest')\">\n <wx-date-picker\n clearable\n :model-value=\"(options.min as string) ?? null\"\n @update:model-value=\"(value) => set('min', value)\"\n />\n </wx-form-item>\n <wx-form-item :label=\"t('fields.latest')\">\n <wx-date-picker\n clearable\n :model-value=\"(options.max as string) ?? null\"\n @update:model-value=\"(value) => set('max', value)\"\n />\n </wx-form-item>\n </template>\n\n <template v-if=\"type === 'checkbox'\">\n <wx-form-item :label=\"t('fields.min-choices')\">\n <wx-input-number\n :model-value=\"(options.min as number) ?? null\"\n :min=\"0\"\n @update:model-value=\"(value) => set('min', value)\"\n />\n </wx-form-item>\n <wx-form-item :label=\"t('fields.max-choices')\">\n <wx-input-number\n :model-value=\"(options.max as number) ?? null\"\n :min=\"0\"\n @update:model-value=\"(value) => set('max', value)\"\n />\n </wx-form-item>\n </template>\n\n <wx-form-item v-if=\"type === 'file'\" :label=\"t('fields.max-size')\">\n <wx-input-number\n :model-value=\"(options.max_size as number) ?? null\"\n :min=\"1\"\n @update:model-value=\"(value) => set('max_size', value)\"\n />\n </wx-form-item>\n </div>\n\n <template v-if=\"type === 'file'\">\n <wx-form-item :label=\"t('fields.extensions')\" :help=\"t('fields.extensions-help')\">\n <wx-tags-input v-model=\"extensions\" />\n </wx-form-item>\n\n <wx-checkbox\n :model-value=\"Boolean(options.multiple)\"\n :label=\"t('fields.multiple')\"\n @update:model-value=\"(value) => set('multiple', value)\"\n />\n </template>\n\n <div class=\"wx-inbox-field-form__checks\">\n <wx-checkbox v-model=\"isEnabled\" :label=\"t('fields.is-enabled')\" />\n <wx-checkbox v-model=\"isRequired\" :label=\"t('fields.is-required')\" />\n <wx-checkbox\n v-if=\"type !== 'hidden'\"\n v-model=\"isFullsize\"\n :label=\"t('fields.is-fullsize')\"\n />\n <wx-checkbox v-model=\"inTable\" :label=\"t('fields.in-table')\" />\n </div>\n\n <!-- Last and apart: the name is for templates and integrations, and the label above is\n where it comes from. -->\n <section class=\"wx-inbox-field-form__advanced\">\n <wx-text size=\"sm\" weight=\"medium\" tone=\"muted\">{{ t('fields.advanced') }}</wx-text>\n\n <wx-form-item\n :label=\"t('fields.name')\"\n :help=\"t('fields.name-help', { name: name.trim() || 'name' })\"\n :error=\"errorOf('name')\"\n >\n <wx-input\n :model-value=\"name\"\n placeholder=\"phone\"\n @update:model-value=\"(value) => writeName(String(value ?? ''))\"\n />\n </wx-form-item>\n\n <wx-alert\n v-if=\"renamedFrom\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"t('fields.name-renamed', { old: renamedFrom, new: name.trim() })\"\n />\n </section>\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('fields.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">{{ t('fields.save') }}</wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-inbox-field-form {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n\n.wx-inbox-field-form__row {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-12);\n}\n\n.wx-inbox-field-form__row > * {\n flex: 1 1 200px;\n min-width: 0;\n}\n\n/* Empty when the type has nothing to configure, and then it should take no room at all. */\n.wx-inbox-field-form__row:empty {\n display: none;\n}\n\n.wx-inbox-field-form__advanced {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n padding-block-start: var(--wx-space-12);\n border-block-start: 1px solid var(--wx-border-muted);\n}\n\n.wx-inbox-field-form__checks {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-16);\n}\n\n.wx-inbox-choices {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n align-items: flex-start;\n}\n\n.wx-inbox-choices__row {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n width: 100%;\n}\n\n.wx-inbox-choices__row > :first-child {\n flex: 0 1 180px;\n min-width: 0;\n}\n\n.wx-inbox-choices__row > :nth-child(2) {\n flex: 1 1 auto;\n min-width: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxRowMenu,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n localizedValue,\n toast,\n useLocales,\n WxBadge,\n WxButton,\n WxIcon,\n WxCard,\n WxEmpty,\n WxSortableList,\n WxText,\n} from '@webx-ui/core'\nimport FieldDialog from './FieldDialog.vue'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport type { InboxField, InboxForm } from './types'\n\n/**\n * The questions the form asks, in the order it asks them.\n *\n * A list with a grip rather than a table, because the order is the thing being edited here\n * and a table cannot be dragged. Each question is saved on its own, in its own dialog: a\n * field is a row with an identity that answers point at (§2.1), not a value of the form.\n */\nconst props = defineProps<{ form: InboxForm; canManage: boolean }>()\n\nconst fields = defineModel<InboxField[]>({ required: true })\n\nconst context = useAdmin()\nconst api = createInboxApi(context)\nconst locales = useLocales()\nuseInboxMessages()\n\nconst t = useTranslate('webx-inbox')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst edit = createModal<\n InboxField,\n { field: InboxField | null; form: InboxForm; taken: string[] }\n>(FieldDialog)\n\nfunction name(field: InboxField): string {\n return localizedValue(field.title, locales.active.value, field.key)\n}\n\n/** What the row says about itself, under its name: the type, then what is true of it. */\nfunction marks(field: InboxField): string[] {\n return [\n t(`fields.type-${field.type}`),\n ...(field.is_required ? [t('fields.is-required')] : []),\n ...(field.in_table ? [t('fields.in-table')] : []),\n ...(field.is_fullsize ? [] : [t('fields.is-fullsize')]),\n ]\n}\n\nconst empty = computed(() => fields.value.length === 0)\n\nasync function open(field: InboxField | null): Promise<void> {\n // The names the other fields answer to: a new one made out of its label must not be one of them.\n const taken = fields.value.filter((one) => one.id !== field?.id).map((one) => one.key)\n const saved = await edit({ field, form: props.form, taken })\n\n if (!saved) return\n\n fields.value =\n field === null\n ? [...fields.value, saved]\n : fields.value.map((one) => (one.id === saved.id ? saved : one))\n}\n\nasync function remove(field: InboxField): Promise<void> {\n const agreed = await confirm({\n title: t('fields.delete-title', { field: name(field) }),\n message: t('fields.delete-text'),\n confirmText: t('fields.delete'),\n cancelText: t('fields.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeField(field.id)\n fields.value = fields.value.filter((one) => one.id !== field.id)\n toast.success(t('panel.deleted'))\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actionsFor(field: InboxField): RowAction[] {\n if (!props.canManage) return []\n\n return [\n { key: 'edit', icon: 'edit', label: t('fields.edit'), run: () => void open(field) },\n {\n key: 'delete',\n icon: 'trash',\n label: t('fields.delete'),\n danger: true,\n run: () => void remove(field),\n },\n ]\n}\n\n/** The whole order, every time — the list is already in it on screen. */\nasync function reorder(): Promise<void> {\n try {\n await api.sortFields(\n props.form.id,\n fields.value.map((field) => field.id),\n )\n } catch (error) {\n toast.danger(message(error, t('panel.reorder-failed')))\n }\n}\n</script>\n\n<template>\n <wx-card :title=\"t('panel.tab-fields')\">\n <template v-if=\"canManage\" #extra>\n <wx-button type=\"primary\" size=\"sm\" @click=\"open(null)\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('fields.new-field') }}\n </wx-button>\n </template>\n\n <wx-empty\n v-if=\"empty\"\n :title=\"t('fields.no-fields')\"\n :description=\"t('fields.no-fields-help')\"\n />\n\n <wx-sortable-list\n v-else\n v-model=\"fields\"\n plain\n item-key=\"id\"\n :item-label=\"name\"\n :drag-label=\"t('panel.reorder')\"\n :disabled=\"!canManage\"\n :aria-label=\"t('panel.tab-fields')\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <div class=\"wx-inbox-field\">\n <div class=\"wx-inbox-field__name\">\n <wx-text weight=\"medium\" truncate>{{ name(item) }}</wx-text>\n <wx-badge v-if=\"!item.is_enabled\" type=\"default\">{{ t('panel.off') }}</wx-badge>\n </div>\n <wx-text size=\"sm\" tone=\"muted\" truncate>\n <!-- A field nobody named answers to its row number, which means nothing to anybody\n reading the list: it shows no name rather than that one. -->\n <template v-if=\"item.name\"\n ><code>{{ item.name }}</code> · </template\n >{{ marks(item).join(' · ') }}\n </wx-text>\n </div>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu :actions=\"actionsFor(item)\" :label=\"name(item)\" />\n </template>\n </wx-sortable-list>\n </wx-card>\n</template>\n\n<style scoped>\n.wx-inbox-field {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-2);\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.wx-inbox-field__name {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n}\n</style>\n","import { defineAsyncComponent } from 'vue'\nexport { inbox, type InboxOptions } from './module'\nexport { createInboxApi, type InboxApi } from './api'\nexport { inboxMessages } from './messages'\nexport { option } from './options'\nexport const WxInboxPage = defineAsyncComponent(() => import('./InboxPage.vue'))\nexport const WxInboxFormEditor = defineAsyncComponent(() => import('./FormEditorPage.vue'))\nexport const WxInboxStatusesPage = defineAsyncComponent(() => import('./StatusesPage.vue'))\nexport { default as WxInboxSubmissionList } from './SubmissionList.vue'\nexport const WxInboxSubmissionPage = defineAsyncComponent(() => import('./SubmissionPage.vue'))\nexport { default as WxInboxFieldList } from './FieldList.vue'\nexport { default as WxInboxFormField } from './FormField.vue'\nexport { FIELD_TYPES, STATUS_COLORS } from './types'\nexport type {\n ChannelDelivery,\n FieldChoice,\n FieldInput,\n FieldOptions,\n FieldType,\n FormCandidate,\n FormChannel,\n FormInput,\n FormOptions,\n InboxField,\n InboxForm,\n InboxAdmin,\n InboxPage,\n InboxRecipient,\n InboxStatus,\n InboxSubmission,\n Recipient,\n RecipientProblem,\n RecipientState,\n StatusColor,\n StatusInput,\n SubmissionAttachment,\n SubmissionColumn,\n SubmissionCounts,\n SubmissionEvent,\n SubmissionInput,\n SubmissionMassInput,\n SubmissionMeta,\n SubmissionQuery,\n SubmissionRow,\n SubmissionsPage,\n SubmissionValue,\n} from './types'\n"],"names":["inboxMessages","seeded","useInboxMessages","i18n","useAdmin","useStatusName","panel","inject","i18nKey","locales","useLocales","status","locale","localizedValue","props","__props","value","_useModel","admin","t","useTranslate","api","createRelationsApi","slug","computed","known","ref","missing","candidates","searching","term","remember","rows","next","row","name","wanted","watch","chosen","mark","options","one","part","search","query","found","opened","pick","option","nextTick","clear","placeholder","_openBlock","_createElementBlock","_hoisted_1","_hoisted_2","_createElementVNode","_hoisted_3","_hoisted_4","_toDisplayString","_hoisted_5","_createBlock","_unref","WxBadge","WxAction","_hoisted_6","_hoisted_7","_createVNode","WxAutocomplete","$event","inbox","path","FormField","statusRequests","createInboxApi","base","data","body","nothing","forgetStatuses","result","statuses","list","asked","id","input","ids","formId","listQuery","fields","key","suffix","fallback","resolve","dismiss","open","useModal","context","message","useErrorText","loading","saving","values","errors","field","hasFiles","errorOf","label","text","choices","choice","load","form","error","toast","save","WxDialog","WxSpace","WxButton","WxAlert","WxSkeleton","_Fragment","_renderList","WxFormItem","WxTextarea","WxSelect","WxRadioGroup","WxCheckboxGroup","WxCheckbox","WxDatePicker","WxInput","CARDS","emit","__emit","route","useRoute","router","useRouter","root","useTemplateRef","width","useElementWidth","asCards","cardsBelow","page","selected","moving","canUpdate","add","createModal","SubmissionCreateDialog","view","placementOptions","placement","hasPlacements","applied","narrow","views","counts","items","answer","index","column","columns","answers","rowMenuWidth","emptyText","statusOptions","formName","state","onState","statusList","actionsFor","mass","remove","moveTo","statusId","confirm","action","byHand","made","exportHref","actions","__expose","WxScreenHead","WxTabs","WxTable","_keys","_withCtx","WxAvatar","WxTooltip","WxIcon","WxDate","WxEntityCard","_createTextVNode","WxRowMenu","WxText","_cache","WxFilterChips","model","active","WxLocales","WxRichText","FIELD_TYPES","STATUS_COLORS","type","title","help","named","isEnabled","isRequired","isFullsize","inTable","map","line","nameOf","words","slugify","candidate","writeName","renamedFrom","was","now","types","hasChoices","extensions","set","addChoice","removeChoice","_","at","setChoice","patch","LocalizedRichText","WxInputNumber","WxTagsInput","edit","FieldDialog","marks","empty","taken","saved","reorder","WxCard","WxEmpty","WxSortableList","item","WxInboxPage","defineAsyncComponent","WxInboxFormEditor","WxInboxStatusesPage","WxInboxSubmissionPage"],"mappings":"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"}
|