import type { ChannelType } from "../Common/Channels/ChannelType"; import type { ITicketAuditedEntity } from "./Common"; import type { TicketNotificationEvent } from "./SelfService"; /** * Ticket-relative recipient roles for CUSTOMER notifications only. * Intentionally excludes any agent/internal-group role — this is the * enforcement point that keeps internal recipients out of customer-facing * notifications. */ export declare enum CustomerNotificationRecipientRole { REQUESTER = "REQUESTER", PARTICIPANT_CONTACTS = "PARTICIPANT_CONTACTS", ACTION_ASSIGNEE = "ACTION_ASSIGNEE", EXTERNAL_APPROVER = "EXTERNAL_APPROVER", /** Members of the organization that have access to the ticket. */ ORGANIZATION_MEMBERS = "ORGANIZATION_MEMBERS", /** Other external recipients defined by the domain/integration. */ OTHER_EXTERNAL_CONTACTS = "OTHER_EXTERNAL_CONTACTS" } /** Per event+channel+locale template selection. `templateId` points at a template SET id; `(channel, locale)` selects the VARIANT inside that set. */ export interface ICustomerNotificationTemplateBinding { channel: ChannelType; locale: string; templateId: string; } /** Granular flags describing what the CUSTOMER may do for this event. Mandatory events MUST keep `canDisable: false`. */ export interface ICustomerNotificationEventCustomerPersonalization { /** Customer may turn this event off entirely. */ canDisable: boolean; /** Customer may pick which channels this event notifies them on. */ canChooseChannels: boolean; /** Customer may mute this event for a single ticket. */ canMutePerTicket: boolean; /** Customer may restore this event to the inherited/general values. */ canRestoreDefaults: boolean; } /** One configured `TicketNotificationEvent` inside a policy. */ export interface ICustomerNotificationEventConfig { event: TicketNotificationEvent; enabled: boolean; /** Mandatory events cannot be disabled or muted. */ mandatory: boolean; customerPersonalization: ICustomerNotificationEventCustomerPersonalization; recipients: CustomerNotificationRecipientRole[]; channels: ChannelType[]; /** * Admin-chosen fallback channel used ONLY when the contact lacks the data * required for the primary channel — per-event, explicit, NOT an automatic * global fallback. `null` = no alternative; the resolver surfaces a WARNING * instead of substituting silently. */ alternativeChannel: ChannelType | null; templates: ICustomerNotificationTemplateBinding[]; } /** * Space-general (`portalId: null`) or per-portal (`portalId: string`) * customer notification policy. Persisted as a single row with `events` as a * JSONB array. */ export interface ITicketCustomerNotificationPolicy extends ITicketAuditedEntity { portalId: string | null; enabled: boolean; /** Policy display name. */ name?: string; /** Policy display description. */ description?: string; /** * Policy-level master switch. When `false`, the resolver ignores the * contact's `ITicketCustomerNotificationPreference` ENTIRELY for this * policy — the general/portal policy's own decisions always apply, * ticket-mute is never honored, and no event resolves with * `origin: 'individual'`/`'muted'`. `true` (the column default) preserves * default behavior. */ respectIndividualPreferences: boolean; defaultLocale: string; fallbackLocale: string; senderName?: string; /** * Opaque reference to a conversations-service email `IChannel.id`. Stored * only; never resolved or validated against the conversations service. * Kept as the EMAIL-only fallback for backward-compat — prefer * `senderIdentityByChannel[EMAIL]` going forward. */ senderIdentityId?: string; /** * Per-channel opaque sender-identity id override — a channel id (same * `IChannel.id` reference as `senderIdentityId`) keyed by `ChannelType`. */ senderIdentityByChannel?: Partial>; replyToAddress?: string; events: ICustomerNotificationEventConfig[]; } /** HTML/TEXT content type. Numeric values mirror Marketing's shape by design. */ export declare enum TemplateContentType { HTML = 1, TEXT = 2 } /** Per-variant lifecycle status. `DISABLED` keeps the variant persisted while making the `DISABLED_TEMPLATE` warning fire for any event still referencing it. */ export declare enum TemplateVariantStatus { ENABLED = "ENABLED", DISABLED = "DISABLED" } /** * One channel+locale variant of a template set's content. `name` is an * INTERNAL, admin-only label — never rendered to the customer. `signature` is * per-variant free text. */ export interface ICustomerNotificationTemplateVariant { channel: ChannelType; locale: string; /** Internal name, never shown to the customer. */ name: string; subject?: string; content: string; signature?: string; /** Variable keys (from the vetted catalog) referenced by this variant's content. */ allowedVariables: string[]; contentType: TemplateContentType; status: TemplateVariantStatus; version: number; } /** One template SET per `TicketNotificationEvent`, holding every channel+locale VARIANT for that event. */ export interface ITicketCustomerNotificationTemplateSet extends ITicketAuditedEntity { event: TicketNotificationEvent; name: string; variants: ICustomerNotificationTemplateVariant[]; } export type ResolvedEventOrigin = "general" | "portal" | "individual" | "mandatory" | "muted"; /** * Channel-agnostic warning codes the resolver can determine structurally. * A strict subset of any richer client-side diagnostic code set. */ export type ResolvedWarningCode = "EVENT_ACTIVE_WITHOUT_CHANNEL" | "EVENT_ACTIVE_WITHOUT_TEMPLATE" | "EVENT_ACTIVE_WITHOUT_RECIPIENTS" | "LANGUAGE_WITHOUT_TEMPLATE_OR_FALLBACK" | "INCOMPATIBLE_RECIPIENTS" | "MANDATORY_EVENT_ALLOWS_MUTE" | "PORTAL_DISABLED" | "DISABLED_TEMPLATE" | "INTERNAL_INFO_EXPOSING_VARIABLES" | "CUSTOMER_INVISIBLE_TEMPLATE_FIELDS"; export interface IResolvedCustomerNotificationWarning { code: ResolvedWarningCode; /** Omitted for policy-level codes (currently only `PORTAL_DISABLED`). */ event?: TicketNotificationEvent; } export interface IResolvedCustomerNotificationEventConfig { event: TicketNotificationEvent; enabled: boolean; mandatory: boolean; customerPersonalization: ICustomerNotificationEventCustomerPersonalization; origin: ResolvedEventOrigin; recipients: CustomerNotificationRecipientRole[]; /** Channels actually used after `alternativeChannel` substitution (if any). */ channels: ChannelType[]; /** Channels requested by the policy/preference, before substitution. */ requestedChannels: ChannelType[]; senderIdentityByChannel: Partial>; /** The admin-configured substitute channel (verbatim from the stored event). `null` = none. */ alternativeChannel: ChannelType | null; alternativeChannelApplied: boolean; warning?: string; templates: ICustomerNotificationTemplateBinding[]; resolvedTemplate?: ICustomerNotificationTemplateBinding; } export interface IResolvedCustomerNotificationConfig { spaceId: string; portalId: string | null; policyOrigin: "portal" | "general"; defaultLocale: string; fallbackLocale: string; senderName?: string; senderIdentityId?: string; senderIdentityByChannel?: Partial>; replyToAddress?: string; /** Whole-ticket mute flag (from the preference's muted ticket ids). */ muted: boolean; events: IResolvedCustomerNotificationEventConfig[]; warnings: IResolvedCustomerNotificationWarning[]; } type AuditedEntityKeys = keyof ITicketAuditedEntity; export type CreateCustomerNotificationPolicyDto = Omit & Partial>; /** * The clearable optionals are nullable: `null` explicitly CLEARS the stored * value on a PATCH while an omitted key leaves it untouched. Do NOT narrow * these back to `string | undefined` — that would make it impossible to erase * an already-saved sender/reply-to/name. */ export type UpdateCustomerNotificationPolicyDto = Partial> & { name?: string | null; description?: string | null; senderName?: string | null; senderIdentityId?: string | null; senderIdentityByChannel?: Partial> | null; replyToAddress?: string | null; }; export type CreateCustomerNotificationTemplateSetDto = Omit; export type UpdateCustomerNotificationTemplateSetDto = Partial; export {};