@webx-ui/module-notifications 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 webx-ui
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,42 @@
1
- # Temporary Holding Version
1
+ # @webx-ui/module-notifications
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The front end of the Notifications section of the [WebX UI](https://github.com/webx-ui/webx-ui)
4
+ admin panel: channels to Telegram and e-mail, their Mustache templates in every language of the
5
+ site, a live preview, tests, and the log of every delivery.
6
+
7
+ The other half is the Composer package `webx-ui/module-notifications`, which owns the channels, the
8
+ providers, the queue, the API and the agent tools. A section appears in the panel when both halves
9
+ are installed.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @webx-ui/module-notifications
15
+ ```
16
+
17
+ ```ts
18
+ import { createAdmin } from '@webx-ui/module-admin'
19
+ import { notifications } from '@webx-ui/module-notifications'
20
+ import '@webx-ui/module-notifications/style.css'
21
+
22
+ createAdmin({
23
+ basePath: '/cms',
24
+ modules: [notifications()],
25
+ }).mount()
26
+ ```
27
+
28
+ `php artisan webx:module:add notifications` installs both halves and writes this into the panel's
29
+ entry. `notifications({ path: '/alerts' })` puts the section somewhere else inside the panel.
30
+
31
+ ## What is in it
32
+
33
+ - The section: channels with their switch, copy and bin; the delivery log with filters and
34
+ «Send again».
35
+ - A channel's page: the described screen `notifications.channel` with the provider's settings, the
36
+ template in `WxCodeEditor` with Mustache highlighted and suggested after `{{`, and the preview
37
+ beside it — rendered by `renderMessage()`, held to the server's render by shared test cases.
38
+ - Node types for screens: `wx-notifications-template`, `wx-notifications-chats`,
39
+ `wx-notifications-emails`, and `wx-notifications-channels` — the picker a module chooses its
40
+ channels for an event with, also exported as `WxNotificationChannelsField`.
41
+
42
+ The guide: https://webx-ui.github.io/webx-ui/guide/notifications
@@ -0,0 +1,19 @@
1
+ /**
2
+ * One channel (§10.3): the described screen `notifications.channel` — the common part and the
3
+ * provider's settings the server lays over it — with the live preview beside it on a wide pane and
4
+ * under it on a narrow one, and one explicit save.
5
+ *
6
+ * A secret is never in the form: the server answers which are set, and the field says so with
7
+ * `••••••••` until a new one is typed. "Forget" is in the head's menu, one per secret that is set,
8
+ * and takes effect on the save, like every other change here.
9
+ *
10
+ * The form's values are what "Check", "Send a test" and the preview run on — saved or not: a
11
+ * channel is tried before it is kept, and a test of a switched-off channel is the point of one.
12
+ */
13
+ type __VLS_Props = {
14
+ base?: string;
15
+ };
16
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
17
+ base: string;
18
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
19
+ export default _default;
@@ -0,0 +1,23 @@
1
+ type __VLS_Props = {
2
+ /** The event the channels are for: `inbox.submission`. */
3
+ event: string;
4
+ disabled?: boolean;
5
+ /** Where the section is in the panel, for the link to it. */
6
+ base?: string;
7
+ addText?: string;
8
+ emptyText?: string;
9
+ };
10
+ type __VLS_PublicProps = {
11
+ modelValue?: number[] | null;
12
+ } & __VLS_Props;
13
+ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
14
+ "update:modelValue": (value: number[] | null) => any;
15
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
16
+ "onUpdate:modelValue"?: ((value: number[] | null) => any) | undefined;
17
+ }>, {
18
+ disabled: boolean;
19
+ base: string;
20
+ emptyText: string;
21
+ addText: string;
22
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
23
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import { ChatRecipient } from './types';
2
+ type __VLS_Props = {
3
+ disabled?: boolean;
4
+ };
5
+ type __VLS_PublicProps = {
6
+ modelValue?: ChatRecipient[] | null;
7
+ } & __VLS_Props;
8
+ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
9
+ "update:modelValue": (value: ChatRecipient[] | null) => any;
10
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
+ "onUpdate:modelValue"?: ((value: ChatRecipient[] | null) => any) | undefined;
12
+ }>, {
13
+ disabled: boolean;
14
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
15
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import { EmailRecipient } from './types';
2
+ type __VLS_Props = {
3
+ disabled?: boolean;
4
+ };
5
+ type __VLS_PublicProps = {
6
+ modelValue?: EmailRecipient[] | null;
7
+ } & __VLS_Props;
8
+ declare const _default: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
9
+ "update:modelValue": (value: EmailRecipient[] | null) => any;
10
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
11
+ "onUpdate:modelValue"?: ((value: EmailRecipient[] | null) => any) | undefined;
12
+ }>, {
13
+ disabled: boolean;
14
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
15
+ export default _default;
@@ -0,0 +1,12 @@
1
+ type __VLS_Props = {
2
+ channel?: number | null;
3
+ token?: string;
4
+ /** Chats already on the list: shown ticked and not added twice. */
5
+ chosen?: string[];
6
+ };
7
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
8
+ channel: number | null;
9
+ token: string;
10
+ chosen: string[];
11
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
12
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLElement>;
2
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import { NotificationsMeta } from './types';
2
+ type __VLS_Props = {
3
+ meta: NotificationsMeta;
4
+ };
5
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
6
+ export default _default;
@@ -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;
@@ -0,0 +1,15 @@
1
+ export { notifications, type NotificationsOptions } from './module';
2
+ export { createNotificationsApi, NOTIFICATIONS_API, type NotificationsApi } from './api';
3
+ export { notificationsMessages } from './messages';
4
+ export { escapeFor, escapeHtml, examplesOf, renderMessage, renderTemplate, TemplateRefusal, type MessageInput, type RenderedMessage, type TemplateValues, } from './render';
5
+ export { telegramHtml } from './telegram';
6
+ export { addresseesOf, snippetOf, variablesOf } from './variables';
7
+ export { provideChannel, useChannel, type ChannelContext } from './context';
8
+ export { default as WxNotificationsPage } from './NotificationsPage';
9
+ export { default as WxNotificationsChannelPage } from './ChannelPage';
10
+ export { default as WxNotificationsPreview } from './MessagePreview';
11
+ export { default as WxNotificationsTemplateField } from './TemplateField';
12
+ export { default as WxNotificationsChatsField } from './ChatsField';
13
+ export { default as WxNotificationsEmailsField } from './EmailsField';
14
+ export { default as WxNotificationChannelsField } from './ChannelsField';
15
+ export type * from './types';