@webx-ui/module-inbox 0.10.0 → 0.11.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormEditorPage-B2Q0gTp3.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 description = option<LocalizedValue>(options, 'description', {})\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 :label=\"t('panel.description')\" :help=\"t('panel.description-help')\">\n <wx-input v-model=\"description\" 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 FormMail,\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 /** Where the letters go now, and how long the queue holds one. */\n mail?: FormMail\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 <!-- Where the letters go, as the site sends them now: on a client's site they went to a\n test sandbox, by a config cached before .env changed, a minute after the form — and\n nothing here said any of it. -->\n <div v-if=\"mail\" class=\"wx-inbox-notifications__route\">\n <wx-text size=\"sm\" tone=\"muted\">\n {{ t('panel.mail-route') }} <code>{{ mail.route.label }}</code>\n </wx-text>\n <wx-text v-if=\"mail.queue.hint\" size=\"sm\" tone=\"muted\">{{ mail.queue.hint }}</wx-text>\n <wx-alert\n v-for=\"warning in mail.warnings\"\n :key=\"warning.code\"\n type=\"warning\"\n variant=\"soft\"\n :description=\"warning.message\"\n />\n </div>\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.wx-inbox-notifications__route {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n max-width: var(--wx-field-max-width, 640px);\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 :mail=\"form.mail\"\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","description","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","warning","WxSpace","WxButton","WxIcon","_hoisted_5","_hoisted_6","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,4 +1,4 @@
1
- import { FormOptions, InboxField, NotifyVia, RecipientState } from './types';
1
+ import { FormMail, FormOptions, InboxField, NotifyVia, RecipientState } from './types';
2
2
  /**
3
3
  * Who hears that something arrived (§9) — one card, and one way of hearing it.
4
4
  *
@@ -27,6 +27,8 @@ type __VLS_Props = {
27
27
  notifications?: boolean;
28
28
  /** How the form tells, as the server worked it out for a form saved before the choice. */
29
29
  via?: NotifyVia;
30
+ /** Where the letters go now, and how long the queue holds one. */
31
+ mail?: FormMail;
30
32
  };
