@webx-ui/module-notifications 0.0.0-stage → 0.2.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":"NotificationsPage-qWdhMayl.js","sources":["../src/NewChannelDialog.vue","../src/NotificationsPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport {\n useModal,\n WxButton,\n WxDialog,\n WxFormItem,\n WxSegmented,\n WxSelect,\n WxSpace,\n WxText,\n} from '@webx-ui/core'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport { useNotificationsMessages } from './i18n'\nimport type { NotificationsMeta } from './types'\n\n/**\n * \"New channel\" (§10.2): the kind first — a channel of another kind is another channel — and the\n * event if it is for one, which is what fills the template with the event's sample.\n */\ndefineOptions({ name: 'WxNotificationsNewChannel' })\n\nconst props = defineProps<{ meta: NotificationsMeta }>()\n\nconst { open, resolve, dismiss } = useModal<{ provider: string; event: string | null }>()\n\nuseNotificationsMessages()\n\nconst t = useTranslate('webx-notifications')\n\nconst provider = ref(props.meta.providers[0]?.key ?? '')\nconst event = ref<string | null>(null)\n\nconst providers = computed(() =>\n props.meta.providers.map((one) => ({ value: one.key, label: one.label, icon: one.icon })),\n)\n\nconst events = computed(() => [\n { value: '', label: t('panel.any-event') },\n ...props.meta.events.map((one) => ({ value: one.key, label: one.label })),\n])\n\nfunction go(): void {\n resolve({ provider: provider.value, event: event.value === '' ? null : event.value })\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('panel.new-title')\" :width=\"460\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('panel.new-help') }}</wx-text>\n\n <wx-form-item :label=\"t('panel.new-provider')\">\n <wx-segmented v-model=\"provider\" :options=\"providers\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('panel.new-event')\">\n <wx-select\n :model-value=\"event ?? ''\"\n :options=\"events\"\n @update:model-value=\"(value) => (event = value === '' ? null : String(value))\"\n />\n </wx-form-item>\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\" :disabled=\"provider === ''\" @click=\"go\">\n {{ t('panel.new-create') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n confirm,\n createModal,\n toast,\n WxAlert,\n WxBadge,\n WxFormItem,\n WxIcon,\n WxSelect,\n WxSwitch,\n WxTable,\n WxText,\n type IconName,\n type TabItem,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport {\n rowMenuWidth,\n useAdmin,\n useErrorText,\n useTranslate,\n WxDate,\n WxListScreen,\n WxRowMenu,\n type RowAction,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { createNotificationsApi } from './api'\nimport { useNotificationsMessages } from './i18n'\nimport NewChannelDialog from './NewChannelDialog.vue'\nimport type {\n ChannelRow,\n DeliveriesPage,\n DeliveryRow,\n DeliveryStatus,\n NotificationsMeta,\n} from './types'\n\n/**\n * The section (§10.1): the channels, the log of what they sent, and the bin — three views of one\n * list screen, the open one in the address (`?view=log`), so a link to the log is a link to it.\n *\n * A channel opens on a page of its own: its form is long, and the preview beside it needs the\n * width a pane next to a list would take away.\n */\nconst props = withDefaults(defineProps<{ base?: string }>(), { base: '/notifications' })\n\nconst admin = useAdmin()\nconst api = createNotificationsApi(admin)\nconst route = useRoute()\nconst router = useRouter()\nuseNotificationsMessages()\n\nconst t = useTranslate('webx-notifications')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst canManage = computed(() => admin.can('notifications.manage'))\n\nconst title = computed(\n () =>\n admin.state.manifest?.modules.find((module) => module.id === 'notifications')?.title ??\n t('module.title'),\n)\n\ntype View = '' | 'log' | 'bin'\n\nconst view = computed<View>({\n get: () => {\n const asked = route.query.view\n\n return asked === 'log' || asked === 'bin' ? asked : ''\n },\n set: (value) => {\n void router.replace({\n query: { ...route.query, view: value === '' ? undefined : value, page: undefined },\n })\n },\n})\n\nconst meta = ref<NotificationsMeta | null>(null)\nconst channels = ref<ChannelRow[]>([])\nconst bin = ref<ChannelRow[]>([])\nconst environment = ref('production')\nconst loading = ref(true)\n\nconst views = computed<TabItem[]>(() => [\n { value: '', label: t('panel.channels'), badge: channels.value.length },\n { value: 'log', label: t('panel.log') },\n { value: 'bin', label: t('panel.bin'), badge: bin.value.length || undefined },\n])\n\nasync function loadChannels(): Promise<void> {\n loading.value = true\n\n try {\n const [live, trashed] = await Promise.all([api.channels(), api.channels({ trashed: true })])\n\n channels.value = live.data\n bin.value = trashed.data\n environment.value = live.environment\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nvoid loadChannels()\napi\n .meta()\n .then((found) => (meta.value = found))\n .catch(() => undefined)\n\nconst create = createModal<{ provider: string; event: string | null }, { meta: NotificationsMeta }>(\n NewChannelDialog,\n)\n\nasync function add(): Promise<void> {\n if (meta.value === null) return\n\n const chosen = await create({ meta: meta.value })\n\n if (!chosen) return\n\n void router.push({\n path: `${props.base}/channels/new`,\n query: { provider: chosen.provider, ...(chosen.event ? { event: chosen.event } : {}) },\n })\n}\n\nfunction open(row: ChannelRow): void {\n void router.push(`${props.base}/channels/${row.id}`)\n}\n\nasync function toggle(row: ChannelRow, enabled: boolean): Promise<void> {\n try {\n const detail = await api.save(row.id, { enabled })\n\n channels.value = channels.value.map((one) => (one.id === row.id ? detail.channel : one))\n toast.success(enabled ? t('panel.enabled') : t('panel.disabled'))\n } catch (error) {\n // Switching on a channel that is not complete is refused in the server's words (§3.3).\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n toast.danger(body?.errors ? Object.values(body.errors).flat().join(' ') : message(error))\n }\n}\n\nasync function copy(row: ChannelRow): Promise<void> {\n try {\n const detail = await api.copy(row.id)\n\n toast.success(t('panel.copied', { name: detail.channel.name }))\n await loadChannels()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function remove(row: ChannelRow): Promise<void> {\n const agreed = await confirm({\n title: t('panel.delete-title', { title: row.name }),\n message: t('panel.delete-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.remove(row.id)\n toast.success(t('panel.deleted'))\n await loadChannels()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function restore(row: ChannelRow): Promise<void> {\n try {\n await api.restore(row.id)\n toast.success(t('panel.restored'))\n await loadChannels()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nasync function purge(row: ChannelRow): Promise<void> {\n const agreed = await confirm({\n title: t('panel.purge-title', { title: row.name }),\n message: t('panel.purge-text'),\n confirmText: t('panel.purge'),\n cancelText: t('panel.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.purge(row.id)\n toast.success(t('panel.purged'))\n await loadChannels()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction actionsFor(row: ChannelRow): RowAction[] {\n if (view.value === 'bin') {\n return canManage.value\n ? [\n {\n key: 'restore',\n label: t('panel.restore'),\n icon: 'refresh',\n run: () => void restore(row),\n },\n {\n key: 'purge',\n label: t('panel.purge'),\n icon: 'trash',\n danger: true,\n run: () => void purge(row),\n },\n ]\n : []\n }\n\n return [\n { key: 'edit', label: t('panel.edit'), icon: 'edit', run: () => open(row) },\n ...(canManage.value\n ? [\n {\n key: 'copy',\n label: t('panel.copy'),\n icon: 'copy' as IconName,\n run: () => void copy(row),\n },\n {\n key: 'delete',\n label: t('panel.delete'),\n icon: 'trash' as IconName,\n danger: true,\n run: () => void remove(row),\n },\n ]\n : []),\n ]\n}\n\n/*\n * The name and the menu stay at every width; the event goes first, then the last delivery and the\n * count — on a phone a row is the name, the switch and the `···`.\n */\nconst channelColumns = computed<TableColumn<ChannelRow>[]>(() => [\n { key: 'name', label: t('module.channel'), minWidth: 160 },\n { key: 'event', label: t('panel.new-event'), width: 200, hideBelow: 720 },\n { key: 'last_delivery', label: t('panel.column-status'), width: 170, hideBelow: 560 },\n ...(view.value === 'bin'\n ? []\n : [{ key: 'enabled', label: '', width: 64, align: 'center' as const }]),\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n])\n\nconst rows = computed(() => (view.value === 'bin' ? bin.value : channels.value))\n\nfunction iconOf(row: { icon?: string | null; provider: string }): IconName {\n return (row.icon ?? (row.provider === 'telegram' ? 'send' : 'mail')) as IconName\n}\n\n/* ---------------------------------------------------------------------------- the log ----- */\n\nconst page = ref<DeliveriesPage | null>(null)\nconst logLoading = ref(false)\nconst status = ref<DeliveryStatus | ''>('')\nconst channelFilter = ref<number | ''>('')\nconst withTests = ref(true)\nconst lastState = ref<TableState | undefined>(undefined)\n\nasync function loadLog(state?: TableState): Promise<void> {\n lastState.value = state ?? lastState.value\n logLoading.value = true\n\n try {\n page.value = await api.deliveries({\n page: lastState.value?.page ?? 1,\n per_page: lastState.value?.perPage,\n status: status.value === '' ? null : status.value,\n channel: channelFilter.value === '' ? null : channelFilter.value,\n tests: withTests.value,\n })\n environment.value = page.value.environment\n } catch (error) {\n toast.danger(message(error))\n } finally {\n logLoading.value = false\n }\n}\n\nwatch(\n view,\n (next) => {\n if (next === 'log') void loadLog()\n },\n { immediate: true },\n)\n\nwatch(\n [status, channelFilter, withTests],\n () => void loadLog({ ...(lastState.value ?? {}), page: 1 } as TableState),\n)\n\nconst filtersCount = computed(\n () => Number(status.value !== '') + Number(channelFilter.value !== '') + Number(!withTests.value),\n)\n\nconst statusOptions = computed(() => [\n { value: '', label: t('panel.all-statuses') },\n ...(['queued', 'sent', 'failed', 'skipped'] as const).map((value) => ({\n value,\n label: t(`module.statuses.${value}`),\n })),\n])\n\nconst channelOptions = computed(() => [\n { value: '', label: t('panel.all-channels') },\n ...channels.value.map((one) => ({ value: one.id, label: one.name })),\n])\n\nconst logColumns = computed<TableColumn<DeliveryRow>[]>(() => [\n { key: 'created_at', label: t('panel.column-when'), width: 150 },\n { key: 'recipient', label: t('panel.column-recipient'), minWidth: 160 },\n { key: 'channel_name', label: t('panel.column-channel'), width: 180, hideBelow: 760 },\n { key: 'status', label: t('panel.column-status'), width: 140 },\n { key: 'attempts', label: t('panel.column-attempts'), width: 90, align: 'right', hideBelow: 900 },\n { key: 'actions', label: '', width: rowMenuWidth, align: 'right' },\n])\n\nconst tone: Record<DeliveryStatus, 'success' | 'danger' | 'warning' | 'default'> = {\n sent: 'success',\n failed: 'danger',\n queued: 'warning',\n skipped: 'default',\n}\n\nasync function retry(row: DeliveryRow): Promise<void> {\n try {\n await api.retry(row.id)\n toast.success(t('panel.retried'))\n await loadLog()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n\nfunction logActionsFor(row: DeliveryRow): RowAction[] {\n const list: RowAction[] = []\n\n if (row.subject_url) {\n list.push({\n key: 'open',\n label: t('panel.open-subject'),\n icon: 'external-link',\n href: row.subject_url,\n target: '_self',\n })\n }\n\n if (row.channel_id !== null) {\n list.push({\n key: 'channel',\n label: t('panel.edit'),\n icon: 'edit',\n run: () => void router.push(`${props.base}/channels/${row.channel_id}`),\n })\n }\n\n if (row.retryable && canManage.value) {\n list.push({\n key: 'retry',\n label: t('panel.retry'),\n icon: 'refresh',\n run: () => void retry(row),\n })\n }\n\n return list\n}\n\nfunction why(row: DeliveryRow): string | null {\n if (row.error) return row.error\n if (row.reason) return t(`module.reasons.${row.reason}`)\n\n return null\n}\n\nconst actions = computed<ScreenAction[]>(() =>\n canManage.value\n ? [\n {\n key: 'new',\n label: t('panel.new'),\n icon: 'plus',\n primary: true,\n disabled: meta.value === null,\n run: () => void add(),\n },\n ]\n : [],\n)\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"view\" :title=\"title\" :views=\"views\" :actions=\"actions\">\n <!-- Over the rows and not over the tabs: it is about what the channels do, not about a view. -->\n <div v-if=\"environment !== 'production'\" class=\"wx-notifications-list__note\">\n <wx-alert type=\"info\" variant=\"soft\" :description=\"t('panel.environment', { environment })\" />\n </div>\n\n <wx-table\n v-if=\"view !== 'log'\"\n :data=\"rows\"\n :columns=\"channelColumns\"\n row-key=\"id\"\n hover\n clickable\n flush\n layout=\"fixed\"\n :loading=\"loading\"\n :cards-below=\"0\"\n :pagination=\"false\"\n :empty-text=\"view === 'bin' ? t('panel.empty-bin') : t('panel.empty-channels-text')\"\n :aria-label=\"title\"\n @row-click=\"(row: ChannelRow) => view !== 'bin' && open(row)\"\n >\n <template #cell-name=\"{ row }\">\n <span class=\"wx-notifications-list__name\">\n <wx-icon :name=\"iconOf(row)\" class=\"wx-notifications-list__icon\" />\n <span class=\"wx-notifications-list__text\">\n <wx-text truncate weight=\"medium\">{{ row.name }}</wx-text>\n <wx-text size=\"sm\" tone=\"muted\" truncate>\n {{ row.provider_label }} ·\n {{ t('panel.recipients', { count: String(row.recipients) }) }}\n </wx-text>\n </span>\n </span>\n </template>\n\n <template #cell-event=\"{ row }\">\n <wx-badge v-if=\"!row.event_declared\" type=\"warning\">{{\n t('panel.event-undeclared')\n }}</wx-badge>\n <wx-text v-else size=\"sm\" :tone=\"row.event ? 'default' : 'muted'\" truncate>\n {{ row.event_label ?? t('panel.any-event') }}\n </wx-text>\n </template>\n\n <template #cell-last_delivery=\"{ row }\">\n <span v-if=\"row.last_delivery\" class=\"wx-notifications-list__last\">\n <wx-badge :type=\"tone[row.last_delivery.status]\" dot>\n {{ t(`module.statuses.${row.last_delivery.status}`) }}\n </wx-badge>\n <wx-date :value=\"row.last_delivery.at\" class=\"wx-notifications-list__when\" />\n </span>\n <wx-text v-else size=\"sm\" tone=\"muted\">{{ t('panel.never-sent') }}</wx-text>\n </template>\n\n <template #cell-enabled=\"{ row }\">\n <span @click.stop>\n <wx-switch\n :model-value=\"row.enabled\"\n :disabled=\"!canManage\"\n :aria-label=\"t('panel.enable')\"\n size=\"sm\"\n @update:model-value=\"(on) => toggle(row, Boolean(on))\"\n />\n </span>\n </template>\n\n <template #cell-actions=\"{ row }\">\n <span @click.stop>\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.name\" />\n </span>\n </template>\n </wx-table>\n\n <wx-table\n v-else\n :data=\"page\"\n :columns=\"logColumns\"\n row-key=\"id\"\n hover\n flush\n layout=\"fixed\"\n expandable\n :loading=\"logLoading\"\n :cards-below=\"0\"\n :filters-count=\"filtersCount\"\n :filters-label=\"t('panel.filters')\"\n :empty-text=\"t('panel.log-empty')\"\n :aria-label=\"t('panel.log')\"\n @state-change=\"loadLog\"\n >\n <template #filters>\n <wx-form-item :label=\"t('panel.column-status')\">\n <wx-select v-model=\"status\" :options=\"statusOptions\" size=\"sm\" />\n </wx-form-item>\n <wx-form-item :label=\"t('panel.column-channel')\">\n <wx-select v-model=\"channelFilter\" :options=\"channelOptions\" size=\"sm\" />\n </wx-form-item>\n <wx-switch v-model=\"withTests\" size=\"sm\">{{ t('panel.with-tests') }}</wx-switch>\n </template>\n\n <template #cell-created_at=\"{ row }\">\n <wx-date :value=\"row.sent_at ?? row.created_at\" />\n </template>\n\n <template #cell-recipient=\"{ row }\">\n <span class=\"wx-notifications-list__text\">\n <wx-text truncate>{{ row.recipient_label ?? row.recipient }}</wx-text>\n <wx-text size=\"sm\" tone=\"muted\" truncate>\n {{ row.event_label ?? row.event }}\n </wx-text>\n </span>\n </template>\n\n <template #cell-channel_name=\"{ row }\">\n <wx-text size=\"sm\" truncate :tone=\"row.channel_id === null ? 'muted' : 'default'\">\n {{ row.channel_name }}{{ row.channel_id === null ? ` (${t('panel.channel-gone')})` : '' }}\n </wx-text>\n </template>\n\n <template #cell-status=\"{ row }\">\n <span class=\"wx-notifications-list__last\">\n <wx-badge :type=\"tone[row.status]\" dot>{{ t(`module.statuses.${row.status}`) }}</wx-badge>\n <wx-badge v-if=\"row.is_test\">{{ t('panel.test-mark') }}</wx-badge>\n </span>\n </template>\n\n <template #cell-attempts=\"{ row }\">\n <wx-text size=\"sm\">{{ row.attempts }}</wx-text>\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"logActionsFor(row)\" :label=\"row.recipient\" />\n </template>\n\n <template #expanded=\"{ row }\">\n <div class=\"wx-notifications-list__details\">\n <wx-text v-if=\"why(row)\" size=\"sm\" :tone=\"row.status === 'failed' ? 'danger' : 'muted'\">\n {{ why(row) }}\n </wx-text>\n <wx-text v-if=\"row.unknown_variables.length > 0\" size=\"sm\" tone=\"muted\">\n {{ t('panel.unknown-variables', { names: row.unknown_variables.join(', ') }) }}\n </wx-text>\n <wx-text v-if=\"row.truncated\" size=\"sm\" tone=\"muted\">{{ t('panel.cut-mark') }}</wx-text>\n <wx-text size=\"sm\" tone=\"muted\" mono>{{ row.recipient }}</wx-text>\n </div>\n </template>\n </wx-table>\n </wx-list-screen>\n</template>\n\n<style scoped>\n.wx-notifications-list__note {\n padding: var(--wx-space-12) var(--wx-space-16) 0;\n}\n\n.wx-notifications-list__name {\n display: flex;\n align-items: center;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n.wx-notifications-list__icon {\n flex: none;\n color: var(--wx-text-muted);\n}\n\n.wx-notifications-list__text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.wx-notifications-list__last {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--wx-space-6);\n min-width: 0;\n}\n\n.wx-notifications-list__when {\n font-size: var(--wx-font-size-sm);\n color: var(--wx-text-muted);\n}\n\n.wx-notifications-list__details {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-4);\n padding: var(--wx-space-8) var(--wx-space-16);\n}\n</style>\n"],"names":["props","__props","open","resolve","dismiss","useModal","useNotificationsMessages","t","useTranslate","provider","ref","event","providers","computed","one","events","go","_createBlock","_unref","WxDialog","$event","_createVNode","WxSpace","WxButton","WxText","WxFormItem","WxSegmented","WxSelect","_cache","value","admin","useAdmin","api","createNotificationsApi","route","useRoute","router","useRouter","message","useErrorText","canManage","title","module","view","asked","meta","channels","bin","environment","loading","views","loadChannels","live","trashed","error","toast","found","create","createModal","NewChannelDialog","add","chosen","row","toggle","enabled","detail","body","copy","remove","confirm","restore","purge","actionsFor","channelColumns","rowMenuWidth","rows","iconOf","page","logLoading","status","channelFilter","withTests","lastState","loadLog","state","watch","next","filtersCount","statusOptions","channelOptions","logColumns","tone","retry","logActionsFor","list","why","actions","WxListScreen","_openBlock","_createElementBlock","_hoisted_1","WxAlert","WxTable","_withCtx","_createElementVNode","_hoisted_2","WxIcon","_hoisted_3","_createTextVNode","_toDisplayString","WxBadge","_hoisted_4","WxDate","WxSwitch","on","WxRowMenu","_hoisted_5","_hoisted_6","_hoisted_7"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The section (§10.1): the channels, the log of what they sent, and the bin — three views of one
3
+ * list screen, the open one in the address (`?view=log`), so a link to the log is a link to it.
4
+ *
5
+ * A channel opens on a page of its own: its form is long, and the preview beside it needs the
6
+ * width a pane next to a list would take away.
7
+ */
8
+ type __VLS_Props = {
9
+ base?: string;
10
+ };
11
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
12
+ base: string;
13
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
14
+ export default _default;
@@ -0,0 +1,25 @@
1
+ import { LocalizedValue } from '@webx-ui/core';
2
+ type __VLS_Props = {
3
+ localized?: boolean;
4
+ disabled?: boolean;
5
+ minHeight?: string;
6
+ };
7
+ type __VLS_PublicProps = {
8
+ modelValue?: string | LocalizedValue | null;
9
+ } & __VLS_Props;
10
+ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
11
+ "update:modelValue": (value: string | LocalizedValue | null) => any;
12
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
13
+ "onUpdate:modelValue"?: ((value: string | LocalizedValue | null) => any) | undefined;
14
+ }>, {
15
+ disabled: boolean;
16
+ minHeight: string;
17
+ localized: boolean;
18
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
19
+ editor: {
20
+ view: import('@codemirror/view').EditorView | null;
21
+ focus: () => void | undefined;
22
+ format: () => boolean;
23
+ } | null;
24
+ }, HTMLDivElement>;
25
+ export default _default;
@@ -0,0 +1,18 @@
1
+ import { ScreenModel } from '@webx-ui/schema';
2
+ type __VLS_Props = {
3
+ channel?: number | null;
4
+ values: ScreenModel;
5
+ /** The fixed recipients, as the server knows them by: a chat id, an address. */
6
+ recipients?: {
7
+ value: string;
8
+ label: string;
9
+ }[];
10
+ };
11
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
12
+ channel: number | null;
13
+ recipients: {
14
+ value: string;
15
+ label: string;
16
+ }[];
17
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
18
+ export default _default;
package/dist/api.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { AdminContext } from '@webx-ui/module-admin';
2
+ import { ScreenModel } from '@webx-ui/schema';
3
+ import { Administrator, ChannelDetail, ChannelsList, CheckAnswer, DeliveriesPage, DeliveriesQuery, DeliveryRow, FoundChat, NotificationsMeta, ServerPreview, TestResult } from './types';
4
+ export interface NotificationsApi {
5
+ /** The providers and the events the site has. */
6
+ meta(): Promise<NotificationsMeta>;
7
+ /** Every channel, the newest first; `event` narrows it (`none` — the ones for any event). */
8
+ channels(filter?: {
9
+ trashed?: boolean;
10
+ event?: string;
11
+ }): Promise<ChannelsList>;
12
+ get(id: number): Promise<ChannelDetail>;
13
+ /** A new channel out of the values of its form; a new channel of an event starts with its sample. */
14
+ create(values: ScreenModel): Promise<ChannelDetail>;
15
+ /**
16
+ * Only the fields given are written, so the switch in the list sends `{ enabled }` alone. An
17
+ * empty secret keeps the one saved; `forget` lists the ones to wipe.
18
+ */
19
+ save(id: number, values: ScreenModel): Promise<ChannelDetail>;
20
+ remove(id: number): Promise<void>;
21
+ restore(id: number): Promise<ChannelDetail>;
22
+ purge(id: number): Promise<void>;
23
+ /** A copy, switched off, its secrets carried over on the server. */
24
+ copy(id: number, name?: string): Promise<ChannelDetail>;
25
+ /** Sends now, past the queue, on the event's examples — through the form's values, not saved. */
26
+ test(id: number | null, values: ScreenModel, recipient?: string | null): Promise<TestResult[]>;
27
+ /** The server's render of the form's values: the check of the live preview. */
28
+ preview(id: number | null, values: ScreenModel, locale?: string): Promise<ServerPreview>;
29
+ /** The settings tried without sending: Telegram's getMe, an SMTP handshake. */
30
+ check(id: number | null, values: ScreenModel): Promise<CheckAnswer>;
31
+ /** The chats the bot has seen; with a token typed into the form or the saved one. */
32
+ findRecipients(input: {
33
+ provider: string;
34
+ channel?: number | null;
35
+ token?: string;
36
+ }): Promise<{
37
+ recipients: FoundChat[];
38
+ hint: string | null;
39
+ }>;
40
+ administrators(): Promise<Administrator[]>;
41
+ deliveries(query?: DeliveriesQuery): Promise<DeliveriesPage>;
42
+ retry(id: number): Promise<DeliveryRow>;
43
+ }
44
+ /** The path of the section under the panel's API. */
45
+ export declare const NOTIFICATIONS_API = "notifications";
46
+ export declare function createNotificationsApi(admin: AdminContext): NotificationsApi;
@@ -0,0 +1,21 @@
1
+ import { ComputedRef, Ref } from 'vue';
2
+ import { ScreenModel } from '@webx-ui/schema';
3
+ import { NotificationEvent, NotificationProvider, NotificationsMeta } from './types';
4
+ /**
5
+ * What the fields of a channel's form know about the form around them: the kind of channel and
6
+ * the event chosen in it, which decide what the template suggests and which addressees there
7
+ * are, and the record itself, which "Find chats" asks the saved token of.
8
+ *
9
+ * Provided by the channel's page; a field drawn anywhere else gets an empty one and works as a
10
+ * plain field — a template with only Mustache's syntax to suggest, chats without a search.
11
+ */
12
+ export interface ChannelContext {
13
+ meta: Ref<NotificationsMeta | null>;
14
+ values: Ref<ScreenModel>;
15
+ /** The saved channel, or null for one not written yet. */
16
+ id: Ref<number | null>;
17
+ provider: ComputedRef<NotificationProvider | null>;
18
+ event: ComputedRef<NotificationEvent | null>;
19
+ }
20
+ export declare function provideChannel(meta: Ref<NotificationsMeta | null>, values: Ref<ScreenModel>, id: Ref<number | null>): ChannelContext;
21
+ export declare function useChannel(): ChannelContext;
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** Puts this package's English under the panel's dictionary, once per panel. */
2
+ export declare function useNotificationsMessages(): void;