31
33
  type __VLS_PublicProps = {
32
34
  modelValue: FormOptions;
@@ -2,7 +2,7 @@ import { defineComponent as J, ref as _, openBlock as d, createBlock as p, unref
2
2
  import { useRoute as pe, useRouter as fe } from "vue-router";
3
3
  import { useAdmin as K, useTranslate as Q, useErrorText as X, WxListScreen as me, WxRowMenu as ve } from "@webx-ui/module-admin";
4
4
  import { useModal as be, WxDialog as xe, WxFormItem as O, WxInput as P, WxSpace as ye, WxButton as T, toast as y, useLocales as _e, createModal as ge, WxCard as we, WxListDetail as ke, WxSkeleton as j, WxEmpty as We, WxIcon as N, WxSortableList as he, WxText as G, WxIndicator as H, WxAction as Ce, localizedValue as Ve, confirm as Fe } from "@webx-ui/core";
5
- import { c as Y, u as Z, _ as Ie, S as Se } from "./index-BJ-mLCee.js";
5
+ import { c as Y, u as Z, _ as Ie, S as Se } from "./index-gLN73Ici.js";
6
6
  const qe = { class: "wx-inbox-create" }, $e = /* @__PURE__ */ J({
7
7
  __name: "FormCreateDialog",
8
8
  setup(U) {
@@ -384,4 +384,4 @@ const qe = { class: "wx-inbox-create" }, $e = /* @__PURE__ */ J({
384
384
  export {
385
385
  Ee as default
386
386
  };
387
- //# sourceMappingURL=InboxPage-PAtQ48A2.js.map
387
+ //# sourceMappingURL=InboxPage-iMQ0pjrR.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InboxPage-PAtQ48A2.js","sources":["../src/FormCreateDialog.vue","../src/InboxPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport type { InboxForm } from './types'\n\n/**\n * A form, started.\n *\n * Two fields, because a form has nothing else until it has questions: what it is called and\n * where it answers. Everything else — who is written to, what happens after sending, the\n * antispam — belongs to the editor, and asking for it before the form exists would be a\n * dialog longer than the screen it leads to.\n *\n * The address is typed rather than made out of the title. A page's address is a sentence\n * transliterated; a form's is a short word somebody chooses once and then writes into a\n * template by hand, and guessing `svyazhites-s-nami` for them helps nobody.\n */\nconst { resolve, dismiss, open } = useModal<InboxForm>()\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 title = ref<LocalizedValue>({})\nconst slug = ref('')\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\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 try {\n resolve(\n await api.createForm({\n title: title.value,\n slug: slug.value.trim(),\n is_enabled: true,\n options: {},\n }),\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 v-model:open=\"open\" :title=\"t('panel.new-form-title')\" :width=\"520\">\n <div class=\"wx-inbox-create\">\n <wx-form-item :label=\"t('panel.title')\" :error=\"errorOf('title')\" required>\n <wx-input v-model=\"title\" localized autofocus />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.slug')\" :help=\"t('panel.slug-help')\" :error=\"errorOf('slug')\">\n <wx-input v-model=\"slug\" placeholder=\"contact\" />\n </wx-form-item>\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\" @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-create {\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 } from 'vue-router'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n localizedValue,\n toast,\n useLocales,\n WxAction,\n WxButton,\n WxCard,\n WxEmpty,\n WxIcon,\n WxIndicator,\n WxListDetail,\n WxSkeleton,\n WxSortableList,\n WxText,\n} from '@webx-ui/core'\nimport FormCreateDialog from './FormCreateDialog.vue'\nimport SubmissionList from './SubmissionList.vue'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport type { InboxForm } from './types'\n\n/**\n * The section: the forms of the site on the left, what came in through one of them on the\n * right (§11, §2.12).\n *\n * One screen rather than two, and that is the decision this is built around. The columns of\n * the list are the fields of the chosen form, so a list of everything would have no columns\n * to speak of; and the two questions somebody opens this section to ask — \"what is new\" and\n * \"what does this form ask\" — are one click apart instead of one screen apart.\n *\n * Which of the two is the screen and which is the chooser is the second decision. The forms\n * are the chooser: they are the `filters` column of the pane, so on a phone they fold into a\n * panel and the submissions are what the section opens on. The other way round — forms as the\n * list, submissions as the record beside it — is what this was, and it cost the reader a\n * screen: somebody who opens the section is asking what came in, and was shown a list of\n * three form names instead, every time.\n *\n * One form is therefore always open (the first, unless the address names another), and what\n * is here is the column: the forms, in the order somebody dragged them, with what is waiting\n * in each.\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 forms = ref<InboxForm[]>([])\nconst loading = ref(true)\n\n/** The list of submissions, for the one action of its own that the section's head carries. */\nconst pane = useTemplateRef<{ create: () => void }>('pane')\n\n/**\n * Whether the forms still have a column, and whether their panel is up.\n *\n * Both live here rather than inside the pane because the way to the forms is in the head of\n * the section, beside its other actions — a button that appears exactly when the column is\n * gone. The pane says when that is.\n */\nconst columnInline = ref(true)\nconst formsOpen = ref(false)\n\nconst canManage = computed(() => context.can('inbox.manage'))\n\nconst create = createModal<InboxForm, Record<string, never>>(FormCreateDialog)\n\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'inbox')?.title ??\n t('module.title'),\n)\n\n/**\n * Which form is open, kept in the address.\n *\n * So that coming back from a submission lands on the form it belonged to (§11) — and so that\n * a link to \"the callback form\" is a link somebody can send.\n */\nconst current = computed<number | null>(() => {\n const asked = route.query.form\n\n return typeof asked === 'string' && asked !== '' ? Number(asked) : null\n})\n\nconst chosen = computed(() => forms.value.find((form) => form.id === current.value) ?? null)\n\nfunction name(form: InboxForm): string {\n return localizedValue(form.title, locales.active.value, form.slug)\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n forms.value = await api.forms()\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid load()\n\n/**\n * One form is always open, and the address always says which.\n *\n * The section is the submissions, so the first form stands in until somebody picks another —\n * on a phone especially, where the alternative is opening the panel before seeing anything at\n * all. It covers the address that names no form, the address that names one that has since\n * been deleted, and the list arriving after the address did; `replace`, because none of the\n * three is a place worth going back to.\n */\nwatch([forms, chosen], () => {\n if (forms.value.length === 0 || chosen.value !== null) return\n\n void router.replace({ query: { ...route.query, form: String(forms.value[0].id) } })\n})\n\nfunction choose(form: InboxForm, close: () => void): void {\n void router.replace({ query: { ...route.query, form: String(form.id) } })\n /* Picked from the panel: the panel has done its job. It is a column on a wide screen, and\n closing what is not open does nothing there. */\n close()\n}\n\nfunction edit(form: InboxForm): void {\n void router.push(`${props.base}/forms/${form.id}`)\n}\n\nfunction actionsFor(form: InboxForm): RowAction[] {\n if (!canManage.value) return []\n\n return [\n { key: 'settings', icon: 'settings', label: t('panel.settings'), run: () => edit(form) },\n { key: 'duplicate', icon: 'copy', label: t('panel.duplicate'), run: () => duplicate(form) },\n // A form that has taken submissions is switched off, never deleted (§2.4). The line is\n // left out rather than greyed: a menu is a list of what is possible.\n ...(form.submissions_count === 0\n ? [\n {\n key: 'delete',\n icon: 'trash' as const,\n label: t('panel.delete'),\n danger: true,\n run: () => remove(form),\n },\n ]\n : []),\n ]\n}\n\nasync function add(): Promise<void> {\n const made = await create({})\n\n if (made) {\n await load()\n edit(made)\n }\n}\n\nasync function duplicate(form: InboxForm): Promise<void> {\n try {\n const copy = await api.duplicateForm(form.id)\n toast.success(t('panel.duplicated'))\n await load()\n edit(copy)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function remove(form: InboxForm): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-form-title', { form: name(form) }),\n message: t('panel.delete-form-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeForm(form.id)\n toast.success(t('panel.deleted'))\n\n /* The address named the form that is gone; what stands in its place is decided above. */\n if (current.value === form.id) {\n const rest = { ...route.query }\n delete rest.form\n void router.replace({ query: rest })\n }\n\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/**\n * The whole order, every time.\n *\n * The list is already in its new order on screen — `WxSortableList` reorders the model before\n * it says anything — so this only has to agree with what is there, and a failure has to put\n * the list back rather than leave the screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await api.sortForms(forms.value.map((form) => form.id))\n } catch (error) {\n toast.danger(message(error, t('panel.reorder-failed')))\n await load()\n }\n}\n\n/**\n * What the section offers. Declared, because on a phone the head folds it into the ···.\n *\n * The one it exists for is a submission, not a form: this is the inbox, and the section is\n * opened dozens of times for what came in for every once that a form is added. A new form is\n * the `+` over the list of forms — beside the thing it makes one more of — and what used to\n * stand here as the primary button was that, which is why it was the biggest thing on the\n * screen and almost never what anybody wanted.\n */\nconst actions = computed<ScreenAction[]>(() => {\n const list: ScreenAction[] = []\n\n if (canManage.value) {\n list.push({\n key: 'statuses',\n label: t('panel.statuses'),\n icon: 'tag',\n run: () => void router.push(`${props.base}/statuses`),\n })\n }\n\n /* The dialog belongs to the list: what is written has to land in it, in the filter that is\n on, and be counted in its tabs. The head only asks for it. */\n if (context.can('inbox.update') && chosen.value !== null) {\n list.push({\n key: 'new-submission',\n label: t('panel.new-submission'),\n icon: 'plus',\n primary: true,\n run: () => void pane.value?.create(),\n })\n }\n\n return list\n})\n</script>\n\n<template>\n <wx-list-screen :title=\"title\" :actions=\"actions\" :card=\"false\">\n <!--\n Written here rather than declared as an action, because an action folds behind the ···\n on a narrow head and this is only ever drawn on one: it is the way to the forms for a\n reader whose column of them is gone. It stands where the other actions of the section\n stand, beside the one that writes a submission.\n -->\n <template v-if=\"!columnInline\" #actions>\n <wx-button variant=\"outline\" @click=\"formsOpen = true\">\n <template #icon><wx-icon name=\"list\" /></template>\n {{ t('panel.forms') }}\n </wx-button>\n </template>\n\n <wx-card class=\"wx-inbox\" padding=\"none\">\n <wx-list-detail\n v-model:filters-open=\"formsOpen\"\n class=\"wx-inbox__panes\"\n :filters-width=\"270\"\n :detail-min=\"420\"\n :filters-title=\"t('panel.forms')\"\n @filters-inline=\"columnInline = $event\"\n >\n <!--\n The forms are what narrows the list, so they are the column that folds: on a phone\n this is a panel raised from the head of the submissions, and the submissions are\n the screen.\n -->\n <template #filters=\"{ inline, close }\">\n <wx-skeleton v-if=\"loading\" class=\"wx-inbox__loading\" :rows=\"5\" />\n\n <template v-else>\n <!--\n In the panel the `+` would stand alone on a line of its own under the drawer's\n own heading, which reads as a stray mark rather than as a control. There is\n room for the word here, so it takes it.\n -->\n <wx-button\n v-if=\"!inline && canManage\"\n class=\"wx-inbox__new-form\"\n variant=\"outline\"\n block\n @click=\"add\"\n >\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('panel.new-form') }}\n </wx-button>\n\n <!--\n A grip and not the whole row: the row is what opens a form, and a list whose\n rows both open and drag is a list where one of the two happens by accident.\n -->\n <wx-sortable-list\n v-model=\"forms\"\n class=\"wx-inbox__forms\"\n :class=\"{ 'is-panel': !inline }\"\n plain\n size=\"sm\"\n item-key=\"id\"\n :item-label=\"name\"\n :drag-label=\"t('panel.reorder')\"\n :disabled=\"!canManage\"\n :title=\"inline ? t('panel.forms') : undefined\"\n @move=\"reorder\"\n >\n <!--\n A new form is made where the forms are and not in the head of the section: it\n is one more of these, and it is asked for once for every few dozen times\n somebody opens the inbox to read what came in. An icon, because the word for\n it is already written beside it.\n -->\n <template v-if=\"inline && canManage\" #extra>\n <wx-action icon=\"plus\" size=\"sm\" :title=\"t('panel.new-form')\" @click=\"add\" />\n </template>\n\n <template #default=\"{ item }\">\n <button\n type=\"button\"\n class=\"wx-inbox-form\"\n :class=\"{ 'is-current': item.id === current, 'is-off': !item.is_enabled }\"\n @click=\"choose(item, close)\"\n >\n <!--\n A switched-off form is said by the name itself — struck through and grey —\n rather than by a badge beside it. The badge was a second thing on a line\n 270px wide that already holds a name, a count and the ···: it did not\n shrink, so it ran under the menu, and it said in a word what the type says\n at a glance.\n -->\n <span class=\"wx-inbox-form__name\">\n <wx-text truncate weight=\"medium\" :tone=\"item.is_enabled ? 'default' : 'muted'\">\n {{ name(item) }}\n </wx-text>\n\n <!--\n On the name's line, not under it. The list stacks what the slot hands it,\n so a count standing beside the form was a third line under the address —\n the row grew by a line to say a single digit. Unread first and in colour;\n the total behind it, quietly, because it is context rather than work.\n -->\n <!-- Only on a form that is switched on: one that is off tells nobody\n because nobody can send it. -->\n <wx-icon\n v-if=\"item.is_enabled && item.notifies === false\"\n class=\"wx-inbox-form__silent\"\n name=\"warning\"\n :label=\"t('panel.notifies-nobody-short')\"\n :title=\"t('panel.notifies-nobody-short')\"\n />\n <wx-indicator\n v-if=\"item.unread_count\"\n class=\"wx-inbox-form__count\"\n type=\"primary\"\n :value=\"item.unread_count\"\n :label=\"t('panel.unread')\"\n />\n <wx-indicator\n v-else-if=\"item.submissions_count\"\n class=\"wx-inbox-form__count\"\n type=\"neutral\"\n :value=\"item.submissions_count\"\n :label=\"t('panel.submissions')\"\n />\n </span>\n <wx-text size=\"sm\" tone=\"muted\" truncate>{{ item.slug }}</wx-text>\n </button>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu :actions=\"actionsFor(item)\" :label=\"name(item)\" />\n </template>\n </wx-sortable-list>\n </template>\n </template>\n\n <!--\n The submissions of the chosen form, and the screen. The pane is the list's, head\n and all: what is above the rows — which form this is, the way to another one where\n there is no column for them — belongs beside the tabs.\n\n Nothing chosen and nothing loading means the site has no forms yet, since a form\n that exists is picked for the reader. The word about it goes here rather than in\n the column, because here is what a phone shows.\n -->\n <template #list>\n <wx-skeleton v-if=\"loading\" class=\"wx-inbox__loading\" :rows=\"6\" />\n\n <submission-list\n v-else-if=\"chosen\"\n ref=\"pane\"\n :key=\"chosen.id\"\n :form=\"chosen\"\n :base=\"props.base\"\n @changed=\"load\"\n />\n\n <wx-empty v-else :title=\"t('panel.no-forms')\" :description=\"t('panel.no-forms-help')\" />\n </template>\n </wx-list-detail>\n </wx-card>\n </wx-list-screen>\n</template>\n\n<style>\n/*\n * What scrolls here is the page.\n *\n * The section used to be as tall as the window, with the rows scrolling inside a box of their\n * own: a second scroller inside the first, its bar running down the middle of the screen, and\n * a wheel that meant one thing over the rows and another an inch to the left. Page after page\n * of the panel scrolls as a page; this one now does too, and the card is as tall as what is\n * in it.\n *\n * The card's corners are still the screen's corners. The two panes inside are square and paint\n * their own background right up to the edge, so without the clip they covered the rounding —\n * four white notches poking out of the card, most visible where the column divider and the\n * bottom rule of the list meet it. `clip` rather than `hidden`: `hidden` would make this a\n * scroll container again, and everything sticky inside would pin itself to a box that never\n * moves (CLAUDE.md §4).\n */\n.wx-inbox > .wx-card__body {\n display: flex;\n flex-direction: column;\n overflow: clip;\n border-radius: inherit;\n}\n\n.wx-inbox__loading {\n padding: var(--wx-space-16);\n}\n\n.wx-inbox__forms {\n padding: var(--wx-space-8) var(--wx-space-12);\n}\n\n/* In the panel the sheet is the column, and it insets what it holds already: a second step\n inside the first stands the same list further from the edge than it stands anywhere else\n (CLAUDE.md §4). */\n.wx-inbox__forms.is-panel {\n padding: 0;\n}\n\n/*\n * A plain list draws its rows edge to edge, which is right until one of them is tinted: the\n * highlight then starts exactly at the first letter and ends exactly at the `···`, so the\n * chosen form reads as a stain rather than as a row, and it sits flush against the rule that\n * divides the two columns. The padding is inside the tint, not around it.\n */\n.wx-inbox__forms.is-plain .wx-sortable-list__row {\n padding-inline: var(--wx-space-8);\n}\n\n/*\n * Air under the rule that carries the column's name, and the rows' own step beside it.\n *\n * The first row started exactly where the line ended, and the first row here is usually the\n * tinted one — so the chosen form read as hanging off the heading rather than as the first of\n * a list. Visible in the dark theme first, where the tint is a shape of its own.\n *\n * The step is the one the rows take inside their tint (above), so the name of the column\n * begins where the names of the forms do and the `+` stands over the row of `···` rather\n * than eight pixels to the right of them. Measured: 338 against 330.\n */\n.wx-inbox__forms.is-plain .wx-sortable-list__head {\n margin-block-end: var(--wx-space-6);\n padding-inline: var(--wx-space-8);\n}\n\n.wx-inbox-form {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-2);\n align-items: flex-start;\n /* The row is the target, so it takes the row: a name of four letters should not leave\n three quarters of the line dead. */\n flex: 1 1 auto;\n min-width: 0;\n padding: 0;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n.wx-inbox-form__name {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n max-width: 100%;\n}\n\n/*\n * Switched off: struck through and grey. On the name alone and not on the line it stands in,\n * because what is off is the form — the count beside it is still true, and a decoration set\n * on the row would run through that too and could not be taken off a child. The grey comes\n * from the text's own `tone`, since `WxText` declares its colour on its own element and an\n * inherited one would never reach it (CLAUDE.md §4).\n */\n.wx-inbox-form.is-off .wx-inbox-form__name > .wx-text {\n text-decoration: line-through;\n}\n\n/*\n * The chosen form, marked on the whole row rather than on the name inside it: the row is what\n * was clicked, and a colour on the words alone leaves the grip and the menu looking like they\n * belong to something else. `:has()` because the row is the list's element and the class is\n * ours — the slot cannot reach the element it is drawn into.\n */\n.wx-inbox__forms .wx-sortable-list__row:has(.wx-inbox-form.is-current) {\n background: var(--wx-bg-subtle);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-inbox-form.is-current {\n color: var(--wx-color-primary);\n}\n\n.wx-inbox-form__silent {\n flex: none;\n color: var(--wx-color-warning);\n}\n\n.wx-inbox-form__count {\n flex: 0 0 auto;\n}\n</style>\n"],"names":["resolve","dismiss","open","useModal","context","useAdmin","api","createInboxApi","useInboxMessages","t","useTranslate","message","useErrorText","title","ref","slug","saving","errors","errorOf","field","save","error","body","toast","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","_createElementVNode","_hoisted_1","WxFormItem","WxInput","props","__props","route","useRoute","router","useRouter","locales","useLocales","forms","loading","pane","useTemplateRef","columnInline","formsOpen","canManage","computed","create","createModal","FormCreateDialog","module","current","asked","chosen","form","name","localizedValue","load","watch","choose","close","edit","actionsFor","duplicate","remove","add","made","copy","confirm","rest","reorder","actions","list","WxListScreen","WxCard","WxListDetail","_cache","_withCtx","inline","WxSkeleton","_createElementBlock","_Fragment","WxIcon","_createTextVNode","WxSortableList","_normalizeClass","item","_hoisted_2","WxText","_toDisplayString","WxIndicator","WxRowMenu","WxAction","SubmissionList","WxEmpty"],"mappings":"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"}
1
+ {"version":3,"file":"InboxPage-iMQ0pjrR.js","sources":["../src/FormCreateDialog.vue","../src/InboxPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSpace,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport type { InboxForm } from './types'\n\n/**\n * A form, started.\n *\n * Two fields, because a form has nothing else until it has questions: what it is called and\n * where it answers. Everything else — who is written to, what happens after sending, the\n * antispam — belongs to the editor, and asking for it before the form exists would be a\n * dialog longer than the screen it leads to.\n *\n * The address is typed rather than made out of the title. A page's address is a sentence\n * transliterated; a form's is a short word somebody chooses once and then writes into a\n * template by hand, and guessing `svyazhites-s-nami` for them helps nobody.\n */\nconst { resolve, dismiss, open } = useModal<InboxForm>()\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 title = ref<LocalizedValue>({})\nconst slug = ref('')\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\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 try {\n resolve(\n await api.createForm({\n title: title.value,\n slug: slug.value.trim(),\n is_enabled: true,\n options: {},\n }),\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 v-model:open=\"open\" :title=\"t('panel.new-form-title')\" :width=\"520\">\n <div class=\"wx-inbox-create\">\n <wx-form-item :label=\"t('panel.title')\" :error=\"errorOf('title')\" required>\n <wx-input v-model=\"title\" localized autofocus />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.slug')\" :help=\"t('panel.slug-help')\" :error=\"errorOf('slug')\">\n <wx-input v-model=\"slug\" placeholder=\"contact\" />\n </wx-form-item>\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\" @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-create {\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 } from 'vue-router'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n localizedValue,\n toast,\n useLocales,\n WxAction,\n WxButton,\n WxCard,\n WxEmpty,\n WxIcon,\n WxIndicator,\n WxListDetail,\n WxSkeleton,\n WxSortableList,\n WxText,\n} from '@webx-ui/core'\nimport FormCreateDialog from './FormCreateDialog.vue'\nimport SubmissionList from './SubmissionList.vue'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport type { InboxForm } from './types'\n\n/**\n * The section: the forms of the site on the left, what came in through one of them on the\n * right (§11, §2.12).\n *\n * One screen rather than two, and that is the decision this is built around. The columns of\n * the list are the fields of the chosen form, so a list of everything would have no columns\n * to speak of; and the two questions somebody opens this section to ask — \"what is new\" and\n * \"what does this form ask\" — are one click apart instead of one screen apart.\n *\n * Which of the two is the screen and which is the chooser is the second decision. The forms\n * are the chooser: they are the `filters` column of the pane, so on a phone they fold into a\n * panel and the submissions are what the section opens on. The other way round — forms as the\n * list, submissions as the record beside it — is what this was, and it cost the reader a\n * screen: somebody who opens the section is asking what came in, and was shown a list of\n * three form names instead, every time.\n *\n * One form is therefore always open (the first, unless the address names another), and what\n * is here is the column: the forms, in the order somebody dragged them, with what is waiting\n * in each.\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 forms = ref<InboxForm[]>([])\nconst loading = ref(true)\n\n/** The list of submissions, for the one action of its own that the section's head carries. */\nconst pane = useTemplateRef<{ create: () => void }>('pane')\n\n/**\n * Whether the forms still have a column, and whether their panel is up.\n *\n * Both live here rather than inside the pane because the way to the forms is in the head of\n * the section, beside its other actions — a button that appears exactly when the column is\n * gone. The pane says when that is.\n */\nconst columnInline = ref(true)\nconst formsOpen = ref(false)\n\nconst canManage = computed(() => context.can('inbox.manage'))\n\nconst create = createModal<InboxForm, Record<string, never>>(FormCreateDialog)\n\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'inbox')?.title ??\n t('module.title'),\n)\n\n/**\n * Which form is open, kept in the address.\n *\n * So that coming back from a submission lands on the form it belonged to (§11) — and so that\n * a link to \"the callback form\" is a link somebody can send.\n */\nconst current = computed<number | null>(() => {\n const asked = route.query.form\n\n return typeof asked === 'string' && asked !== '' ? Number(asked) : null\n})\n\nconst chosen = computed(() => forms.value.find((form) => form.id === current.value) ?? null)\n\nfunction name(form: InboxForm): string {\n return localizedValue(form.title, locales.active.value, form.slug)\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n forms.value = await api.forms()\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid load()\n\n/**\n * One form is always open, and the address always says which.\n *\n * The section is the submissions, so the first form stands in until somebody picks another —\n * on a phone especially, where the alternative is opening the panel before seeing anything at\n * all. It covers the address that names no form, the address that names one that has since\n * been deleted, and the list arriving after the address did; `replace`, because none of the\n * three is a place worth going back to.\n */\nwatch([forms, chosen], () => {\n if (forms.value.length === 0 || chosen.value !== null) return\n\n void router.replace({ query: { ...route.query, form: String(forms.value[0].id) } })\n})\n\nfunction choose(form: InboxForm, close: () => void): void {\n void router.replace({ query: { ...route.query, form: String(form.id) } })\n /* Picked from the panel: the panel has done its job. It is a column on a wide screen, and\n closing what is not open does nothing there. */\n close()\n}\n\nfunction edit(form: InboxForm): void {\n void router.push(`${props.base}/forms/${form.id}`)\n}\n\nfunction actionsFor(form: InboxForm): RowAction[] {\n if (!canManage.value) return []\n\n return [\n { key: 'settings', icon: 'settings', label: t('panel.settings'), run: () => edit(form) },\n { key: 'duplicate', icon: 'copy', label: t('panel.duplicate'), run: () => duplicate(form) },\n // A form that has taken submissions is switched off, never deleted (§2.4). The line is\n // left out rather than greyed: a menu is a list of what is possible.\n ...(form.submissions_count === 0\n ? [\n {\n key: 'delete',\n icon: 'trash' as const,\n label: t('panel.delete'),\n danger: true,\n run: () => remove(form),\n },\n ]\n : []),\n ]\n}\n\nasync function add(): Promise<void> {\n const made = await create({})\n\n if (made) {\n await load()\n edit(made)\n }\n}\n\nasync function duplicate(form: InboxForm): Promise<void> {\n try {\n const copy = await api.duplicateForm(form.id)\n toast.success(t('panel.duplicated'))\n await load()\n edit(copy)\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function remove(form: InboxForm): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-form-title', { form: name(form) }),\n message: t('panel.delete-form-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeForm(form.id)\n toast.success(t('panel.deleted'))\n\n /* The address named the form that is gone; what stands in its place is decided above. */\n if (current.value === form.id) {\n const rest = { ...route.query }\n delete rest.form\n void router.replace({ query: rest })\n }\n\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\n/**\n * The whole order, every time.\n *\n * The list is already in its new order on screen — `WxSortableList` reorders the model before\n * it says anything — so this only has to agree with what is there, and a failure has to put\n * the list back rather than leave the screen disagreeing with the database.\n */\nasync function reorder(): Promise<void> {\n try {\n await api.sortForms(forms.value.map((form) => form.id))\n } catch (error) {\n toast.danger(message(error, t('panel.reorder-failed')))\n await load()\n }\n}\n\n/**\n * What the section offers. Declared, because on a phone the head folds it into the ···.\n *\n * The one it exists for is a submission, not a form: this is the inbox, and the section is\n * opened dozens of times for what came in for every once that a form is added. A new form is\n * the `+` over the list of forms — beside the thing it makes one more of — and what used to\n * stand here as the primary button was that, which is why it was the biggest thing on the\n * screen and almost never what anybody wanted.\n */\nconst actions = computed<ScreenAction[]>(() => {\n const list: ScreenAction[] = []\n\n if (canManage.value) {\n list.push({\n key: 'statuses',\n label: t('panel.statuses'),\n icon: 'tag',\n run: () => void router.push(`${props.base}/statuses`),\n })\n }\n\n /* The dialog belongs to the list: what is written has to land in it, in the filter that is\n on, and be counted in its tabs. The head only asks for it. */\n if (context.can('inbox.update') && chosen.value !== null) {\n list.push({\n key: 'new-submission',\n label: t('panel.new-submission'),\n icon: 'plus',\n primary: true,\n run: () => void pane.value?.create(),\n })\n }\n\n return list\n})\n</script>\n\n<template>\n <wx-list-screen :title=\"title\" :actions=\"actions\" :card=\"false\">\n <!--\n Written here rather than declared as an action, because an action folds behind the ···\n on a narrow head and this is only ever drawn on one: it is the way to the forms for a\n reader whose column of them is gone. It stands where the other actions of the section\n stand, beside the one that writes a submission.\n -->\n <template v-if=\"!columnInline\" #actions>\n <wx-button variant=\"outline\" @click=\"formsOpen = true\">\n <template #icon><wx-icon name=\"list\" /></template>\n {{ t('panel.forms') }}\n </wx-button>\n </template>\n\n <wx-card class=\"wx-inbox\" padding=\"none\">\n <wx-list-detail\n v-model:filters-open=\"formsOpen\"\n class=\"wx-inbox__panes\"\n :filters-width=\"270\"\n :detail-min=\"420\"\n :filters-title=\"t('panel.forms')\"\n @filters-inline=\"columnInline = $event\"\n >\n <!--\n The forms are what narrows the list, so they are the column that folds: on a phone\n this is a panel raised from the head of the submissions, and the submissions are\n the screen.\n -->\n <template #filters=\"{ inline, close }\">\n <wx-skeleton v-if=\"loading\" class=\"wx-inbox__loading\" :rows=\"5\" />\n\n <template v-else>\n <!--\n In the panel the `+` would stand alone on a line of its own under the drawer's\n own heading, which reads as a stray mark rather than as a control. There is\n room for the word here, so it takes it.\n -->\n <wx-button\n v-if=\"!inline && canManage\"\n class=\"wx-inbox__new-form\"\n variant=\"outline\"\n block\n @click=\"add\"\n >\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('panel.new-form') }}\n </wx-button>\n\n <!--\n A grip and not the whole row: the row is what opens a form, and a list whose\n rows both open and drag is a list where one of the two happens by accident.\n -->\n <wx-sortable-list\n v-model=\"forms\"\n class=\"wx-inbox__forms\"\n :class=\"{ 'is-panel': !inline }\"\n plain\n size=\"sm\"\n item-key=\"id\"\n :item-label=\"name\"\n :drag-label=\"t('panel.reorder')\"\n :disabled=\"!canManage\"\n :title=\"inline ? t('panel.forms') : undefined\"\n @move=\"reorder\"\n >\n <!--\n A new form is made where the forms are and not in the head of the section: it\n is one more of these, and it is asked for once for every few dozen times\n somebody opens the inbox to read what came in. An icon, because the word for\n it is already written beside it.\n -->\n <template v-if=\"inline && canManage\" #extra>\n <wx-action icon=\"plus\" size=\"sm\" :title=\"t('panel.new-form')\" @click=\"add\" />\n </template>\n\n <template #default=\"{ item }\">\n <button\n type=\"button\"\n class=\"wx-inbox-form\"\n :class=\"{ 'is-current': item.id === current, 'is-off': !item.is_enabled }\"\n @click=\"choose(item, close)\"\n >\n <!--\n A switched-off form is said by the name itself — struck through and grey —\n rather than by a badge beside it. The badge was a second thing on a line\n 270px wide that already holds a name, a count and the ···: it did not\n shrink, so it ran under the menu, and it said in a word what the type says\n at a glance.\n -->\n <span class=\"wx-inbox-form__name\">\n <wx-text truncate weight=\"medium\" :tone=\"item.is_enabled ? 'default' : 'muted'\">\n {{ name(item) }}\n </wx-text>\n\n <!--\n On the name's line, not under it. The list stacks what the slot hands it,\n so a count standing beside the form was a third line under the address —\n the row grew by a line to say a single digit. Unread first and in colour;\n the total behind it, quietly, because it is context rather than work.\n -->\n <!-- Only on a form that is switched on: one that is off tells nobody\n because nobody can send it. -->\n <wx-icon\n v-if=\"item.is_enabled && item.notifies === false\"\n class=\"wx-inbox-form__silent\"\n name=\"warning\"\n :label=\"t('panel.notifies-nobody-short')\"\n :title=\"t('panel.notifies-nobody-short')\"\n />\n <wx-indicator\n v-if=\"item.unread_count\"\n class=\"wx-inbox-form__count\"\n type=\"primary\"\n :value=\"item.unread_count\"\n :label=\"t('panel.unread')\"\n />\n <wx-indicator\n v-else-if=\"item.submissions_count\"\n class=\"wx-inbox-form__count\"\n type=\"neutral\"\n :value=\"item.submissions_count\"\n :label=\"t('panel.submissions')\"\n />\n </span>\n <wx-text size=\"sm\" tone=\"muted\" truncate>{{ item.slug }}</wx-text>\n </button>\n </template>\n\n <template #actions=\"{ item }\">\n <wx-row-menu :actions=\"actionsFor(item)\" :label=\"name(item)\" />\n </template>\n </wx-sortable-list>\n </template>\n </template>\n\n <!--\n The submissions of the chosen form, and the screen. The pane is the list's, head\n and all: what is above the rows — which form this is, the way to another one where\n there is no column for them — belongs beside the tabs.\n\n Nothing chosen and nothing loading means the site has no forms yet, since a form\n that exists is picked for the reader. The word about it goes here rather than in\n the column, because here is what a phone shows.\n -->\n <template #list>\n <wx-skeleton v-if=\"loading\" class=\"wx-inbox__loading\" :rows=\"6\" />\n\n <submission-list\n v-else-if=\"chosen\"\n ref=\"pane\"\n :key=\"chosen.id\"\n :form=\"chosen\"\n :base=\"props.base\"\n @changed=\"load\"\n />\n\n <wx-empty v-else :title=\"t('panel.no-forms')\" :description=\"t('panel.no-forms-help')\" />\n </template>\n </wx-list-detail>\n </wx-card>\n </wx-list-screen>\n</template>\n\n<style>\n/*\n * What scrolls here is the page.\n *\n * The section used to be as tall as the window, with the rows scrolling inside a box of their\n * own: a second scroller inside the first, its bar running down the middle of the screen, and\n * a wheel that meant one thing over the rows and another an inch to the left. Page after page\n * of the panel scrolls as a page; this one now does too, and the card is as tall as what is\n * in it.\n *\n * The card's corners are still the screen's corners. The two panes inside are square and paint\n * their own background right up to the edge, so without the clip they covered the rounding —\n * four white notches poking out of the card, most visible where the column divider and the\n * bottom rule of the list meet it. `clip` rather than `hidden`: `hidden` would make this a\n * scroll container again, and everything sticky inside would pin itself to a box that never\n * moves (CLAUDE.md §4).\n */\n.wx-inbox > .wx-card__body {\n display: flex;\n flex-direction: column;\n overflow: clip;\n border-radius: inherit;\n}\n\n.wx-inbox__loading {\n padding: var(--wx-space-16);\n}\n\n.wx-inbox__forms {\n padding: var(--wx-space-8) var(--wx-space-12);\n}\n\n/* In the panel the sheet is the column, and it insets what it holds already: a second step\n inside the first stands the same list further from the edge than it stands anywhere else\n (CLAUDE.md §4). */\n.wx-inbox__forms.is-panel {\n padding: 0;\n}\n\n/*\n * A plain list draws its rows edge to edge, which is right until one of them is tinted: the\n * highlight then starts exactly at the first letter and ends exactly at the `···`, so the\n * chosen form reads as a stain rather than as a row, and it sits flush against the rule that\n * divides the two columns. The padding is inside the tint, not around it.\n */\n.wx-inbox__forms.is-plain .wx-sortable-list__row {\n padding-inline: var(--wx-space-8);\n}\n\n/*\n * Air under the rule that carries the column's name, and the rows' own step beside it.\n *\n * The first row started exactly where the line ended, and the first row here is usually the\n * tinted one — so the chosen form read as hanging off the heading rather than as the first of\n * a list. Visible in the dark theme first, where the tint is a shape of its own.\n *\n * The step is the one the rows take inside their tint (above), so the name of the column\n * begins where the names of the forms do and the `+` stands over the row of `···` rather\n * than eight pixels to the right of them. Measured: 338 against 330.\n */\n.wx-inbox__forms.is-plain .wx-sortable-list__head {\n margin-block-end: var(--wx-space-6);\n padding-inline: var(--wx-space-8);\n}\n\n.wx-inbox-form {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-2);\n align-items: flex-start;\n /* The row is the target, so it takes the row: a name of four letters should not leave\n three quarters of the line dead. */\n flex: 1 1 auto;\n min-width: 0;\n padding: 0;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: start;\n cursor: pointer;\n}\n\n.wx-inbox-form__name {\n display: flex;\n align-items: center;\n gap: var(--wx-space-6);\n min-width: 0;\n max-width: 100%;\n}\n\n/*\n * Switched off: struck through and grey. On the name alone and not on the line it stands in,\n * because what is off is the form — the count beside it is still true, and a decoration set\n * on the row would run through that too and could not be taken off a child. The grey comes\n * from the text's own `tone`, since `WxText` declares its colour on its own element and an\n * inherited one would never reach it (CLAUDE.md §4).\n */\n.wx-inbox-form.is-off .wx-inbox-form__name > .wx-text {\n text-decoration: line-through;\n}\n\n/*\n * The chosen form, marked on the whole row rather than on the name inside it: the row is what\n * was clicked, and a colour on the words alone leaves the grip and the menu looking like they\n * belong to something else. `:has()` because the row is the list's element and the class is\n * ours — the slot cannot reach the element it is drawn into.\n */\n.wx-inbox__forms .wx-sortable-list__row:has(.wx-inbox-form.is-current) {\n background: var(--wx-bg-subtle);\n border-radius: var(--wx-radius-sm);\n}\n\n.wx-inbox-form.is-current {\n color: var(--wx-color-primary);\n}\n\n.wx-inbox-form__silent {\n flex: none;\n color: var(--wx-color-warning);\n}\n\n.wx-inbox-form__count {\n flex: 0 0 auto;\n}\n</style>\n"],"names":["resolve","dismiss","open","useModal","context","useAdmin","api","createInboxApi","useInboxMessages","t","useTranslate","message","useErrorText","title","ref","slug","saving","errors","errorOf","field","save","error","body","toast","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","_createElementVNode","_hoisted_1","WxFormItem","WxInput","props","__props","route","useRoute","router","useRouter","locales","useLocales","forms","loading","pane","useTemplateRef","columnInline","formsOpen","canManage","computed","create","createModal","FormCreateDialog","module","current","asked","chosen","form","name","localizedValue","load","watch","choose","close","edit","actionsFor","duplicate","remove","add","made","copy","confirm","rest","reorder","actions","list","WxListScreen","WxCard","WxListDetail","_cache","_withCtx","inline","WxSkeleton","_createElementBlock","_Fragment","WxIcon","_createTextVNode","WxSortableList","_normalizeClass","item","_hoisted_2","WxText","_toDisplayString","WxIndicator","WxRowMenu","WxAction","SubmissionList","WxEmpty"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { defineComponent as O, ref as d, watch as J, computed as R, openBlock as v, createBlock as A, unref as e, isRef as K, withCtx as i, createElementVNode as N, createVNode as u, createTextVNode as g, toDisplayString as m, createElementBlock as I, Fragment as D, createCommentVNode as $ } from "vue";
2
2
  import { useAdmin as q, useTranslate as P, useErrorText as h, WxListScreen as Q, WxRowMenu as X } from "@webx-ui/module-admin";
3
3
  import { useModal as Y, WxDialog as Z, WxFormItem as E, WxInput as F, WxSelect as ee, WxCheckbox as z, WxSpace as ae, WxButton as L, toast as w, createModal as le, WxEmpty as te, WxSortableList as se, WxBadge as oe, WxText as ne, confirm as ue } from "@webx-ui/core";
4
- import { c as j, u as G, b as re, _ as H, d as ie } from "./index-BJ-mLCee.js";
4
+ import { c as j, u as G, b as re, _ as H, d as ie } from "./index-gLN73Ici.js";
5
5
  const de = { class: "wx-inbox-status-form" }, ce = { class: "wx-inbox-status-form__checks" }, pe = /* @__PURE__ */ O({
6
6
  __name: "StatusDialog",
7
7
  props: {
@@ -293,4 +293,4 @@ const de = { class: "wx-inbox-status-form" }, ce = { class: "wx-inbox-status-for
293
293
  export {
294
294
  ke as default
295
295
  };
296
- //# sourceMappingURL=StatusesPage-xVeRPm4u.js.map
296
+ //# sourceMappingURL=StatusesPage-arNMRLzM.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StatusesPage-xVeRPm4u.js","sources":["../src/StatusDialog.vue","../src/StatusesPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxCheckbox,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport { STATUS_COLORS, type InboxStatus, type StatusColor } from './types'\n\n/**\n * One state, written.\n *\n * The colour is a tone rather than a swatch: the panel has a light theme and a dark one, and\n * a colour picked in one of them is unreadable in the other.\n *\n * Nothing here refuses a second default — the server clears the flag on whoever had it, which\n * is what somebody moving the default from one status to another means, and refusing the save\n * would be the other reading of the same click.\n */\nconst props = defineProps<{ status: InboxStatus | null }>()\n\nconst { resolve, dismiss, open } = useModal<InboxStatus>()\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 key = ref('')\nconst title = ref<LocalizedValue>({})\nconst color = ref<StatusColor>('default')\nconst isDefault = ref(false)\nconst isSpam = ref(false)\nconst isClosed = ref(false)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nwatch(\n () => props.status,\n (status) => {\n key.value = status?.key ?? ''\n title.value = { ...(status?.title ?? {}) }\n color.value = status?.color ?? 'default'\n isDefault.value = status?.is_default ?? false\n isSpam.value = status?.is_spam ?? false\n isClosed.value = status?.is_closed ?? false\n errors.value = {}\n },\n { immediate: true },\n)\n\nconst colors = computed(() =>\n STATUS_COLORS.map((one) => ({ value: one, label: t(`panel.color-${one}`) })),\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 key: key.value.trim(),\n title: title.value,\n color: color.value,\n is_default: isDefault.value,\n is_spam: isSpam.value,\n is_closed: isClosed.value,\n }\n\n try {\n resolve(\n props.status === null\n ? await api.createStatus(input)\n : await api.saveStatus(props.status.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=\"status ? t('panel.settings') : t('panel.new-status')\"\n :width=\"520\"\n >\n <div class=\"wx-inbox-status-form\">\n <wx-form-item :label=\"t('panel.title')\" :error=\"errorOf('title')\" required>\n <wx-input v-model=\"title\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.status-key')\" :error=\"errorOf('key')\" required>\n <wx-input v-model=\"key\" placeholder=\"in-progress\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.status-color')\" :error=\"errorOf('color')\">\n <wx-select v-model=\"color\" :options=\"colors\" />\n </wx-form-item>\n\n <div class=\"wx-inbox-status-form__checks\">\n <wx-checkbox v-model=\"isDefault\" :label=\"t('panel.status-default')\" />\n <wx-checkbox v-model=\"isSpam\" :label=\"t('panel.status-spam')\" />\n <wx-checkbox v-model=\"isClosed\" :disabled=\"isSpam\" :label=\"t('panel.status-closed')\" />\n </div>\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\" @click=\"save\">{{ t('panel.save') }}</wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-inbox-status-form {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n\n.wx-inbox-status-form__checks {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-16);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n WxBadge,\n WxEmpty,\n WxSortableList,\n WxText,\n} from '@webx-ui/core'\nimport StatusDialog from './StatusDialog.vue'\nimport { createInboxApi } from './api'\nimport { useInboxMessages, useStatusName } from './i18n'\nimport type { InboxStatus } from './types'\n\n/**\n * The states a submission can be in (§2.11).\n *\n * Rows rather than an enum, because every panel ends up wanting its own words — and the flags\n * are what the section reads, not the keys, so renaming \"New\" to whatever this client calls\n * it keeps it the status a new submission gets.\n *\n * Their order is the order of the tabs over the list of submissions, which is why it is\n * dragged rather than typed.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/inbox' })\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 statuses = ref<InboxStatus[]>([])\nconst loading = ref(true)\n\nconst canManage = context.can('inbox.manage')\n\nconst edit = createModal<InboxStatus, { status: InboxStatus | null }>(StatusDialog)\n\n/* In the panel's language, not the content's: a status is a word of the interface. */\nconst name = useStatusName()\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n statuses.value = await api.statuses()\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid load()\n\nasync function open(status: InboxStatus | null): Promise<void> {\n if (await edit({ status })) await load()\n}\n\nasync function remove(status: InboxStatus): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-status-title', { status: name(status) }),\n message: t('panel.delete-status-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeStatus(status.id)\n toast.success(t('panel.deleted'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actionsFor(status: InboxStatus): RowAction[] {\n if (!canManage) return []\n\n return [\n { key: 'edit', icon: 'edit', label: t('panel.settings'), run: () => void open(status) },\n // A status with submissions in them cannot go: they would have nothing to be in. The\n // line is left out rather than greyed, the way every list of the panel does it.\n ...(status.submissions_count === 0\n ? [\n {\n key: 'delete',\n icon: 'trash' as const,\n label: t('panel.delete'),\n danger: true,\n run: () => void remove(status),\n },\n ]\n : []),\n ]\n}\n\n/* What the section offers. Declared, because on a phone the head folds it into the ···. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage\n ? [\n {\n key: 'new',\n label: t('panel.new-status'),\n icon: 'plus',\n primary: true,\n run: () => void open(null),\n },\n ]\n : [],\n)\n\nasync function reorder(): Promise<void> {\n try {\n await api.sortStatuses(statuses.value.map((status) => status.id))\n } catch (error) {\n toast.danger(message(error, t('panel.reorder-failed')))\n await load()\n }\n}\n</script>\n\n<template>\n <div class=\"wx-inbox-statuses\">\n <wx-list-screen\n :title=\"t('panel.statuses')\"\n :back=\"props.base\"\n :back-label=\"t('panel.forms')\"\n :actions=\"actions\"\n >\n <wx-empty v-if=\"!loading && statuses.length === 0\" :title=\"t('panel.no-statuses')\" />\n\n <wx-sortable-list\n v-else\n v-model=\"statuses\"\n plain\n item-key=\"id\"\n :item-label=\"name\"\n :drag-label=\"t('panel.reorder')\"\n :disabled=\"!canManage\"\n :aria-label=\"t('panel.statuses')\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <div class=\"wx-inbox-status\">\n <wx-badge :type=\"item.color\">{{ name(item) }}</wx-badge>\n\n <wx-text size=\"sm\" tone=\"muted\" truncate>\n <code>{{ item.key }}</code>\n <template v-if=\"item.is_default\"> · {{ t('panel.status-default') }}</template>\n <template v-if=\"item.is_spam\"> · {{ t('panel.status-spam') }}</template>\n <template v-else-if=\"item.is_closed\"> · {{ t('panel.status-closed') }}</template>\n <template v-if=\"item.submissions_count\">\n · {{ t('panel.in-use') }} ({{ item.submissions_count }})\n </template>\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-list-screen>\n </div>\n</template>\n\n<style scoped>\n.wx-inbox-statuses {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--wx-space-12);\n}\n\n.wx-inbox-statuses > .wx-list-screen {\n align-self: stretch;\n}\n\n.wx-inbox-status {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n flex: 1 1 auto;\n min-width: 0;\n flex-wrap: wrap;\n}\n</style>\n"],"names":["props","__props","resolve","dismiss","open","useModal","context","useAdmin","api","createInboxApi","useInboxMessages","t","useTranslate","message","useErrorText","key","ref","title","color","isDefault","isSpam","isClosed","saving","errors","watch","status","colors","computed","STATUS_COLORS","one","errorOf","field","save","input","error","body","toast","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","_createElementVNode","_hoisted_1","WxFormItem","WxInput","WxSelect","_hoisted_2","WxCheckbox","statuses","loading","canManage","edit","createModal","StatusDialog","name","useStatusName","load","remove","confirm","actionsFor","actions","reorder","_openBlock","_createElementBlock","WxListScreen","WxEmpty","WxSortableList","_withCtx","item","WxBadge","_createTextVNode","_toDisplayString","WxText","_Fragment","WxRowMenu"],"mappings":"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"}
1
+ {"version":3,"file":"StatusesPage-arNMRLzM.js","sources":["../src/StatusDialog.vue","../src/StatusesPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxCheckbox,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n type LocalizedValue,\n} from '@webx-ui/core'\nimport { createInboxApi } from './api'\nimport { useInboxMessages } from './i18n'\nimport { STATUS_COLORS, type InboxStatus, type StatusColor } from './types'\n\n/**\n * One state, written.\n *\n * The colour is a tone rather than a swatch: the panel has a light theme and a dark one, and\n * a colour picked in one of them is unreadable in the other.\n *\n * Nothing here refuses a second default — the server clears the flag on whoever had it, which\n * is what somebody moving the default from one status to another means, and refusing the save\n * would be the other reading of the same click.\n */\nconst props = defineProps<{ status: InboxStatus | null }>()\n\nconst { resolve, dismiss, open } = useModal<InboxStatus>()\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 key = ref('')\nconst title = ref<LocalizedValue>({})\nconst color = ref<StatusColor>('default')\nconst isDefault = ref(false)\nconst isSpam = ref(false)\nconst isClosed = ref(false)\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nwatch(\n () => props.status,\n (status) => {\n key.value = status?.key ?? ''\n title.value = { ...(status?.title ?? {}) }\n color.value = status?.color ?? 'default'\n isDefault.value = status?.is_default ?? false\n isSpam.value = status?.is_spam ?? false\n isClosed.value = status?.is_closed ?? false\n errors.value = {}\n },\n { immediate: true },\n)\n\nconst colors = computed(() =>\n STATUS_COLORS.map((one) => ({ value: one, label: t(`panel.color-${one}`) })),\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 key: key.value.trim(),\n title: title.value,\n color: color.value,\n is_default: isDefault.value,\n is_spam: isSpam.value,\n is_closed: isClosed.value,\n }\n\n try {\n resolve(\n props.status === null\n ? await api.createStatus(input)\n : await api.saveStatus(props.status.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=\"status ? t('panel.settings') : t('panel.new-status')\"\n :width=\"520\"\n >\n <div class=\"wx-inbox-status-form\">\n <wx-form-item :label=\"t('panel.title')\" :error=\"errorOf('title')\" required>\n <wx-input v-model=\"title\" localized />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.status-key')\" :error=\"errorOf('key')\" required>\n <wx-input v-model=\"key\" placeholder=\"in-progress\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.status-color')\" :error=\"errorOf('color')\">\n <wx-select v-model=\"color\" :options=\"colors\" />\n </wx-form-item>\n\n <div class=\"wx-inbox-status-form__checks\">\n <wx-checkbox v-model=\"isDefault\" :label=\"t('panel.status-default')\" />\n <wx-checkbox v-model=\"isSpam\" :label=\"t('panel.status-spam')\" />\n <wx-checkbox v-model=\"isClosed\" :disabled=\"isSpam\" :label=\"t('panel.status-closed')\" />\n </div>\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\" @click=\"save\">{{ t('panel.save') }}</wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n.wx-inbox-status-form {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n}\n\n.wx-inbox-status-form__checks {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-16);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport {\n confirm,\n createModal,\n toast,\n WxBadge,\n WxEmpty,\n WxSortableList,\n WxText,\n} from '@webx-ui/core'\nimport StatusDialog from './StatusDialog.vue'\nimport { createInboxApi } from './api'\nimport { useInboxMessages, useStatusName } from './i18n'\nimport type { InboxStatus } from './types'\n\n/**\n * The states a submission can be in (§2.11).\n *\n * Rows rather than an enum, because every panel ends up wanting its own words — and the flags\n * are what the section reads, not the keys, so renaming \"New\" to whatever this client calls\n * it keeps it the status a new submission gets.\n *\n * Their order is the order of the tabs over the list of submissions, which is why it is\n * dragged rather than typed.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/inbox' })\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 statuses = ref<InboxStatus[]>([])\nconst loading = ref(true)\n\nconst canManage = context.can('inbox.manage')\n\nconst edit = createModal<InboxStatus, { status: InboxStatus | null }>(StatusDialog)\n\n/* In the panel's language, not the content's: a status is a word of the interface. */\nconst name = useStatusName()\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n statuses.value = await api.statuses()\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid load()\n\nasync function open(status: InboxStatus | null): Promise<void> {\n if (await edit({ status })) await load()\n}\n\nasync function remove(status: InboxStatus): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-status-title', { status: name(status) }),\n message: t('panel.delete-status-text'),\n confirmText: t('panel.delete'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.removeStatus(status.id)\n toast.success(t('panel.deleted'))\n await load()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actionsFor(status: InboxStatus): RowAction[] {\n if (!canManage) return []\n\n return [\n { key: 'edit', icon: 'edit', label: t('panel.settings'), run: () => void open(status) },\n // A status with submissions in them cannot go: they would have nothing to be in. The\n // line is left out rather than greyed, the way every list of the panel does it.\n ...(status.submissions_count === 0\n ? [\n {\n key: 'delete',\n icon: 'trash' as const,\n label: t('panel.delete'),\n danger: true,\n run: () => void remove(status),\n },\n ]\n : []),\n ]\n}\n\n/* What the section offers. Declared, because on a phone the head folds it into the ···. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage\n ? [\n {\n key: 'new',\n label: t('panel.new-status'),\n icon: 'plus',\n primary: true,\n run: () => void open(null),\n },\n ]\n : [],\n)\n\nasync function reorder(): Promise<void> {\n try {\n await api.sortStatuses(statuses.value.map((status) => status.id))\n } catch (error) {\n toast.danger(message(error, t('panel.reorder-failed')))\n await load()\n }\n}\n</script>\n\n<template>\n <div class=\"wx-inbox-statuses\">\n <wx-list-screen\n :title=\"t('panel.statuses')\"\n :back=\"props.base\"\n :back-label=\"t('panel.forms')\"\n :actions=\"actions\"\n >\n <wx-empty v-if=\"!loading && statuses.length === 0\" :title=\"t('panel.no-statuses')\" />\n\n <wx-sortable-list\n v-else\n v-model=\"statuses\"\n plain\n item-key=\"id\"\n :item-label=\"name\"\n :drag-label=\"t('panel.reorder')\"\n :disabled=\"!canManage\"\n :aria-label=\"t('panel.statuses')\"\n @move=\"reorder\"\n >\n <template #default=\"{ item }\">\n <div class=\"wx-inbox-status\">\n <wx-badge :type=\"item.color\">{{ name(item) }}</wx-badge>\n\n <wx-text size=\"sm\" tone=\"muted\" truncate>\n <code>{{ item.key }}</code>\n <template v-if=\"item.is_default\"> · {{ t('panel.status-default') }}</template>\n <template v-if=\"item.is_spam\"> · {{ t('panel.status-spam') }}</template>\n <template v-else-if=\"item.is_closed\"> · {{ t('panel.status-closed') }}</template>\n <template v-if=\"item.submissions_count\">\n · {{ t('panel.in-use') }} ({{ item.submissions_count }})\n </template>\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-list-screen>\n </div>\n</template>\n\n<style scoped>\n.wx-inbox-statuses {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--wx-space-12);\n}\n\n.wx-inbox-statuses > .wx-list-screen {\n align-self: stretch;\n}\n\n.wx-inbox-status {\n display: flex;\n align-items: center;\n gap: var(--wx-space-8);\n flex: 1 1 auto;\n min-width: 0;\n flex-wrap: wrap;\n}\n</style>\n"],"names":["props","__props","resolve","dismiss","open","useModal","context","useAdmin","api","createInboxApi","useInboxMessages","t","useTranslate","message","useErrorText","key","ref","title","color","isDefault","isSpam","isClosed","saving","errors","watch","status","colors","computed","STATUS_COLORS","one","errorOf","field","save","input","error","body","toast","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","_createElementVNode","_hoisted_1","WxFormItem","WxInput","WxSelect","_hoisted_2","WxCheckbox","statuses","loading","canManage","edit","createModal","StatusDialog","name","useStatusName","load","remove","confirm","actionsFor","actions","reorder","_openBlock","_createElementBlock","WxListScreen","WxEmpty","WxSortableList","_withCtx","item","WxBadge","_createTextVNode","_toDisplayString","WxText","_Fragment","WxRowMenu"],"mappings":"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"}