@aglyn/plugins-email 1.0.0-beta.203 → 1.0.0-beta.205

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-email",
3
- "version": "1.0.0-beta.203",
3
+ "version": "1.0.0-beta.205",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,22 +25,22 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.203",
29
- "@aglyn/shared-data-enums": "1.0.0-beta.203",
30
- "@aglyn/shared-data-mdi": "1.0.0-beta.203",
31
- "@aglyn/shared-data-types": "1.0.0-beta.203",
32
- "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.203",
33
- "@aglyn/shared-ui-jsx": "1.0.0-beta.203",
34
- "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.203",
35
- "@aglyn/shared-ui-next": "1.0.0-beta.203",
36
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.203",
37
- "@aglyn/shared-ui-theme": "1.0.0-beta.203",
38
- "@aglyn/shared-util-email": "1.0.0-beta.203",
39
- "@aglyn/shared-util-http": "1.0.0-beta.203",
40
- "@aglyn/shared-util-timestamp": "1.0.0-beta.203",
41
- "@aglyn/shared-util-tools": "1.0.0-beta.203",
42
- "@aglyn/tenant-data-admin": "1.0.0-beta.203",
43
- "@aglyn/tenant-feature-instance": "1.0.0-beta.203",
28
+ "@aglyn/aglyn": "1.0.0-beta.205",
29
+ "@aglyn/shared-data-enums": "1.0.0-beta.205",
30
+ "@aglyn/shared-data-mdi": "1.0.0-beta.205",
31
+ "@aglyn/shared-data-types": "1.0.0-beta.205",
32
+ "@aglyn/shared-ui-email-campaigns": "1.0.0-beta.205",
33
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.205",
34
+ "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.205",
35
+ "@aglyn/shared-ui-next": "1.0.0-beta.205",
36
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.205",
37
+ "@aglyn/shared-ui-theme": "1.0.0-beta.205",
38
+ "@aglyn/shared-util-email": "1.0.0-beta.205",
39
+ "@aglyn/shared-util-http": "1.0.0-beta.205",
40
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.205",
41
+ "@aglyn/shared-util-tools": "1.0.0-beta.205",
42
+ "@aglyn/tenant-data-admin": "1.0.0-beta.205",
43
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.205",
44
44
  "@swc/helpers": "0.5.23"
45
45
  },
46
46
  "peerDependencies": {
@@ -36,6 +36,12 @@ interface EmailBlockStyleProps {
36
36
  export declare const SECTION_ID: Aglyn.ComponentId;
37
37
  export interface EmailSectionProps extends EmailBlockStyleProps {
38
38
  backgroundColor?: string;
39
+ /**
40
+ * `header` or `footer` when the section is the email's own band of that
41
+ * kind (AGL-3372): the send then leaves the sender's matching band out.
42
+ * Set by the Header and Footer presets; draws nothing itself.
43
+ */
44
+ emailRole?: 'header' | 'footer';
39
45
  /** Inner padding in px (default 24). */
40
46
  padding?: number;
41
47
  align?: 'left' | 'center' | 'right';
@@ -28,12 +28,14 @@ import { generatePresetId } from "../utils/generate-preset-id.js";
28
28
  // ---------------------------------------------------------------- section
29
29
  export const SECTION_ID = 'emailSection';
30
30
  export const EmailSection = /*#__PURE__*/ forwardRef((props, ref)=>{
31
- const { backgroundColor, padding, align, children, sx } = props, rest = _object_without_properties_loose(props, [
31
+ // `emailRole` is read at send time and must not reach the DOM.
32
+ const { backgroundColor, padding, align, children, sx, emailRole: _emailRole } = props, rest = _object_without_properties_loose(props, [
32
33
  "backgroundColor",
33
34
  "padding",
34
35
  "align",
35
36
  "children",
36
- "sx"
37
+ "sx",
38
+ "emailRole"
37
39
  ]);
38
40
  return /*#__PURE__*/ _jsx(Box, _extends({
39
41
  ref: ref
@@ -422,8 +424,10 @@ export const EmailButton = /*#__PURE__*/ forwardRef((props, ref)=>{
422
424
  px: 3,
423
425
  py: 1.25,
424
426
  borderRadius: 1,
425
- backgroundColor: backgroundColor || '#1a73e8',
426
- color: color || '#ffffff',
427
+ // The theme's accent when nothing was picked, as the sent email
428
+ // resolves it (AGL-3370): the canvas and the inbox agree.
429
+ backgroundColor: backgroundColor || 'primary.main',
430
+ color: color || 'primary.contrastText',
427
431
  fontFamily: 'Helvetica, Arial, sans-serif',
428
432
  fontSize: 15,
429
433
  fontWeight: 600
@@ -732,9 +736,12 @@ export const emailHtmlSchema = {
732
736
  $id: null,
733
737
  componentId: SECTION_ID,
734
738
  pluginId: BUNDLE_ID,
739
+ // Marks the band as the email's header, so the sender's own header is
740
+ // not drawn above it (AGL-3372).
735
741
  props: {
736
742
  align: 'center',
737
- padding: 24
743
+ padding: 24,
744
+ emailRole: 'header'
738
745
  },
739
746
  nodes: [
740
747
  {
@@ -788,9 +795,12 @@ export const emailHtmlSchema = {
788
795
  $id: null,
789
796
  componentId: SECTION_ID,
790
797
  pluginId: BUNDLE_ID,
798
+ // Marks the band as the email's footer, so the sender's own footer is
799
+ // not drawn under it (AGL-3372).
791
800
  props: {
792
801
  align: 'center',
793
- padding: 24
802
+ padding: 24,
803
+ emailRole: 'footer'
794
804
  },
795
805
  nodes: [
796
806
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/email-blocks.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiCardTextOutline,\n mdiEmailOutline,\n mdiFormatText,\n mdiImageOutline,\n mdiMinus,\n mdiPackageVariantClosed,\n mdiPageLayoutFooter,\n mdiPageLayoutHeader,\n mdiArrowExpandVertical,\n mdiGestureTapButton,\n mdiCodeTags,\n} from '@aglyn/shared-data-mdi'\nimport { sanitizeAuthorHtml } from '@aglyn/aglyn/app-utils/author-html'\nimport { mergeSxProps } from '@aglyn/shared-ui-theme'\nimport Box from '@mui/material/Box'\nimport type { SxProps, Theme } from '@mui/material/styles'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n/**\n * Email-safe blocks (AGL-346): the besigner canvas renders these web\n * previews; the render pipeline (email-render.ts in app-utils) converts\n * the same nodes to inline-styled table HTML for email clients. Merge\n * tokens like {{contact.firstName}} substitute at send time.\n *\n * Component ids are persisted in screen documents; never rename.\n *\n * Every block pulls `sx` out of its props and composes it AFTER its own\n * defaults with MUI array composition (AGL-587): the renderer's Leaf\n * passes the node-level sx (styles-panel output) through props.sx, and a\n * literal `sx={{...}}` after `{...rest}` used to silently discard it.\n */\n\ninterface EmailBlockStyleProps {\n /** Node-level styles merged by the renderer; wins over block defaults. */\n sx?: SxProps<Theme>\n}\n\n// ---------------------------------------------------------------- section\nexport const SECTION_ID: Aglyn.ComponentId = 'emailSection'\n\nexport interface EmailSectionProps extends EmailBlockStyleProps {\n backgroundColor?: string\n /** Inner padding in px (default 24). */\n padding?: number\n align?: 'left' | 'center' | 'right'\n children?: JSX.Children\n}\n\nexport const EmailSection = forwardRef<HTMLDivElement, EmailSectionProps>(\n (props, ref) => {\n const { backgroundColor, padding, align, children, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n maxWidth: 600,\n mx: 'auto',\n backgroundColor: backgroundColor || '#ffffff',\n p: `${padding ?? 24}px`,\n textAlign: align ?? 'left',\n },\n sx,\n )}\n >\n {children}\n </Box>\n )\n },\n)\nEmailSection.displayName = 'EmailSection'\n\nexport const emailSectionSchema: Aglyn.ComponentSchema<EmailSectionProps> = {\n $id: SECTION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email section',\n description: 'An email-safe band with its own background color and padding.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n attributes: [\n {\n name: 'backgroundColor',\n label: 'Background color',\n description: 'Solid colors only — gradients are unreliable in email.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'padding',\n label: 'Padding (px)',\n description: 'Inner spacing around the section content.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'align',\n label: 'Content alignment',\n description:\n 'How every block inside this section lines up. Each block can still ' +\n 'override it with its own alignment.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ------------------------------------------------------------------- text\nexport const TEXT_ID: Aglyn.ComponentId = 'emailText'\n\nexport interface EmailTextProps extends EmailBlockStyleProps {\n children?: string\n variant?: 'heading' | 'subheading' | 'body' | 'caption'\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nconst TEXT_PRESETS: Record<\n NonNullable<EmailTextProps['variant']>,\n { fontSize: number; fontWeight: number; lineHeight: number }\n> = {\n heading: { fontSize: 28, fontWeight: 700, lineHeight: 1.25 },\n subheading: { fontSize: 20, fontWeight: 600, lineHeight: 1.3 },\n body: { fontSize: 15, fontWeight: 400, lineHeight: 1.55 },\n caption: { fontSize: 12, fontWeight: 400, lineHeight: 1.4 },\n}\n\nexport const EmailText = forwardRef<HTMLDivElement, EmailTextProps>(\n (props, ref) => {\n const { children, variant, color, align, sx, ...rest } = props\n const preset = TEXT_PRESETS[variant ?? 'body']\n return (\n <Typography\n ref={ref}\n component=\"div\"\n {...rest}\n sx={mergeSxProps(\n {\n fontSize: preset.fontSize,\n fontWeight: preset.fontWeight,\n lineHeight: preset.lineHeight,\n color: color || '#1a1a1a',\n textAlign: align ?? 'left',\n fontFamily: 'Helvetica, Arial, sans-serif',\n mb: 1,\n },\n sx,\n )}\n >\n {children ?? 'Email text'}\n </Typography>\n )\n },\n)\nEmailText.displayName = 'EmailText'\n\nexport const emailTextSchema: Aglyn.ComponentSchema<EmailTextProps> = {\n $id: TEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email text',\n description: 'A paragraph of email copy in one of the preset styles.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Text',\n description:\n 'Supports merge tokens: {{contact.firstName}}, {{contact.email}}, ' +\n '{{org.name}}, {{unsubscribeUrl}}.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'variant',\n label: 'Style',\n description:\n 'Size and weight of this text — Heading and Subheading also give it ' +\n 'the spacing above and below that a heading needs.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'heading', label: 'Heading' },\n { value: 'subheading', label: 'Subheading' },\n { value: 'body', label: 'Body' },\n { value: 'caption', label: 'Caption' },\n ],\n },\n {\n name: 'color',\n label: 'Color',\n description:\n 'Text color. Keep it dark on a light background: many clients ' +\n 'ignore dark mode overrides and show your color as-is.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'How this block of text lines up within the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// --------------------------------------------------------------- richtext\nexport const RICHTEXT_ID: Aglyn.ComponentId = 'emailRichtext'\n\nexport interface EmailRichtextProps extends EmailBlockStyleProps {\n /** HTML fragment; sanitized with the custom-html policy (AGL-320). */\n html?: string\n}\n\nexport const EmailRichtext = forwardRef<HTMLDivElement, EmailRichtextProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n if (!html?.trim()) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {'Rich text — add HTML in the attributes panel'}\n </Box>\n )\n }\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n { fontFamily: 'Helvetica, Arial, sans-serif', fontSize: 15 },\n sx,\n )}\n // Sanitized on every render — same policy as the custom HTML block.\n // The `typeof window` guard this replaced existed only because that\n // policy was DOMPurify, which needs a DOM; it is now a pure function\n // (AGL-1901), so the body survives a server render instead of being\n // blanked — which for an email is the render that gets SENT.\n dangerouslySetInnerHTML={{ __html: sanitizeAuthorHtml(html) }}\n />\n )\n },\n)\nEmailRichtext.displayName = 'EmailRichtext'\n\nexport const emailRichtextSchema: Aglyn.ComponentSchema<EmailRichtextProps> = {\n $id: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email rich text',\n description: 'An HTML fragment in an email, sanitised on render.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Formatted content (p, b, i, a, ul/ol, h1-h4). Scripts and forms ' +\n 'are stripped; keep styling simple for email clients. Merge ' +\n 'tokens work here too.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\n// ------------------------------------------------------------------ image\nexport const IMAGE_ID: Aglyn.ComponentId = 'emailImage'\n\nexport interface EmailImageProps extends EmailBlockStyleProps {\n src?: string\n alt?: string\n /** Rendered width in px (max 600). */\n width?: number\n href?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailImage = forwardRef<HTMLDivElement, EmailImageProps>(\n (props, ref) => {\n const { src: storedSrc, alt, width, href, align, sx, ...rest } = props\n // A media-picker target, so it can hold a media reference (AGL-1215).\n // This is the CANVAS preview only — the sent HTML is built by\n // `email-render`, which needs an absolute URL and cannot resolve either\n // a reference or the relative CDN path it replaces.\n const src = Aglyn.resolveMediaSrc(storedSrc)\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1 }, sx)}\n >\n {src ? (\n <Box\n component=\"img\"\n src={src}\n alt={alt ?? ''}\n sx={{ maxWidth: '100%', width: width ? `${width}px` : '100%' }}\n />\n ) : (\n <Box\n sx={{\n height: 120,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n }}\n >\n {'Image — pick from the media library'}\n </Box>\n )}\n </Box>\n )\n },\n)\nEmailImage.displayName = 'EmailImage'\n\nexport const emailImageSchema: Aglyn.ComponentSchema<EmailImageProps> = {\n $id: IMAGE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email image',\n description: 'An email-safe image, capped at 600px wide, optionally linked.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'src',\n label: 'Source URL',\n description: 'Use an absolute URL — email clients cannot resolve relative paths.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'alt',\n label: 'Alt text',\n // AGL-1896: the same \"Browse media\" default the site Image element\n // gets. It matters MORE here — most clients block images by default,\n // so for most recipients the alt text IS the block.\n description:\n 'Shown while images are blocked — most clients block by default. ' +\n 'Filled in from the media library when you pick a file that has ' +\n 'alt text.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'width',\n label: 'Width (px)',\n description:\n 'How wide the image renders. Blank fills the section — which is ' +\n 'usually right, since a phone is narrower than any number you pick.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'href',\n label: 'Link URL',\n description:\n 'Where the image goes when it is clicked. Blank leaves it as a ' +\n 'plain picture.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the image sits when it is narrower than the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ----------------------------------------------------------------- button\nexport const BUTTON_ID: Aglyn.ComponentId = 'emailButton'\n\nexport interface EmailButtonProps extends EmailBlockStyleProps {\n children?: string\n href?: string\n backgroundColor?: string\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailButton = forwardRef<HTMLDivElement, EmailButtonProps>(\n (props, ref) => {\n const { children, href, backgroundColor, color, align, sx, ...rest } =\n props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1.5 }, sx)}\n >\n <Box\n component=\"span\"\n sx={{\n display: 'inline-block',\n px: 3,\n py: 1.25,\n borderRadius: 1,\n backgroundColor: backgroundColor || '#1a73e8',\n color: color || '#ffffff',\n fontFamily: 'Helvetica, Arial, sans-serif',\n fontSize: 15,\n fontWeight: 600,\n }}\n >\n {children ?? 'Call to action'}\n </Box>\n </Box>\n )\n },\n)\nEmailButton.displayName = 'EmailButton'\n\nexport const emailButtonSchema: Aglyn.ComponentSchema<EmailButtonProps> = {\n $id: BUTTON_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email button',\n description: 'A call-to-action button that survives every mail client.',\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Label',\n description:\n 'The words on the button. Say what happens next — \"See the report\" ' +\n 'reads as a destination where \"Click here\" does not.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'href',\n label: 'Link URL',\n description: 'Absolute URL (https://…). Merge tokens allowed.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'backgroundColor',\n label: 'Button color',\n description:\n 'The fill behind the label. Check it against the label color — a ' +\n 'button nobody can read is a link nobody clicks.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'color',\n label: 'Label color',\n description: 'The color of the words on the button.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the button sits across the width of the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ---------------------------------------------------------------- divider\nexport const DIVIDER_ID: Aglyn.ComponentId = 'emailDivider'\n\nexport interface EmailDividerProps extends EmailBlockStyleProps {\n color?: string\n}\n\nexport const EmailDivider = forwardRef<HTMLHRElement, EmailDividerProps>(\n (props, ref) => {\n const { color, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n component=\"hr\"\n {...rest}\n sx={mergeSxProps(\n {\n border: 0,\n // Split shorthand so a palette token path from the theme-\n // reference color picker (AGL-588) resolves through the sx\n // system instead of dying inside a template string.\n borderTopStyle: 'solid',\n borderTopWidth: 1,\n borderTopColor: color || '#e0e0e0',\n my: 2,\n },\n sx,\n )}\n />\n )\n },\n)\nEmailDivider.displayName = 'EmailDivider'\n\nexport const emailDividerSchema: Aglyn.ComponentSchema<EmailDividerProps> = {\n $id: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email divider',\n description: 'A horizontal rule between email sections.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'color',\n label: 'Line color',\n description:\n 'The color of the rule. A light grey separates sections without ' +\n 'drawing the eye to the line itself.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n ],\n}\n\n// ----------------------------------------------------------------- spacer\nexport const SPACER_ID: Aglyn.ComponentId = 'emailSpacer'\n\nexport interface EmailSpacerProps extends EmailBlockStyleProps {\n /** Height in px (default 24). */\n height?: number\n}\n\nexport const EmailSpacer = forwardRef<HTMLDivElement, EmailSpacerProps>(\n (props, ref) => {\n const { height, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ height: height ?? 24 }, sx)}\n />\n )\n },\n)\nEmailSpacer.displayName = 'EmailSpacer'\n\nexport const emailSpacerSchema: Aglyn.ComponentSchema<EmailSpacerProps> = {\n $id: SPACER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email spacer',\n description: 'Fixed vertical whitespace between email blocks.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'height',\n label: 'Height (px)',\n description:\n 'How much empty vertical space this block adds. Email clients drop ' +\n 'CSS margins, so a spacer is how you get reliable breathing room.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n}\n\n// ---------------------------------------------------------------- product\nexport const PRODUCT_ID: Aglyn.ComponentId = 'emailProduct'\n\nexport interface EmailProductProps extends EmailBlockStyleProps {\n /** Product id — resolved server-side at render (rename-safe). */\n productId?: string\n buttonLabel?: string\n}\n\nexport const EmailProduct = forwardRef<HTMLDivElement, EmailProductProps>(\n (props, ref) => {\n const { productId, buttonLabel, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 2,\n my: 1,\n textAlign: 'center',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {productId\n ? `Product card (${productId}) — name, price, image fill in at send`\n : 'Product card — pick a product in the attributes panel'}\n <Box sx={{ mt: 1, fontWeight: 600 }}>\n {buttonLabel ?? 'Shop now'}\n </Box>\n </Box>\n )\n },\n)\nEmailProduct.displayName = 'EmailProduct'\n\nexport const emailProductSchema: Aglyn.ComponentSchema<EmailProductProps> = {\n $id: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email product',\n description: 'A product card resolved at send time, so it never goes stale.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n label: 'Product',\n description:\n 'Stored by id — the current name, price, and image render at send.',\n component: Aglyn.FieldComponentType.PRODUCT_SELECT,\n },\n {\n name: 'buttonLabel',\n label: 'Button label',\n description:\n 'The words on the card’s button. Blank uses the block’s default, ' +\n 'and the link always points at this product’s page.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\n// ------------------------------------------------------------ custom html\nexport const HTML_ID: Aglyn.ComponentId = 'emailHtml'\n\nexport interface EmailHtmlProps extends EmailBlockStyleProps {\n html?: string\n}\n\nexport const EmailHtml = forwardRef<HTMLDivElement, EmailHtmlProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ fontFamily: 'Helvetica, Arial, sans-serif' }, sx)}\n // No `typeof window` guard any more (AGL-1901): the sanitizer needs\n // no DOM, so a server-rendered email keeps its custom HTML.\n dangerouslySetInnerHTML={{\n __html: html ? sanitizeAuthorHtml(html) : '',\n }}\n />\n )\n },\n)\nEmailHtml.displayName = 'EmailHtml'\n\nexport const emailHtmlSchema: Aglyn.ComponentSchema<EmailHtmlProps> = {\n $id: HTML_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email custom HTML',\n description: 'Hand-written HTML for an email, used as-is.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Raw table markup for advanced layouts. Sanitized with the ' +\n 'custom-html policy; the render pipeline passes it through as-is ' +\n 'after sanitization.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\n/**\n * The two bands most emails a site sends begin and end with (AGL-3287).\n *\n * Each drops into any email like the blocks above, and the Components page\n * starts a reusable Header or Footer email block from these same trees, by\n * the ids core names in `REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS` — one tree\n * each, wherever it is started from.\n *\n * The words are placeholders to replace. No merge token names the company in\n * both kinds of email — a campaign supplies `{{org.name}}`, and no token is\n * supplied by every one of a site's own emails — while a token an email does\n * not supply is blanked before sending, which would leave the header with no\n * name at all.\n *\n * The footer carries no unsubscribe link: a campaign send appends its own,\n * and a second one would do the same thing twice.\n */\nexport const EMAIL_HEADER_PRESET: Aglyn.PresetSchema = {\n $id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.header,\n type: Aglyn.NodeType.PRESET,\n displayName: 'Header',\n description:\n 'Your logo above your company name, for the top of an email. Pick the ' +\n 'logo from your media library — until you do, the picture shows nothing ' +\n 'in a sent email.',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.BLOCKS,\n tags: ['logo', 'masthead', 'top', 'brand'],\n icon: { path: mdiPageLayoutHeader.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: { align: 'center', padding: 24 },\n nodes: [\n {\n $id: null,\n componentId: IMAGE_ID,\n pluginId: BUNDLE_ID,\n props: { alt: 'Your company logo', width: 160, align: 'center' },\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children: 'Your company',\n variant: 'subheading',\n align: 'center',\n },\n },\n ],\n },\n}\n\n/**\n * The Footer's small print, a muted gray that still reads at 5:1 on white.\n * A literal on purpose: it is written into the sent mail's own HTML, where no\n * theme token resolves.\n */\nconst FOOTER_TEXT_COLOR = '#6b6b6b'\n\n/** See {@link EMAIL_HEADER_PRESET}. */\nexport const EMAIL_FOOTER_PRESET: Aglyn.PresetSchema = {\n $id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.footer,\n type: Aglyn.NodeType.PRESET,\n displayName: 'Footer',\n description:\n 'Your company name and address, and why the reader gets your emails, for ' +\n 'the bottom of an email. Campaign emails add the unsubscribe link for you.',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.BLOCKS,\n tags: ['address', 'bottom', 'contact', 'legal'],\n icon: { path: mdiPageLayoutFooter.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: { align: 'center', padding: 24 },\n nodes: [\n {\n $id: null,\n componentId: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children: 'Your company · 123 Main St, City',\n variant: 'caption',\n align: 'center',\n color: FOOTER_TEXT_COLOR,\n },\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children:\n \"You're receiving this because you're a customer of Your company.\",\n variant: 'caption',\n align: 'center',\n color: FOOTER_TEXT_COLOR,\n },\n },\n ],\n },\n}\n\nexport const emailPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(SECTION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email section',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(TEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Hello {{contact.firstName}},', variant: 'body' },\n },\n },\n {\n $id: generatePresetId(RICHTEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email rich text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(IMAGE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email image',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: IMAGE_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(BUTTON_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email button',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: BUTTON_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Shop now' },\n },\n },\n {\n $id: generatePresetId(DIVIDER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email divider',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(SPACER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email spacer',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SPACER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(PRODUCT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email product',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(HTML_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email custom HTML',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: HTML_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n EMAIL_HEADER_PRESET,\n EMAIL_FOOTER_PRESET,\n]\n"],"names":["Aglyn","mdiCardTextOutline","mdiEmailOutline","mdiFormatText","mdiImageOutline","mdiMinus","mdiPackageVariantClosed","mdiPageLayoutFooter","mdiPageLayoutHeader","mdiArrowExpandVertical","mdiGestureTapButton","mdiCodeTags","sanitizeAuthorHtml","mergeSxProps","Box","Typography","forwardRef","BUNDLE_ID","generatePresetId","SECTION_ID","EmailSection","props","ref","backgroundColor","padding","align","children","sx","rest","maxWidth","mx","p","textAlign","displayName","emailSectionSchema","$id","pluginId","description","category","ComponentCategory","LAYOUT","icon","path","color","attributes","name","label","component","FieldComponentType","COLOR_PICKER","TEXT_FIELD","type","SELECT","options","value","TEXT_ID","TEXT_PRESETS","heading","fontSize","fontWeight","lineHeight","subheading","body","caption","EmailText","variant","preset","fontFamily","mb","emailTextSchema","DATA_DISPLAY","flags","textEditable","FEATURE_FLAG","ENABLED","TEXTAREA","RICHTEXT_ID","EmailRichtext","html","trim","border","borderColor","dangerouslySetInnerHTML","__html","emailRichtextSchema","selfClosing","IMAGE_ID","EmailImage","src","storedSrc","alt","width","href","resolveMediaSrc","my","height","display","alignItems","justifyContent","emailImageSchema","BUTTON_ID","EmailButton","px","py","borderRadius","emailButtonSchema","INPUT","DIVIDER_ID","EmailDivider","borderTopStyle","borderTopWidth","borderTopColor","emailDividerSchema","SPACER_ID","EmailSpacer","emailSpacerSchema","PRODUCT_ID","EmailProduct","productId","buttonLabel","mt","emailProductSchema","PRODUCT_SELECT","HTML_ID","EmailHtml","emailHtmlSchema","EMAIL_HEADER_PRESET","REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS","header","NodeType","PRESET","BLOCKS","tags","data","componentId","nodes","FOOTER_TEXT_COLOR","EMAIL_FOOTER_PRESET","footer","emailPresets"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/email-blocks.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiCardTextOutline,\n mdiEmailOutline,\n mdiFormatText,\n mdiImageOutline,\n mdiMinus,\n mdiPackageVariantClosed,\n mdiPageLayoutFooter,\n mdiPageLayoutHeader,\n mdiArrowExpandVertical,\n mdiGestureTapButton,\n mdiCodeTags,\n} from '@aglyn/shared-data-mdi'\nimport { sanitizeAuthorHtml } from '@aglyn/aglyn/app-utils/author-html'\nimport { mergeSxProps } from '@aglyn/shared-ui-theme'\nimport Box from '@mui/material/Box'\nimport type { SxProps, Theme } from '@mui/material/styles'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n/**\n * Email-safe blocks (AGL-346): the besigner canvas renders these web\n * previews; the render pipeline (email-render.ts in app-utils) converts\n * the same nodes to inline-styled table HTML for email clients. Merge\n * tokens like {{contact.firstName}} substitute at send time.\n *\n * Component ids are persisted in screen documents; never rename.\n *\n * Every block pulls `sx` out of its props and composes it AFTER its own\n * defaults with MUI array composition (AGL-587): the renderer's Leaf\n * passes the node-level sx (styles-panel output) through props.sx, and a\n * literal `sx={{...}}` after `{...rest}` used to silently discard it.\n */\n\ninterface EmailBlockStyleProps {\n /** Node-level styles merged by the renderer; wins over block defaults. */\n sx?: SxProps<Theme>\n}\n\n// ---------------------------------------------------------------- section\nexport const SECTION_ID: Aglyn.ComponentId = 'emailSection'\n\nexport interface EmailSectionProps extends EmailBlockStyleProps {\n backgroundColor?: string\n /**\n * `header` or `footer` when the section is the email's own band of that\n * kind (AGL-3372): the send then leaves the sender's matching band out.\n * Set by the Header and Footer presets; draws nothing itself.\n */\n emailRole?: 'header' | 'footer'\n /** Inner padding in px (default 24). */\n padding?: number\n align?: 'left' | 'center' | 'right'\n children?: JSX.Children\n}\n\nexport const EmailSection = forwardRef<HTMLDivElement, EmailSectionProps>(\n (props, ref) => {\n // `emailRole` is read at send time and must not reach the DOM.\n const { backgroundColor, padding, align, children, sx, emailRole: _emailRole, ...rest } =\n props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n maxWidth: 600,\n mx: 'auto',\n backgroundColor: backgroundColor || '#ffffff',\n p: `${padding ?? 24}px`,\n textAlign: align ?? 'left',\n },\n sx,\n )}\n >\n {children}\n </Box>\n )\n },\n)\nEmailSection.displayName = 'EmailSection'\n\nexport const emailSectionSchema: Aglyn.ComponentSchema<EmailSectionProps> = {\n $id: SECTION_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email section',\n description: 'An email-safe band with its own background color and padding.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n attributes: [\n {\n name: 'backgroundColor',\n label: 'Background color',\n description: 'Solid colors only — gradients are unreliable in email.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'padding',\n label: 'Padding (px)',\n description: 'Inner spacing around the section content.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'align',\n label: 'Content alignment',\n description:\n 'How every block inside this section lines up. Each block can still ' +\n 'override it with its own alignment.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ------------------------------------------------------------------- text\nexport const TEXT_ID: Aglyn.ComponentId = 'emailText'\n\nexport interface EmailTextProps extends EmailBlockStyleProps {\n children?: string\n variant?: 'heading' | 'subheading' | 'body' | 'caption'\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nconst TEXT_PRESETS: Record<\n NonNullable<EmailTextProps['variant']>,\n { fontSize: number; fontWeight: number; lineHeight: number }\n> = {\n heading: { fontSize: 28, fontWeight: 700, lineHeight: 1.25 },\n subheading: { fontSize: 20, fontWeight: 600, lineHeight: 1.3 },\n body: { fontSize: 15, fontWeight: 400, lineHeight: 1.55 },\n caption: { fontSize: 12, fontWeight: 400, lineHeight: 1.4 },\n}\n\nexport const EmailText = forwardRef<HTMLDivElement, EmailTextProps>(\n (props, ref) => {\n const { children, variant, color, align, sx, ...rest } = props\n const preset = TEXT_PRESETS[variant ?? 'body']\n return (\n <Typography\n ref={ref}\n component=\"div\"\n {...rest}\n sx={mergeSxProps(\n {\n fontSize: preset.fontSize,\n fontWeight: preset.fontWeight,\n lineHeight: preset.lineHeight,\n color: color || '#1a1a1a',\n textAlign: align ?? 'left',\n fontFamily: 'Helvetica, Arial, sans-serif',\n mb: 1,\n },\n sx,\n )}\n >\n {children ?? 'Email text'}\n </Typography>\n )\n },\n)\nEmailText.displayName = 'EmailText'\n\nexport const emailTextSchema: Aglyn.ComponentSchema<EmailTextProps> = {\n $id: TEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email text',\n description: 'A paragraph of email copy in one of the preset styles.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Text',\n description:\n 'Supports merge tokens: {{contact.firstName}}, {{contact.email}}, ' +\n '{{org.name}}, {{unsubscribeUrl}}.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n {\n name: 'variant',\n label: 'Style',\n description:\n 'Size and weight of this text — Heading and Subheading also give it ' +\n 'the spacing above and below that a heading needs.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'heading', label: 'Heading' },\n { value: 'subheading', label: 'Subheading' },\n { value: 'body', label: 'Body' },\n { value: 'caption', label: 'Caption' },\n ],\n },\n {\n name: 'color',\n label: 'Color',\n description:\n 'Text color. Keep it dark on a light background: many clients ' +\n 'ignore dark mode overrides and show your color as-is.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'How this block of text lines up within the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// --------------------------------------------------------------- richtext\nexport const RICHTEXT_ID: Aglyn.ComponentId = 'emailRichtext'\n\nexport interface EmailRichtextProps extends EmailBlockStyleProps {\n /** HTML fragment; sanitized with the custom-html policy (AGL-320). */\n html?: string\n}\n\nexport const EmailRichtext = forwardRef<HTMLDivElement, EmailRichtextProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n if (!html?.trim()) {\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n p: 2,\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {'Rich text — add HTML in the attributes panel'}\n </Box>\n )\n }\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n { fontFamily: 'Helvetica, Arial, sans-serif', fontSize: 15 },\n sx,\n )}\n // Sanitized on every render — same policy as the custom HTML block.\n // The `typeof window` guard this replaced existed only because that\n // policy was DOMPurify, which needs a DOM; it is now a pure function\n // (AGL-1901), so the body survives a server render instead of being\n // blanked — which for an email is the render that gets SENT.\n dangerouslySetInnerHTML={{ __html: sanitizeAuthorHtml(html) }}\n />\n )\n },\n)\nEmailRichtext.displayName = 'EmailRichtext'\n\nexport const emailRichtextSchema: Aglyn.ComponentSchema<EmailRichtextProps> = {\n $id: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email rich text',\n description: 'An HTML fragment in an email, sanitised on render.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Formatted content (p, b, i, a, ul/ol, h1-h4). Scripts and forms ' +\n 'are stripped; keep styling simple for email clients. Merge ' +\n 'tokens work here too.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\n// ------------------------------------------------------------------ image\nexport const IMAGE_ID: Aglyn.ComponentId = 'emailImage'\n\nexport interface EmailImageProps extends EmailBlockStyleProps {\n src?: string\n alt?: string\n /** Rendered width in px (max 600). */\n width?: number\n href?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailImage = forwardRef<HTMLDivElement, EmailImageProps>(\n (props, ref) => {\n const { src: storedSrc, alt, width, href, align, sx, ...rest } = props\n // A media-picker target, so it can hold a media reference (AGL-1215).\n // This is the CANVAS preview only — the sent HTML is built by\n // `email-render`, which needs an absolute URL and cannot resolve either\n // a reference or the relative CDN path it replaces.\n const src = Aglyn.resolveMediaSrc(storedSrc)\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1 }, sx)}\n >\n {src ? (\n <Box\n component=\"img\"\n src={src}\n alt={alt ?? ''}\n sx={{ maxWidth: '100%', width: width ? `${width}px` : '100%' }}\n />\n ) : (\n <Box\n sx={{\n height: 120,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: '1px dashed',\n borderColor: 'divider',\n color: 'text.secondary',\n fontSize: 13,\n }}\n >\n {'Image — pick from the media library'}\n </Box>\n )}\n </Box>\n )\n },\n)\nEmailImage.displayName = 'EmailImage'\n\nexport const emailImageSchema: Aglyn.ComponentSchema<EmailImageProps> = {\n $id: IMAGE_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email image',\n description: 'An email-safe image, capped at 600px wide, optionally linked.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'src',\n label: 'Source URL',\n description: 'Use an absolute URL — email clients cannot resolve relative paths.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'alt',\n label: 'Alt text',\n // AGL-1896: the same \"Browse media\" default the site Image element\n // gets. It matters MORE here — most clients block images by default,\n // so for most recipients the alt text IS the block.\n description:\n 'Shown while images are blocked — most clients block by default. ' +\n 'Filled in from the media library when you pick a file that has ' +\n 'alt text.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'width',\n label: 'Width (px)',\n description:\n 'How wide the image renders. Blank fills the section — which is ' +\n 'usually right, since a phone is narrower than any number you pick.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n {\n name: 'href',\n label: 'Link URL',\n description:\n 'Where the image goes when it is clicked. Blank leaves it as a ' +\n 'plain picture.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the image sits when it is narrower than the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ----------------------------------------------------------------- button\nexport const BUTTON_ID: Aglyn.ComponentId = 'emailButton'\n\nexport interface EmailButtonProps extends EmailBlockStyleProps {\n children?: string\n href?: string\n backgroundColor?: string\n color?: string\n align?: 'left' | 'center' | 'right'\n}\n\nexport const EmailButton = forwardRef<HTMLDivElement, EmailButtonProps>(\n (props, ref) => {\n const { children, href, backgroundColor, color, align, sx, ...rest } =\n props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ textAlign: align ?? 'center', my: 1.5 }, sx)}\n >\n <Box\n component=\"span\"\n sx={{\n display: 'inline-block',\n px: 3,\n py: 1.25,\n borderRadius: 1,\n // The theme's accent when nothing was picked, as the sent email\n // resolves it (AGL-3370): the canvas and the inbox agree.\n backgroundColor: backgroundColor || 'primary.main',\n color: color || 'primary.contrastText',\n fontFamily: 'Helvetica, Arial, sans-serif',\n fontSize: 15,\n fontWeight: 600,\n }}\n >\n {children ?? 'Call to action'}\n </Box>\n </Box>\n )\n },\n)\nEmailButton.displayName = 'EmailButton'\n\nexport const emailButtonSchema: Aglyn.ComponentSchema<EmailButtonProps> = {\n $id: BUTTON_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email button',\n description: 'A call-to-action button that survives every mail client.',\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n flags: { textEditable: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'children',\n label: 'Label',\n description:\n 'The words on the button. Say what happens next — \"See the report\" ' +\n 'reads as a destination where \"Click here\" does not.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'href',\n label: 'Link URL',\n description: 'Absolute URL (https://…). Merge tokens allowed.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n {\n name: 'backgroundColor',\n label: 'Button color',\n description:\n 'The fill behind the label. Check it against the label color — a ' +\n 'button nobody can read is a link nobody clicks.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'color',\n label: 'Label color',\n description: 'The color of the words on the button.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n {\n name: 'align',\n label: 'Alignment',\n description: 'Where the button sits across the width of the section.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n ],\n },\n ],\n}\n\n// ---------------------------------------------------------------- divider\nexport const DIVIDER_ID: Aglyn.ComponentId = 'emailDivider'\n\nexport interface EmailDividerProps extends EmailBlockStyleProps {\n color?: string\n}\n\nexport const EmailDivider = forwardRef<HTMLHRElement, EmailDividerProps>(\n (props, ref) => {\n const { color, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n component=\"hr\"\n {...rest}\n sx={mergeSxProps(\n {\n border: 0,\n // Split shorthand so a palette token path from the theme-\n // reference color picker (AGL-588) resolves through the sx\n // system instead of dying inside a template string.\n borderTopStyle: 'solid',\n borderTopWidth: 1,\n borderTopColor: color || '#e0e0e0',\n my: 2,\n },\n sx,\n )}\n />\n )\n },\n)\nEmailDivider.displayName = 'EmailDivider'\n\nexport const emailDividerSchema: Aglyn.ComponentSchema<EmailDividerProps> = {\n $id: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email divider',\n description: 'A horizontal rule between email sections.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'color',\n label: 'Line color',\n description:\n 'The color of the rule. A light grey separates sections without ' +\n 'drawing the eye to the line itself.',\n component: Aglyn.FieldComponentType.COLOR_PICKER,\n },\n ],\n}\n\n// ----------------------------------------------------------------- spacer\nexport const SPACER_ID: Aglyn.ComponentId = 'emailSpacer'\n\nexport interface EmailSpacerProps extends EmailBlockStyleProps {\n /** Height in px (default 24). */\n height?: number\n}\n\nexport const EmailSpacer = forwardRef<HTMLDivElement, EmailSpacerProps>(\n (props, ref) => {\n const { height, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ height: height ?? 24 }, sx)}\n />\n )\n },\n)\nEmailSpacer.displayName = 'EmailSpacer'\n\nexport const emailSpacerSchema: Aglyn.ComponentSchema<EmailSpacerProps> = {\n $id: SPACER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email spacer',\n description: 'Fixed vertical whitespace between email blocks.',\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'height',\n label: 'Height (px)',\n description:\n 'How much empty vertical space this block adds. Email clients drop ' +\n 'CSS margins, so a spacer is how you get reliable breathing room.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n },\n ],\n}\n\n// ---------------------------------------------------------------- product\nexport const PRODUCT_ID: Aglyn.ComponentId = 'emailProduct'\n\nexport interface EmailProductProps extends EmailBlockStyleProps {\n /** Product id — resolved server-side at render (rename-safe). */\n productId?: string\n buttonLabel?: string\n}\n\nexport const EmailProduct = forwardRef<HTMLDivElement, EmailProductProps>(\n (props, ref) => {\n const { productId, buttonLabel, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps(\n {\n border: '1px solid',\n borderColor: 'divider',\n borderRadius: 1,\n p: 2,\n my: 1,\n textAlign: 'center',\n color: 'text.secondary',\n fontSize: 13,\n },\n sx,\n )}\n >\n {productId\n ? `Product card (${productId}) — name, price, image fill in at send`\n : 'Product card — pick a product in the attributes panel'}\n <Box sx={{ mt: 1, fontWeight: 600 }}>\n {buttonLabel ?? 'Shop now'}\n </Box>\n </Box>\n )\n },\n)\nEmailProduct.displayName = 'EmailProduct'\n\nexport const emailProductSchema: Aglyn.ComponentSchema<EmailProductProps> = {\n $id: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email product',\n description: 'A product card resolved at send time, so it never goes stale.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n label: 'Product',\n description:\n 'Stored by id — the current name, price, and image render at send.',\n component: Aglyn.FieldComponentType.PRODUCT_SELECT,\n },\n {\n name: 'buttonLabel',\n label: 'Button label',\n description:\n 'The words on the card’s button. Blank uses the block’s default, ' +\n 'and the link always points at this product’s page.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n },\n ],\n}\n\n// ------------------------------------------------------------ custom html\nexport const HTML_ID: Aglyn.ComponentId = 'emailHtml'\n\nexport interface EmailHtmlProps extends EmailBlockStyleProps {\n html?: string\n}\n\nexport const EmailHtml = forwardRef<HTMLDivElement, EmailHtmlProps>(\n (props, ref) => {\n const { html, sx, ...rest } = props\n return (\n <Box\n ref={ref}\n {...rest}\n sx={mergeSxProps({ fontFamily: 'Helvetica, Arial, sans-serif' }, sx)}\n // No `typeof window` guard any more (AGL-1901): the sanitizer needs\n // no DOM, so a server-rendered email keeps its custom HTML.\n dangerouslySetInnerHTML={{\n __html: html ? sanitizeAuthorHtml(html) : '',\n }}\n />\n )\n },\n)\nEmailHtml.displayName = 'EmailHtml'\n\nexport const emailHtmlSchema: Aglyn.ComponentSchema<EmailHtmlProps> = {\n $id: HTML_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Email custom HTML',\n description: 'Hand-written HTML for an email, used as-is.',\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'html',\n label: 'HTML',\n description:\n 'Raw table markup for advanced layouts. Sanitized with the ' +\n 'custom-html policy; the render pipeline passes it through as-is ' +\n 'after sanitization.',\n component: Aglyn.FieldComponentType.TEXTAREA,\n },\n ],\n}\n\n/**\n * The two bands most emails a site sends begin and end with (AGL-3287).\n *\n * Each drops into any email like the blocks above, and the Components page\n * starts a reusable Header or Footer email block from these same trees, by\n * the ids core names in `REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS` — one tree\n * each, wherever it is started from.\n *\n * The words are placeholders to replace. No merge token names the company in\n * both kinds of email — a campaign supplies `{{org.name}}`, and no token is\n * supplied by every one of a site's own emails — while a token an email does\n * not supply is blanked before sending, which would leave the header with no\n * name at all.\n *\n * The footer carries no unsubscribe link: a campaign send appends its own,\n * and a second one would do the same thing twice.\n */\nexport const EMAIL_HEADER_PRESET: Aglyn.PresetSchema = {\n $id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.header,\n type: Aglyn.NodeType.PRESET,\n displayName: 'Header',\n description:\n 'Your logo above your company name, for the top of an email. Pick the ' +\n 'logo from your media library — until you do, the picture shows nothing ' +\n 'in a sent email.',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.BLOCKS,\n tags: ['logo', 'masthead', 'top', 'brand'],\n icon: { path: mdiPageLayoutHeader.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n // Marks the band as the email's header, so the sender's own header is\n // not drawn above it (AGL-3372).\n props: { align: 'center', padding: 24, emailRole: 'header' },\n nodes: [\n {\n $id: null,\n componentId: IMAGE_ID,\n pluginId: BUNDLE_ID,\n props: { alt: 'Your company logo', width: 160, align: 'center' },\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children: 'Your company',\n variant: 'subheading',\n align: 'center',\n },\n },\n ],\n },\n}\n\n/**\n * The Footer's small print, a muted gray that still reads at 5:1 on white.\n * A literal on purpose: it is written into the sent mail's own HTML, where no\n * theme token resolves.\n */\nconst FOOTER_TEXT_COLOR = '#6b6b6b'\n\n/** See {@link EMAIL_HEADER_PRESET}. */\nexport const EMAIL_FOOTER_PRESET: Aglyn.PresetSchema = {\n $id: Aglyn.REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS.footer,\n type: Aglyn.NodeType.PRESET,\n displayName: 'Footer',\n description:\n 'Your company name and address, and why the reader gets your emails, for ' +\n 'the bottom of an email. Campaign emails add the unsubscribe link for you.',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.BLOCKS,\n tags: ['address', 'bottom', 'contact', 'legal'],\n icon: { path: mdiPageLayoutFooter.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n // Marks the band as the email's footer, so the sender's own footer is\n // not drawn under it (AGL-3372).\n props: { align: 'center', padding: 24, emailRole: 'footer' },\n nodes: [\n {\n $id: null,\n componentId: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children: 'Your company · 123 Main St, City',\n variant: 'caption',\n align: 'center',\n color: FOOTER_TEXT_COLOR,\n },\n },\n {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: {\n children:\n \"You're receiving this because you're a customer of Your company.\",\n variant: 'caption',\n align: 'center',\n color: FOOTER_TEXT_COLOR,\n },\n },\n ],\n },\n}\n\nexport const emailPresets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(SECTION_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email section',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiEmailOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SECTION_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(TEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiFormatText.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: TEXT_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Hello {{contact.firstName}},', variant: 'body' },\n },\n },\n {\n $id: generatePresetId(RICHTEXT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email rich text',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCardTextOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: RICHTEXT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(IMAGE_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email image',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiImageOutline.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: IMAGE_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(BUTTON_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email button',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.INPUT,\n icon: { path: mdiGestureTapButton.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: BUTTON_ID,\n pluginId: BUNDLE_ID,\n props: { children: 'Shop now' },\n },\n },\n {\n $id: generatePresetId(DIVIDER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email divider',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiMinus.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: DIVIDER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(SPACER_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email spacer',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.LAYOUT,\n icon: { path: mdiArrowExpandVertical.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: SPACER_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(PRODUCT_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email product',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiPackageVariantClosed.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: PRODUCT_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n {\n $id: generatePresetId(HTML_ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Email custom HTML',\n pluginId: BUNDLE_ID,\n category: Aglyn.ComponentCategory.DATA_DISPLAY,\n icon: { path: mdiCodeTags.path, sx: { color: '#f57c00' } },\n data: {\n $id: null,\n componentId: HTML_ID,\n pluginId: BUNDLE_ID,\n props: {},\n },\n },\n EMAIL_HEADER_PRESET,\n EMAIL_FOOTER_PRESET,\n]\n"],"names":["Aglyn","mdiCardTextOutline","mdiEmailOutline","mdiFormatText","mdiImageOutline","mdiMinus","mdiPackageVariantClosed","mdiPageLayoutFooter","mdiPageLayoutHeader","mdiArrowExpandVertical","mdiGestureTapButton","mdiCodeTags","sanitizeAuthorHtml","mergeSxProps","Box","Typography","forwardRef","BUNDLE_ID","generatePresetId","SECTION_ID","EmailSection","props","ref","backgroundColor","padding","align","children","sx","emailRole","_emailRole","rest","maxWidth","mx","p","textAlign","displayName","emailSectionSchema","$id","pluginId","description","category","ComponentCategory","LAYOUT","icon","path","color","attributes","name","label","component","FieldComponentType","COLOR_PICKER","TEXT_FIELD","type","SELECT","options","value","TEXT_ID","TEXT_PRESETS","heading","fontSize","fontWeight","lineHeight","subheading","body","caption","EmailText","variant","preset","fontFamily","mb","emailTextSchema","DATA_DISPLAY","flags","textEditable","FEATURE_FLAG","ENABLED","TEXTAREA","RICHTEXT_ID","EmailRichtext","html","trim","border","borderColor","dangerouslySetInnerHTML","__html","emailRichtextSchema","selfClosing","IMAGE_ID","EmailImage","src","storedSrc","alt","width","href","resolveMediaSrc","my","height","display","alignItems","justifyContent","emailImageSchema","BUTTON_ID","EmailButton","px","py","borderRadius","emailButtonSchema","INPUT","DIVIDER_ID","EmailDivider","borderTopStyle","borderTopWidth","borderTopColor","emailDividerSchema","SPACER_ID","EmailSpacer","emailSpacerSchema","PRODUCT_ID","EmailProduct","productId","buttonLabel","mt","emailProductSchema","PRODUCT_SELECT","HTML_ID","EmailHtml","emailHtmlSchema","EMAIL_HEADER_PRESET","REUSABLE_EMAIL_BLOCK_STARTER_PRESET_IDS","header","NodeType","PRESET","BLOCKS","tags","data","componentId","nodes","FOOTER_TEXT_COLOR","EMAIL_FOOTER_PRESET","footer","emailPresets"],"mappings":"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"}
@@ -21,6 +21,7 @@ import { mdiDeleteOutline, mdiEmailCheckOutline } from "@aglyn/shared-data-mdi";
21
21
  import { AppLink, CardDisplay, MdiIcon, useConfirmationContext } from "@aglyn/shared-ui-jsx";
22
22
  import RowActionsMenu from "@aglyn/shared-ui-jsx/components/row-actions-menu.component";
23
23
  import { useSnackbar } from "@aglyn/shared-ui-snackstack";
24
+ import SenderReadinessPanel from "@aglyn/tenant-feature-instance/components/sender-readiness-panel";
24
25
  import { useSendingApi } from "@aglyn/tenant-feature-instance/hooks/use-sending-identity-api";
25
26
  import { Alert, Button, Chip, Divider, Stack, Typography } from "@mui/material";
26
27
  import { useRouter } from "next/navigation";
@@ -431,6 +432,9 @@ function OrgSendingDomainPage(props) {
431
432
  severity: record.dmarc.policy === 'reject' && record.status !== 'verified' ? 'error' : 'info',
432
433
  children: record.dmarc.consequence
433
434
  }) : null,
435
+ record.readiness ? /*#__PURE__*/ _jsx(SenderReadinessPanel, {
436
+ readiness: record.readiness
437
+ }) : null,
434
438
  ((_record_records = record.records) == null ? void 0 : _record_records.some((entry)=>entry.value)) ? /*#__PURE__*/ _jsxs(_Fragment, {
435
439
  children: [
436
440
  /*#__PURE__*/ _jsx(Typography, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-sending-domain-detail.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { PageHeaderRecord, pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiDeleteOutline, mdiEmailCheckOutline } from '@aglyn/shared-data-mdi'\nimport {\n AppLink,\n CardDisplay,\n MdiIcon,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useSendingApi,\n type SendingDomainView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\nimport { Alert, Button, Chip, Divider, Stack, Typography } from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n describeOrgSendingDomainRemoval,\n describeSendingDomain,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport {\n OrgSiteSelect,\n orgSiteName,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport { RecordRow } from './sending-domain-detail'\nimport { useOrgSendingViews } from './use-org-sending-views'\n\nexport interface OrgSendingDomainDetailProps {\n domain: string\n /** The organization's Emails page, for the way back to Sending. */\n basePath: string\n}\n\n/**\n * ONE SENDING DOMAIN, from the organization's page —\n * `/[orgSlug]/emails/sending/{domain}`.\n *\n * The domain is the organization's, so its page reads the same as it does\n * from a site's: what to publish, whether a lookup can see it, and the checks\n * that move it on. What changes is the half that is a site's choice. Under a\n * site this page asks whether THIS site sends as the domain; over the org it\n * lists the sites that do, and offers to move another site onto it.\n *\n * The records come from the domains route, which is owner-or-admin. An editor\n * still sees the domain's state and the sites sending as it — both come back\n * on each site's own identity read, which an editor may make — and is told\n * why the records are not shown rather than handed an error.\n *\n * `inconclusive` is held apart from the record exactly as on the site's page:\n * a lookup nobody answered changed nothing.\n */\nexport function OrgSendingDomainDetail(props: OrgSendingDomainDetailProps) {\n const mount = useEmailOrgMount()\n return mount ? <OrgSendingDomainPage mount={mount} {...props} /> : null\n}\nOrgSendingDomainDetail.displayName = 'OrgSendingDomainDetail'\n\nfunction OrgSendingDomainPage(\n props: OrgSendingDomainDetailProps & { mount: EmailOrgMount },\n) {\n const { mount, domain, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { confirm } = useConfirmationContext()\n const { enqueueSnackbar } = useSnackbar()\n // Held in a ref: `load` depends on it and the hook hands a fresh function\n // back per render, which would re-run the fetch on every render.\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n\n // The first page of sites by name: enough to say which of them send as\n // this domain, and bounded however many sites the organization runs.\n const { sites } = useOrgSitePage(mount)\n const {\n views,\n reload,\n first,\n loading: viewsLoading,\n } = useOrgSendingViews(sites)\n const canManage = first?.canManage === true\n\n const [record, setRecord] = useState<SendingDomainView | null>(null)\n const [refusal, setRefusal] = useState('')\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n const [unreachable, setUnreachable] = useState(false)\n const [assignHostId, setAssignHostId] = useState('')\n\n const load = useCallback(async () => {\n setLoading(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'GET',\n query: { orgId: mount.orgId },\n })\n setLoading(false)\n if (!response.ok) {\n setRecord(null)\n setRefusal(payload?.error ?? 'This domain’s records could not be read.')\n return\n }\n setRefusal('')\n setRecord(\n (payload?.domains ?? []).find(\n (one: SendingDomainView) => one.domain === domain,\n ) ?? null,\n )\n }, [call, mount.orgId, domain])\n\n useEffect(() => {\n void load()\n }, [load])\n\n /** The domain as the list knows it, for a reader the records route refused. */\n const listed = first?.domains.find((one) => one.domain === domain) ?? null\n const status = record?.status ?? listed?.status ?? null\n const using = mount.hosts.filter(\n (site) => views[site.id]?.selected === domain,\n )\n /** Sites whose identity this page has not read, so cannot speak for. */\n const unchecked = mount.hosts.filter((site) => !views[site.id]).length\n const offered = mount.hosts.filter(\n (site) => views[site.id]?.selected !== domain,\n )\n\n const handleVerify = useCallback(async () => {\n if (busy) return\n setBusy(true)\n setUnreachable(false)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'verify' },\n })\n setBusy(false)\n if (response.status === 503) {\n setUnreachable(true)\n return\n }\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'The check could not run',\n {\n variant: 'warning',\n },\n )\n }\n await Promise.all([load(), reload()])\n notifyRef.current(\n payload?.verified\n ? 'Verified — this domain can send'\n : 'Checked. Some records are still missing.',\n { variant: payload?.verified ? 'success' : 'info' },\n )\n }, [busy, call, domain, load, mount.orgId, reload])\n\n const handleRequestRecords = useCallback(async () => {\n if (busy) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'request' },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not request records',\n { variant: 'warning' },\n )\n }\n await load()\n }, [busy, call, domain, load, mount.orgId])\n\n /** Moves one site onto this domain, or off it with `'platform'`. */\n const handleUse = useCallback(\n async (hostId: string, next: string) => {\n if (busy || !hostId) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain: next },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not change the identity',\n { variant: 'warning' },\n )\n }\n setAssignHostId('')\n await reload(hostId)\n // The address the ROUTE settled on — see the site's page.\n const site = orgSiteName(mount, hostId)\n notifyRef.current(\n payload?.from\n ? `${site} now sends as ${payload.from}`\n : `${site}’s sending address was changed`,\n { variant: 'success' },\n )\n },\n [busy, call, mount, reload],\n )\n\n const handleRelease = useCallback(async () => {\n const ok = await confirm({\n ...describeOrgSendingDomainRemoval({\n domain,\n sites: using.map((site) => ({\n name: orgSiteName(mount, site.id),\n issued: views[site.id]?.platformDomain === domain,\n })),\n unchecked,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: mount.orgId, domain },\n })\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not remove the domain',\n { variant: 'warning' },\n )\n }\n router.push(`${basePath}/sending`)\n }, [basePath, call, confirm, domain, mount, router, unchecked, using, views])\n\n const state = status\n ? describeSendingDomain({\n status,\n pendingProvider: record?.pendingProvider ?? status === 'requested',\n issueError: record?.lastIssueError ?? listed?.lastIssueError ?? null,\n missing: record?.lastMissing ?? null,\n })\n : null\n const missing = new Set(record?.lastMissing ?? [])\n const actions: RowActionsMenuItem[] =\n canManage && record\n ? [\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleRelease(),\n },\n ]\n : []\n\n const card = (\n <CardDisplay\n header={'Sending domain'}\n help={pluginDocsHelp('emailCampaigns', { anchor: '#sending-domains' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n component={AppLink as any}\n {...({ componentVariant: 'naked', nativeButton: false } as any)}\n href={`${basePath}/sending`}\n size=\"small\"\n color=\"primary\"\n >\n {'All of Sending'}\n </Button>\n {actions.length ? <RowActionsMenu items={actions} /> : null}\n </Stack>\n ),\n }}\n >\n {(loading || viewsLoading) && !state ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : !state ? (\n <Alert severity=\"warning\">\n {refusal ||\n `${domain} is not claimed by this organization. It may have been ` +\n 'removed — go back and add it again if you still want it.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Chip\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n </Stack>\n\n <Alert severity={state.severity}>{state.text}</Alert>\n\n {unreachable ? (\n <Alert severity={INCONCLUSIVE_CHECK.severity} icon={false}>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {INCONCLUSIVE_CHECK.label}\n </Typography>\n <Typography variant=\"body2\">{INCONCLUSIVE_CHECK.text}</Typography>\n </Alert>\n ) : null}\n\n {/*\n THE SITES SENDING AS IT, of those this page read. Each is the\n site's own choice, and moving one off it is the org twin of the\n \"stop sending\" control on a site's page.\n */}\n <Divider />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Sites sending as this domain'}\n </Typography>\n {using.length ? (\n <Stack spacing={1}>\n {using.map((site) => (\n <Stack\n key={site.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\">\n {orgSiteName(mount, site.id)}\n </Typography>\n {canManage ? (\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() => void handleUse(site.id, 'platform')}\n >\n {'Stop sending as this domain'}\n </Button>\n ) : null}\n </Stack>\n ))}\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {unchecked\n ? 'None of the sites checked sends as it.'\n : 'No site sends as it.'}\n </Typography>\n )}\n {unchecked && !viewsLoading ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${unchecked} more ${unchecked === 1 ? 'site was' : 'sites were'} ` +\n 'not checked; the Sending page reads them a page at a time.'}\n </Typography>\n ) : null}\n {canManage && status === 'verified' && offered.length ? (\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <OrgSiteSelect\n mount={mount}\n label=\"Send a site’s email as this domain\"\n value={assignHostId}\n onChange={setAssignHostId}\n sites={offered}\n />\n <Button\n variant=\"outlined\"\n startIcon={\n <MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />\n }\n disabled={busy || !assignHostId}\n onClick={() => void handleUse(assignHostId, domain)}\n >\n {'Send as this domain'}\n </Button>\n </Stack>\n ) : null}\n\n {/*\n THE RECORDS, which only the domains route returns. A reader it\n refused is told why, in place of the records, rather than shown an\n empty section that reads as \"nothing to publish\".\n */}\n <Divider />\n {refusal ? (\n <Alert severity=\"info\">\n {canManage\n ? refusal\n : 'The records to publish, and the checks that verify them, ' +\n 'are shown to the organization’s owners and admins.'}\n </Alert>\n ) : record ? (\n <>\n {record.dmarc ? (\n <Alert\n severity={\n record.dmarc.policy === 'reject' &&\n record.status !== 'verified'\n ? 'error'\n : 'info'\n }\n >\n {record.dmarc.consequence}\n </Alert>\n ) : null}\n {record.records?.some((entry) => entry.value) ? (\n <>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Publish these records'}\n </Typography>\n <Stack spacing={2}>\n {record.records\n .filter((entry) => entry.value)\n .map((entry) => (\n <RecordRow\n key={`${entry.type}:${entry.name}`}\n record={entry}\n missing={missing.has(`${entry.type}:${entry.name}`)}\n />\n ))}\n {record.dmarcSuggestion &&\n record.dmarc?.policy === 'absent' ? (\n <RecordRow\n record={record.dmarcSuggestion}\n missing={false}\n />\n ) : null}\n </Stack>\n </>\n ) : null}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {record.status === 'requested' && canManage ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleRequestRecords()}\n >\n {busy ? 'Working…' : 'Request records'}\n </Button>\n ) : null}\n {record.status !== 'requested' ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleVerify()}\n >\n {busy ? 'Checking…' : 'Check DNS'}\n </Button>\n ) : null}\n </Stack>\n </>\n ) : null}\n </Stack>\n )}\n </CardDisplay>\n )\n\n return (\n <>\n <PageHeaderRecord title={domain} />\n {card}\n </>\n )\n}\nOrgSendingDomainPage.displayName = 'OrgSendingDomainPage'\n\nexport default OrgSendingDomainDetail\n"],"names":["PageHeaderRecord","pluginDocsHelp","mdiDeleteOutline","mdiEmailCheckOutline","AppLink","CardDisplay","MdiIcon","useConfirmationContext","RowActionsMenu","useSnackbar","useSendingApi","Alert","Button","Chip","Divider","Stack","Typography","useRouter","useCallback","useEffect","useRef","useState","describeOrgSendingDomainRemoval","describeSendingDomain","INCONCLUSIVE_CHECK","OrgSiteSelect","orgSiteName","useEmailOrgMount","useOrgSitePage","RecordRow","useOrgSendingViews","OrgSendingDomainDetail","props","mount","OrgSendingDomainPage","displayName","record","domain","basePath","call","router","confirm","enqueueSnackbar","notifyRef","current","sites","views","reload","first","loading","viewsLoading","canManage","setRecord","refusal","setRefusal","setLoading","busy","setBusy","unreachable","setUnreachable","assignHostId","setAssignHostId","load","response","payload","path","method","query","orgId","ok","error","domains","find","one","listed","status","using","hosts","filter","site","id","selected","unchecked","length","offered","handleVerify","body","action","variant","Promise","all","verified","handleRequestRecords","handleUse","hostId","next","from","handleRelease","map","name","issued","platformDomain","confirmationButtonProps","color","then","catch","push","state","pendingProvider","issueError","lastIssueError","missing","lastMissing","Set","actions","key","label","icon","size","destructive","onClick","card","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","direction","spacing","sx","alignItems","component","componentVariant","nativeButton","href","items","severity","useFlexGap","flexWrap","sending","text","fontWeight","disabled","value","onChange","startIcon","dmarc","policy","consequence","records","some","entry","has","type","dmarcSuggestion","gap","title"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/org-sending-domain-detail.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { PageHeaderRecord, pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiDeleteOutline, mdiEmailCheckOutline } from '@aglyn/shared-data-mdi'\nimport {\n AppLink,\n CardDisplay,\n MdiIcon,\n useConfirmationContext,\n} from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport SenderReadinessPanel from '@aglyn/tenant-feature-instance/components/sender-readiness-panel'\nimport {\n useSendingApi,\n type SendingDomainView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\nimport { Alert, Button, Chip, Divider, Stack, Typography } from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n describeOrgSendingDomainRemoval,\n describeSendingDomain,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport {\n OrgSiteSelect,\n orgSiteName,\n useEmailOrgMount,\n useOrgSitePage,\n type EmailOrgMount,\n} from './email-org-mount'\nimport { RecordRow } from './sending-domain-detail'\nimport { useOrgSendingViews } from './use-org-sending-views'\n\nexport interface OrgSendingDomainDetailProps {\n domain: string\n /** The organization's Emails page, for the way back to Sending. */\n basePath: string\n}\n\n/**\n * ONE SENDING DOMAIN, from the organization's page —\n * `/[orgSlug]/emails/sending/{domain}`.\n *\n * The domain is the organization's, so its page reads the same as it does\n * from a site's: what to publish, whether a lookup can see it, and the checks\n * that move it on. What changes is the half that is a site's choice. Under a\n * site this page asks whether THIS site sends as the domain; over the org it\n * lists the sites that do, and offers to move another site onto it.\n *\n * The records come from the domains route, which is owner-or-admin. An editor\n * still sees the domain's state and the sites sending as it — both come back\n * on each site's own identity read, which an editor may make — and is told\n * why the records are not shown rather than handed an error.\n *\n * `inconclusive` is held apart from the record exactly as on the site's page:\n * a lookup nobody answered changed nothing.\n */\nexport function OrgSendingDomainDetail(props: OrgSendingDomainDetailProps) {\n const mount = useEmailOrgMount()\n return mount ? <OrgSendingDomainPage mount={mount} {...props} /> : null\n}\nOrgSendingDomainDetail.displayName = 'OrgSendingDomainDetail'\n\nfunction OrgSendingDomainPage(\n props: OrgSendingDomainDetailProps & { mount: EmailOrgMount },\n) {\n const { mount, domain, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { confirm } = useConfirmationContext()\n const { enqueueSnackbar } = useSnackbar()\n // Held in a ref: `load` depends on it and the hook hands a fresh function\n // back per render, which would re-run the fetch on every render.\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n\n // The first page of sites by name: enough to say which of them send as\n // this domain, and bounded however many sites the organization runs.\n const { sites } = useOrgSitePage(mount)\n const {\n views,\n reload,\n first,\n loading: viewsLoading,\n } = useOrgSendingViews(sites)\n const canManage = first?.canManage === true\n\n const [record, setRecord] = useState<SendingDomainView | null>(null)\n const [refusal, setRefusal] = useState('')\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n const [unreachable, setUnreachable] = useState(false)\n const [assignHostId, setAssignHostId] = useState('')\n\n const load = useCallback(async () => {\n setLoading(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'GET',\n query: { orgId: mount.orgId },\n })\n setLoading(false)\n if (!response.ok) {\n setRecord(null)\n setRefusal(payload?.error ?? 'This domain’s records could not be read.')\n return\n }\n setRefusal('')\n setRecord(\n (payload?.domains ?? []).find(\n (one: SendingDomainView) => one.domain === domain,\n ) ?? null,\n )\n }, [call, mount.orgId, domain])\n\n useEffect(() => {\n void load()\n }, [load])\n\n /** The domain as the list knows it, for a reader the records route refused. */\n const listed = first?.domains.find((one) => one.domain === domain) ?? null\n const status = record?.status ?? listed?.status ?? null\n const using = mount.hosts.filter(\n (site) => views[site.id]?.selected === domain,\n )\n /** Sites whose identity this page has not read, so cannot speak for. */\n const unchecked = mount.hosts.filter((site) => !views[site.id]).length\n const offered = mount.hosts.filter(\n (site) => views[site.id]?.selected !== domain,\n )\n\n const handleVerify = useCallback(async () => {\n if (busy) return\n setBusy(true)\n setUnreachable(false)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'verify' },\n })\n setBusy(false)\n if (response.status === 503) {\n setUnreachable(true)\n return\n }\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'The check could not run',\n {\n variant: 'warning',\n },\n )\n }\n await Promise.all([load(), reload()])\n notifyRef.current(\n payload?.verified\n ? 'Verified — this domain can send'\n : 'Checked. Some records are still missing.',\n { variant: payload?.verified ? 'success' : 'info' },\n )\n }, [busy, call, domain, load, mount.orgId, reload])\n\n const handleRequestRecords = useCallback(async () => {\n if (busy) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: mount.orgId, domain, action: 'request' },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not request records',\n { variant: 'warning' },\n )\n }\n await load()\n }, [busy, call, domain, load, mount.orgId])\n\n /** Moves one site onto this domain, or off it with `'platform'`. */\n const handleUse = useCallback(\n async (hostId: string, next: string) => {\n if (busy || !hostId) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain: next },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not change the identity',\n { variant: 'warning' },\n )\n }\n setAssignHostId('')\n await reload(hostId)\n // The address the ROUTE settled on — see the site's page.\n const site = orgSiteName(mount, hostId)\n notifyRef.current(\n payload?.from\n ? `${site} now sends as ${payload.from}`\n : `${site}’s sending address was changed`,\n { variant: 'success' },\n )\n },\n [busy, call, mount, reload],\n )\n\n const handleRelease = useCallback(async () => {\n const ok = await confirm({\n ...describeOrgSendingDomainRemoval({\n domain,\n sites: using.map((site) => ({\n name: orgSiteName(mount, site.id),\n issued: views[site.id]?.platformDomain === domain,\n })),\n unchecked,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: mount.orgId, domain },\n })\n if (!response.ok) {\n return void notifyRef.current(\n payload?.error ?? 'Could not remove the domain',\n { variant: 'warning' },\n )\n }\n router.push(`${basePath}/sending`)\n }, [basePath, call, confirm, domain, mount, router, unchecked, using, views])\n\n const state = status\n ? describeSendingDomain({\n status,\n pendingProvider: record?.pendingProvider ?? status === 'requested',\n issueError: record?.lastIssueError ?? listed?.lastIssueError ?? null,\n missing: record?.lastMissing ?? null,\n })\n : null\n const missing = new Set(record?.lastMissing ?? [])\n const actions: RowActionsMenuItem[] =\n canManage && record\n ? [\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleRelease(),\n },\n ]\n : []\n\n const card = (\n <CardDisplay\n header={'Sending domain'}\n help={pluginDocsHelp('emailCampaigns', { anchor: '#sending-domains' })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Button\n component={AppLink as any}\n {...({ componentVariant: 'naked', nativeButton: false } as any)}\n href={`${basePath}/sending`}\n size=\"small\"\n color=\"primary\"\n >\n {'All of Sending'}\n </Button>\n {actions.length ? <RowActionsMenu items={actions} /> : null}\n </Stack>\n ),\n }}\n >\n {(loading || viewsLoading) && !state ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : !state ? (\n <Alert severity=\"warning\">\n {refusal ||\n `${domain} is not claimed by this organization. It may have been ` +\n 'removed — go back and add it again if you still want it.'}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <Chip\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n </Stack>\n\n <Alert severity={state.severity}>{state.text}</Alert>\n\n {unreachable ? (\n <Alert severity={INCONCLUSIVE_CHECK.severity} icon={false}>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {INCONCLUSIVE_CHECK.label}\n </Typography>\n <Typography variant=\"body2\">{INCONCLUSIVE_CHECK.text}</Typography>\n </Alert>\n ) : null}\n\n {/*\n THE SITES SENDING AS IT, of those this page read. Each is the\n site's own choice, and moving one off it is the org twin of the\n \"stop sending\" control on a site's page.\n */}\n <Divider />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Sites sending as this domain'}\n </Typography>\n {using.length ? (\n <Stack spacing={1}>\n {using.map((site) => (\n <Stack\n key={site.id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\">\n {orgSiteName(mount, site.id)}\n </Typography>\n {canManage ? (\n <Button\n size=\"small\"\n disabled={busy}\n onClick={() => void handleUse(site.id, 'platform')}\n >\n {'Stop sending as this domain'}\n </Button>\n ) : null}\n </Stack>\n ))}\n </Stack>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {unchecked\n ? 'None of the sites checked sends as it.'\n : 'No site sends as it.'}\n </Typography>\n )}\n {unchecked && !viewsLoading ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${unchecked} more ${unchecked === 1 ? 'site was' : 'sites were'} ` +\n 'not checked; the Sending page reads them a page at a time.'}\n </Typography>\n ) : null}\n {canManage && status === 'verified' && offered.length ? (\n <Stack\n direction=\"row\"\n spacing={1}\n useFlexGap\n sx={{ alignItems: 'center', flexWrap: 'wrap' }}\n >\n <OrgSiteSelect\n mount={mount}\n label=\"Send a site’s email as this domain\"\n value={assignHostId}\n onChange={setAssignHostId}\n sites={offered}\n />\n <Button\n variant=\"outlined\"\n startIcon={\n <MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />\n }\n disabled={busy || !assignHostId}\n onClick={() => void handleUse(assignHostId, domain)}\n >\n {'Send as this domain'}\n </Button>\n </Stack>\n ) : null}\n\n {/*\n THE RECORDS, which only the domains route returns. A reader it\n refused is told why, in place of the records, rather than shown an\n empty section that reads as \"nothing to publish\".\n */}\n <Divider />\n {refusal ? (\n <Alert severity=\"info\">\n {canManage\n ? refusal\n : 'The records to publish, and the checks that verify them, ' +\n 'are shown to the organization’s owners and admins.'}\n </Alert>\n ) : record ? (\n <>\n {record.dmarc ? (\n <Alert\n severity={\n record.dmarc.policy === 'reject' &&\n record.status !== 'verified'\n ? 'error'\n : 'info'\n }\n >\n {record.dmarc.consequence}\n </Alert>\n ) : null}\n {/*\n SPF, DKIM and DMARC as a receiver reads them, and whether they\n align with the From domain (AGL-3328). The records table below is\n what to publish; this is what the published zone adds up to.\n */}\n {record.readiness ? (\n <SenderReadinessPanel readiness={record.readiness} />\n ) : null}\n {record.records?.some((entry) => entry.value) ? (\n <>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Publish these records'}\n </Typography>\n <Stack spacing={2}>\n {record.records\n .filter((entry) => entry.value)\n .map((entry) => (\n <RecordRow\n key={`${entry.type}:${entry.name}`}\n record={entry}\n missing={missing.has(`${entry.type}:${entry.name}`)}\n />\n ))}\n {record.dmarcSuggestion &&\n record.dmarc?.policy === 'absent' ? (\n <RecordRow\n record={record.dmarcSuggestion}\n missing={false}\n />\n ) : null}\n </Stack>\n </>\n ) : null}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ flexWrap: 'wrap', gap: 1 }}\n >\n {record.status === 'requested' && canManage ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleRequestRecords()}\n >\n {busy ? 'Working…' : 'Request records'}\n </Button>\n ) : null}\n {record.status !== 'requested' ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleVerify()}\n >\n {busy ? 'Checking…' : 'Check DNS'}\n </Button>\n ) : null}\n </Stack>\n </>\n ) : null}\n </Stack>\n )}\n </CardDisplay>\n )\n\n return (\n <>\n <PageHeaderRecord title={domain} />\n {card}\n </>\n )\n}\nOrgSendingDomainPage.displayName = 'OrgSendingDomainPage'\n\nexport default OrgSendingDomainDetail\n"],"names":["PageHeaderRecord","pluginDocsHelp","mdiDeleteOutline","mdiEmailCheckOutline","AppLink","CardDisplay","MdiIcon","useConfirmationContext","RowActionsMenu","useSnackbar","SenderReadinessPanel","useSendingApi","Alert","Button","Chip","Divider","Stack","Typography","useRouter","useCallback","useEffect","useRef","useState","describeOrgSendingDomainRemoval","describeSendingDomain","INCONCLUSIVE_CHECK","OrgSiteSelect","orgSiteName","useEmailOrgMount","useOrgSitePage","RecordRow","useOrgSendingViews","OrgSendingDomainDetail","props","mount","OrgSendingDomainPage","displayName","record","domain","basePath","call","router","confirm","enqueueSnackbar","notifyRef","current","sites","views","reload","first","loading","viewsLoading","canManage","setRecord","refusal","setRefusal","setLoading","busy","setBusy","unreachable","setUnreachable","assignHostId","setAssignHostId","load","response","payload","path","method","query","orgId","ok","error","domains","find","one","listed","status","using","hosts","filter","site","id","selected","unchecked","length","offered","handleVerify","body","action","variant","Promise","all","verified","handleRequestRecords","handleUse","hostId","next","from","handleRelease","map","name","issued","platformDomain","confirmationButtonProps","color","then","catch","push","state","pendingProvider","issueError","lastIssueError","missing","lastMissing","Set","actions","key","label","icon","size","destructive","onClick","card","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","direction","spacing","sx","alignItems","component","componentVariant","nativeButton","href","items","severity","useFlexGap","flexWrap","sending","text","fontWeight","disabled","value","onChange","startIcon","dmarc","policy","consequence","readiness","records","some","entry","has","type","dmarcSuggestion","gap","title"],"mappings":"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"}
@@ -25,6 +25,7 @@ import { Alert, Button, Chip, Divider, Stack, Typography } from "@mui/material";
25
25
  import { useRouter } from "next/navigation";
26
26
  import { useCallback, useEffect, useRef, useState } from "react";
27
27
  import { describeSendingDomain, describeSendingDomainRemoval, INCONCLUSIVE_CHECK } from "../model/sending-domain-status.js";
28
+ import SenderReadinessPanel from "@aglyn/tenant-feature-instance/components/sender-readiness-panel";
28
29
  import { useSendingApi } from "@aglyn/tenant-feature-instance/hooks/use-sending-identity-api";
29
30
  /**
30
31
  * The record block, in the shape a registrar's own form asks for. Shared with
@@ -410,6 +411,9 @@ RecordRow.displayName = 'RecordRow';
410
411
  severity: record.dmarc.policy === 'reject' && record.status !== 'verified' ? 'error' : 'info',
411
412
  children: record.dmarc.consequence
412
413
  }) : null,
414
+ record.readiness ? /*#__PURE__*/ _jsx(SenderReadinessPanel, {
415
+ readiness: record.readiness
416
+ }) : null,
413
417
  ((_record_records = record.records) == null ? void 0 : _record_records.length) && record.records.some((entry)=>entry.value) ? /*#__PURE__*/ _jsxs(_Fragment, {
414
418
  children: [
415
419
  /*#__PURE__*/ _jsx(Divider, {}),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/sending-domain-detail.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { PageHeaderRecord, pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiDeleteOutline, mdiEmailCheckOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Chip,\n Divider,\n Stack,\n Typography,\n} from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n describeSendingDomain,\n describeSendingDomainRemoval,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport {\n useSendingApi,\n type SendingDnsRecordView,\n type SendingDomainView,\n type SendingIdentityView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\n\nexport interface SendingDomainDetailProps {\n hostId: string\n domain: string\n basePath: string\n}\n\n/**\n * The record block, in the shape a registrar's own form asks for. Shared with\n * the organization's page for a domain, which prints the same records.\n */\nexport function RecordRow(props: {\n record: SendingDnsRecordView\n missing: boolean\n}) {\n const { record, missing } = props\n const target = record.priority\n ? `${record.priority} ${record.value}`\n : record.value\n return (\n <Stack spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip size=\"small\" label={record.type} variant=\"outlined\" />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {record.purpose === 'spf'\n ? 'Authorization'\n : record.purpose === 'dkim'\n ? 'Signing key'\n : record.purpose === 'return-path'\n ? 'Bounce routing'\n : 'DMARC policy'}\n </Typography>\n {record.required ? null : (\n <Chip size=\"small\" label=\"Optional\" variant=\"outlined\" />\n )}\n {missing ? <Chip size=\"small\" label=\"Not seen yet\" color=\"warning\" /> : null}\n </Stack>\n <Typography\n variant=\"body2\"\n component=\"code\"\n sx={{\n p: 1,\n bgcolor: 'action.hover',\n borderRadius: 1,\n fontFamily: 'monospace',\n whiteSpace: 'pre-wrap',\n overflowWrap: 'anywhere',\n }}\n >\n {`${record.type} ${record.name} → ${target || '(not issued yet)'}`}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {record.note}\n </Typography>\n </Stack>\n )\n}\nRecordRow.displayName = 'RecordRow'\n\n/**\n * ONE SENDING DOMAIN: what to publish, whether we can see it, and whether\n * this site sends as it.\n *\n * ## `inconclusive` is not a failure, and this page is where that is felt\n *\n * Pressing Check DNS has three outcomes, not two. The third is that nobody\n * answered — a resolver outage, a timeout — and the route says so with a\n * `503` rather than with `verified: false`. It is held HERE, in the surface's\n * own state, next to a status the record still holds, because the record was\n * not changed and saying otherwise would send a customer whose DNS is perfect\n * to go and edit a zone that has nothing wrong with it.\n *\n * That is why `lastCheck` is a separate piece of state from `record.status`\n * and never assigned into it.\n */\nexport function SendingDomainDetail(props: SendingDomainDetailProps) {\n const { hostId, domain, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { enqueueSnackbar } = useSnackbar()\n /*\n * The snackbar held in a REF, for the reason `use-campaign-send-api` holds\n * the user in one: `load` is a dependency of the effect that runs it, so\n * anything whose identity changes per render re-runs the fetch — and a\n * hook returning a fresh object each render turns that into a loop that\n * never settles. Reading it through a ref makes the effect depend on the\n * things that actually identify the request.\n */\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n const { confirm } = useConfirmationContext()\n\n const [identity, setIdentity] = useState<SendingIdentityView | null>(null)\n const [record, setRecord] = useState<SendingDomainView | null>(null)\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n /**\n * The outcome of the last check the reader ran, and ONLY when it was\n * inconclusive. Never merged into the record: an unanswered lookup is\n * evidence of nothing, and the record's own status is still the last thing\n * we actually established.\n */\n const [unreachable, setUnreachable] = useState(false)\n\n const load = useCallback(async () => {\n setLoading(true)\n const view = await call({\n path: 'sending-identity',\n method: 'GET',\n query: { hostId },\n })\n if (!view.response.ok) {\n setLoading(false)\n return void notifyRef.current(\n view.payload?.error ?? 'Could not read the sending identity',\n { variant: 'warning' },\n )\n }\n setIdentity(view.payload as SendingIdentityView)\n const orgId = String(view.payload?.orgId ?? '')\n const domains = orgId\n ? await call({\n path: 'sending-domains',\n method: 'GET',\n query: { orgId },\n })\n : null\n setLoading(false)\n const found = (domains?.payload?.domains ?? []).find(\n (one: SendingDomainView) => one.domain === domain,\n )\n setRecord(found ?? null)\n }, [call, hostId, domain])\n\n useEffect(() => {\n void load()\n }, [load])\n\n const handleVerify = useCallback(async () => {\n if (busy || !identity?.orgId) return\n setBusy(true)\n setUnreachable(false)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: identity.orgId, domain, action: 'verify' },\n })\n setBusy(false)\n /*\n * THE THIRD OUTCOME.\n *\n * A `503` here means the lookup got no answer. Nothing about the domain\n * changed, so nothing on screen changes either except a notice saying the\n * question could not be asked. Treating it as a failed check — which is\n * what a plain `!response.ok` branch would do — is how a customer ends up\n * rewriting records that were already correct.\n */\n if (response.status === 503) {\n setUnreachable(true)\n return\n }\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'The check could not run', {\n variant: 'warning',\n })\n }\n await load()\n notifyRef.current(\n payload?.verified\n ? 'Verified — this domain can send'\n : 'Checked. Some records are still missing.',\n { variant: payload?.verified ? 'success' : 'info' },\n )\n }, [busy, call, identity?.orgId, domain, load])\n\n const handleRequestRecords = useCallback(async () => {\n if (busy || !identity?.orgId) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: identity.orgId, domain, action: 'request' },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'Could not request records', {\n variant: 'warning',\n })\n }\n await load()\n }, [busy, call, identity?.orgId, domain, load])\n\n const handleUse = useCallback(\n async (next: string) => {\n if (busy) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain: next },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'Could not change the identity', {\n variant: 'warning',\n })\n }\n await load()\n /*\n * The address the ROUTE settled on, not the one this click asked for.\n *\n * Clearing the selection does not name a domain — it hands the site back\n * to the one provisioned for it — so only the response knows what the\n * `From:` line now says. A message composed from `next` would announce\n * an address chosen here rather than the one recipients will see.\n */\n notifyRef.current(\n payload?.from\n ? `This site now sends as ${payload.from}`\n : 'This site’s sending address was changed',\n { variant: 'success' },\n )\n },\n [busy, call, hostId, load],\n )\n\n const handleRelease = useCallback(async () => {\n if (!identity?.orgId) return\n /*\n * The consequence stated rather than implied, and it is not one\n * consequence — see `describeSendingDomainRemoval`, which holds all three.\n *\n * Taken from the model rather than written here because the LIST asks to\n * remove the same record from its row menu. Two confirmations describing\n * one action differently is how a merchant learns to dismiss the harsher\n * of them.\n */\n const ok = await confirm({\n ...describeSendingDomainRemoval({\n domain,\n selected: identity.selected,\n platformDomain: identity.platformDomain,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel, so the\n // resolved value alone can never gate this.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: identity.orgId, domain },\n })\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'Could not remove the domain', {\n variant: 'warning',\n })\n }\n router.push(`${basePath}/sending`)\n }, [confirm, call, identity, domain, router, basePath])\n\n const state = describeSendingDomain({\n status: record?.status ?? 'requested',\n pendingProvider: record?.pendingProvider ?? record?.status === 'requested',\n issueError: record?.lastIssueError ?? null,\n missing: record?.lastMissing ?? null,\n })\n const missing = new Set(record?.lastMissing ?? [])\n const canManage = identity?.canManage === true\n const isSelected = identity?.selected === domain\n\n const actions: RowActionsMenuItem[] = canManage\n ? [\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleRelease(),\n },\n ]\n : []\n\n /* The card, named so the page chrome above it is a plain list of\n what this surface publishes upward. */\n const card = (\n <CardDisplay\n header={'Sending domain'}\n help={pluginDocsHelp('emailCampaigns', {\n anchor: '#sending-domains',\n })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: actions.length ? <RowActionsMenu items={actions} /> : null,\n }}\n >\n {loading ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : !record ? (\n <Alert severity=\"warning\">\n {`${domain} is not claimed by this workspace. It may have been ` +\n `removed — go back and add it again if you still want it.`}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n {isSelected ? (\n <Chip label=\"This site sends as this\" color=\"primary\" size=\"small\" />\n ) : null}\n </Stack>\n\n <Alert severity={state.severity}>{state.text}</Alert>\n\n {/*\n The unanswered check, BESIDE the state above and never instead of\n it. Both are true at once: the record still says what it said, and\n the last thing we tried could not be completed.\n */}\n {unreachable ? (\n <Alert severity={INCONCLUSIVE_CHECK.severity} icon={false}>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {INCONCLUSIVE_CHECK.label}\n </Typography>\n <Typography variant=\"body2\">{INCONCLUSIVE_CHECK.text}</Typography>\n </Alert>\n ) : null}\n\n {/*\n The customer's DMARC policy, read and never written. It decides\n whether unauthenticated mail from this domain is filed as spam or\n refused outright, which is something a person publishing these\n records should know before they finish rather than after.\n */}\n {record.dmarc ? (\n <Alert\n severity={\n record.dmarc.policy === 'reject' && record.status !== 'verified'\n ? 'error'\n : 'info'\n }\n >\n {record.dmarc.consequence}\n </Alert>\n ) : null}\n\n {record.records?.length &&\n record.records.some((entry) => entry.value) ? (\n <>\n <Divider />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Publish these records'}\n </Typography>\n <Stack spacing={2}>\n {record.records\n .filter((entry) => entry.value)\n .map((entry) => (\n <RecordRow\n key={`${entry.type}:${entry.name}`}\n record={entry}\n missing={missing.has(`${entry.type}:${entry.name}`)}\n />\n ))}\n {record.dmarcSuggestion &&\n record.dmarc?.policy === 'absent' ? (\n <RecordRow record={record.dmarcSuggestion} missing={false} />\n ) : null}\n </Stack>\n </>\n ) : null}\n\n <Divider />\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', gap: 1 }}>\n {record.status === 'requested' && canManage ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleRequestRecords()}\n >\n {busy ? 'Working…' : 'Request records'}\n </Button>\n ) : null}\n {record.status !== 'requested' ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleVerify()}\n >\n {busy ? 'Checking…' : 'Check DNS'}\n </Button>\n ) : null}\n {canManage && record.status === 'verified' && !isSelected ? (\n <Button\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />}\n disabled={busy}\n onClick={() => void handleUse(domain)}\n >\n {'Send this site’s email as this domain'}\n </Button>\n ) : null}\n {canManage && isSelected ? (\n /*\n * Clearing the selection returns the site to the domain\n * PROVISIONED for it — `{label}.mail.aglyn.app` — not to the\n * shared pool, and the label says so. A site whose plan carries\n * no such domain is refused here with the reason, which is why\n * the control is worded as leaving this domain rather than\n * promising a destination it may not reach.\n */\n <Button\n variant=\"outlined\"\n disabled={busy}\n onClick={() => void handleUse('platform')}\n >\n {'Stop sending this site’s email as this domain'}\n </Button>\n ) : null}\n </Stack>\n </Stack>\n )}\n </CardDisplay>\n )\n\n return (\n <>\n {/* The page heading and the trail name the domain; this card is\n then free to say what it holds rather than repeating the title. */}\n <PageHeaderRecord title={domain} />\n {card}\n </>\n )\n}\nSendingDomainDetail.displayName = 'SendingDomainDetail'\n\nexport default SendingDomainDetail\n"],"names":["PageHeaderRecord","pluginDocsHelp","mdiDeleteOutline","mdiEmailCheckOutline","CardDisplay","MdiIcon","useConfirmationContext","RowActionsMenu","useSnackbar","Alert","Button","Chip","Divider","Stack","Typography","useRouter","useCallback","useEffect","useRef","useState","describeSendingDomain","describeSendingDomainRemoval","INCONCLUSIVE_CHECK","useSendingApi","RecordRow","props","record","missing","target","priority","value","spacing","direction","sx","alignItems","size","label","type","variant","fontWeight","purpose","required","color","component","p","bgcolor","borderRadius","fontFamily","whiteSpace","overflowWrap","name","note","displayName","SendingDomainDetail","hostId","domain","basePath","call","router","enqueueSnackbar","notifyRef","current","confirm","identity","setIdentity","setRecord","loading","setLoading","busy","setBusy","unreachable","setUnreachable","load","view","domains","path","method","query","response","ok","payload","error","orgId","String","found","find","one","handleVerify","body","action","status","verified","handleRequestRecords","handleUse","next","from","handleRelease","selected","platformDomain","confirmationButtonProps","then","catch","push","state","pendingProvider","issueError","lastIssueError","lastMissing","Set","canManage","isSelected","actions","key","icon","destructive","onClick","card","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","length","items","severity","sending","text","dmarc","policy","consequence","records","some","entry","filter","map","has","dmarcSuggestion","flexWrap","gap","disabled","startIcon","title"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/sending-domain-detail.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { PageHeaderRecord, pluginDocsHelp } from '@aglyn/aglyn'\nimport { mdiDeleteOutline, mdiEmailCheckOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport RowActionsMenu, {\n type RowActionsMenuItem,\n} from '@aglyn/shared-ui-jsx/components/row-actions-menu.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Chip,\n Divider,\n Stack,\n Typography,\n} from '@mui/material'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n describeSendingDomain,\n describeSendingDomainRemoval,\n INCONCLUSIVE_CHECK,\n} from '../model/sending-domain-status'\nimport SenderReadinessPanel from '@aglyn/tenant-feature-instance/components/sender-readiness-panel'\nimport {\n useSendingApi,\n type SendingDnsRecordView,\n type SendingDomainView,\n type SendingIdentityView,\n} from '@aglyn/tenant-feature-instance/hooks/use-sending-identity-api'\n\nexport interface SendingDomainDetailProps {\n hostId: string\n domain: string\n basePath: string\n}\n\n/**\n * The record block, in the shape a registrar's own form asks for. Shared with\n * the organization's page for a domain, which prints the same records.\n */\nexport function RecordRow(props: {\n record: SendingDnsRecordView\n missing: boolean\n}) {\n const { record, missing } = props\n const target = record.priority\n ? `${record.priority} ${record.value}`\n : record.value\n return (\n <Stack spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip size=\"small\" label={record.type} variant=\"outlined\" />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {record.purpose === 'spf'\n ? 'Authorization'\n : record.purpose === 'dkim'\n ? 'Signing key'\n : record.purpose === 'return-path'\n ? 'Bounce routing'\n : 'DMARC policy'}\n </Typography>\n {record.required ? null : (\n <Chip size=\"small\" label=\"Optional\" variant=\"outlined\" />\n )}\n {missing ? <Chip size=\"small\" label=\"Not seen yet\" color=\"warning\" /> : null}\n </Stack>\n <Typography\n variant=\"body2\"\n component=\"code\"\n sx={{\n p: 1,\n bgcolor: 'action.hover',\n borderRadius: 1,\n fontFamily: 'monospace',\n whiteSpace: 'pre-wrap',\n overflowWrap: 'anywhere',\n }}\n >\n {`${record.type} ${record.name} → ${target || '(not issued yet)'}`}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {record.note}\n </Typography>\n </Stack>\n )\n}\nRecordRow.displayName = 'RecordRow'\n\n/**\n * ONE SENDING DOMAIN: what to publish, whether we can see it, and whether\n * this site sends as it.\n *\n * ## `inconclusive` is not a failure, and this page is where that is felt\n *\n * Pressing Check DNS has three outcomes, not two. The third is that nobody\n * answered — a resolver outage, a timeout — and the route says so with a\n * `503` rather than with `verified: false`. It is held HERE, in the surface's\n * own state, next to a status the record still holds, because the record was\n * not changed and saying otherwise would send a customer whose DNS is perfect\n * to go and edit a zone that has nothing wrong with it.\n *\n * That is why `lastCheck` is a separate piece of state from `record.status`\n * and never assigned into it.\n */\nexport function SendingDomainDetail(props: SendingDomainDetailProps) {\n const { hostId, domain, basePath } = props\n const call = useSendingApi()\n const router = useRouter()\n const { enqueueSnackbar } = useSnackbar()\n /*\n * The snackbar held in a REF, for the reason `use-campaign-send-api` holds\n * the user in one: `load` is a dependency of the effect that runs it, so\n * anything whose identity changes per render re-runs the fetch — and a\n * hook returning a fresh object each render turns that into a loop that\n * never settles. Reading it through a ref makes the effect depend on the\n * things that actually identify the request.\n */\n const notifyRef = useRef(enqueueSnackbar)\n notifyRef.current = enqueueSnackbar\n const { confirm } = useConfirmationContext()\n\n const [identity, setIdentity] = useState<SendingIdentityView | null>(null)\n const [record, setRecord] = useState<SendingDomainView | null>(null)\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n /**\n * The outcome of the last check the reader ran, and ONLY when it was\n * inconclusive. Never merged into the record: an unanswered lookup is\n * evidence of nothing, and the record's own status is still the last thing\n * we actually established.\n */\n const [unreachable, setUnreachable] = useState(false)\n\n const load = useCallback(async () => {\n setLoading(true)\n const view = await call({\n path: 'sending-identity',\n method: 'GET',\n query: { hostId },\n })\n if (!view.response.ok) {\n setLoading(false)\n return void notifyRef.current(\n view.payload?.error ?? 'Could not read the sending identity',\n { variant: 'warning' },\n )\n }\n setIdentity(view.payload as SendingIdentityView)\n const orgId = String(view.payload?.orgId ?? '')\n const domains = orgId\n ? await call({\n path: 'sending-domains',\n method: 'GET',\n query: { orgId },\n })\n : null\n setLoading(false)\n const found = (domains?.payload?.domains ?? []).find(\n (one: SendingDomainView) => one.domain === domain,\n )\n setRecord(found ?? null)\n }, [call, hostId, domain])\n\n useEffect(() => {\n void load()\n }, [load])\n\n const handleVerify = useCallback(async () => {\n if (busy || !identity?.orgId) return\n setBusy(true)\n setUnreachable(false)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: identity.orgId, domain, action: 'verify' },\n })\n setBusy(false)\n /*\n * THE THIRD OUTCOME.\n *\n * A `503` here means the lookup got no answer. Nothing about the domain\n * changed, so nothing on screen changes either except a notice saying the\n * question could not be asked. Treating it as a failed check — which is\n * what a plain `!response.ok` branch would do — is how a customer ends up\n * rewriting records that were already correct.\n */\n if (response.status === 503) {\n setUnreachable(true)\n return\n }\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'The check could not run', {\n variant: 'warning',\n })\n }\n await load()\n notifyRef.current(\n payload?.verified\n ? 'Verified — this domain can send'\n : 'Checked. Some records are still missing.',\n { variant: payload?.verified ? 'success' : 'info' },\n )\n }, [busy, call, identity?.orgId, domain, load])\n\n const handleRequestRecords = useCallback(async () => {\n if (busy || !identity?.orgId) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'POST',\n body: { orgId: identity.orgId, domain, action: 'request' },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'Could not request records', {\n variant: 'warning',\n })\n }\n await load()\n }, [busy, call, identity?.orgId, domain, load])\n\n const handleUse = useCallback(\n async (next: string) => {\n if (busy) return\n setBusy(true)\n const { response, payload } = await call({\n path: 'sending-identity',\n method: 'POST',\n body: { hostId, domain: next },\n })\n setBusy(false)\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'Could not change the identity', {\n variant: 'warning',\n })\n }\n await load()\n /*\n * The address the ROUTE settled on, not the one this click asked for.\n *\n * Clearing the selection does not name a domain — it hands the site back\n * to the one provisioned for it — so only the response knows what the\n * `From:` line now says. A message composed from `next` would announce\n * an address chosen here rather than the one recipients will see.\n */\n notifyRef.current(\n payload?.from\n ? `This site now sends as ${payload.from}`\n : 'This site’s sending address was changed',\n { variant: 'success' },\n )\n },\n [busy, call, hostId, load],\n )\n\n const handleRelease = useCallback(async () => {\n if (!identity?.orgId) return\n /*\n * The consequence stated rather than implied, and it is not one\n * consequence — see `describeSendingDomainRemoval`, which holds all three.\n *\n * Taken from the model rather than written here because the LIST asks to\n * remove the same record from its row menu. Two confirmations describing\n * one action differently is how a merchant learns to dismiss the harsher\n * of them.\n */\n const ok = await confirm({\n ...describeSendingDomainRemoval({\n domain,\n selected: identity.selected,\n platformDomain: identity.platformDomain,\n }),\n confirmationButtonProps: { color: 'error' },\n })\n // `confirm` resolves with no value and REJECTS on cancel, so the\n // resolved value alone can never gate this.\n .then(() => true)\n .catch(() => false)\n if (!ok) return\n const { response, payload } = await call({\n path: 'sending-domains',\n method: 'DELETE',\n query: { orgId: identity.orgId, domain },\n })\n if (!response.ok) {\n return void notifyRef.current(payload?.error ?? 'Could not remove the domain', {\n variant: 'warning',\n })\n }\n router.push(`${basePath}/sending`)\n }, [confirm, call, identity, domain, router, basePath])\n\n const state = describeSendingDomain({\n status: record?.status ?? 'requested',\n pendingProvider: record?.pendingProvider ?? record?.status === 'requested',\n issueError: record?.lastIssueError ?? null,\n missing: record?.lastMissing ?? null,\n })\n const missing = new Set(record?.lastMissing ?? [])\n const canManage = identity?.canManage === true\n const isSelected = identity?.selected === domain\n\n const actions: RowActionsMenuItem[] = canManage\n ? [\n {\n key: 'release',\n label: 'Remove domain',\n icon: <MdiIcon path={mdiDeleteOutline.path} size={0.8} />,\n destructive: true,\n onClick: () => void handleRelease(),\n },\n ]\n : []\n\n /* The card, named so the page chrome above it is a plain list of\n what this surface publishes upward. */\n const card = (\n <CardDisplay\n header={'Sending domain'}\n help={pluginDocsHelp('emailCampaigns', {\n anchor: '#sending-domains',\n })}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n HeaderProps={{\n action: actions.length ? <RowActionsMenu items={actions} /> : null,\n }}\n >\n {loading ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : !record ? (\n <Alert severity=\"warning\">\n {`${domain} is not claimed by this workspace. It may have been ` +\n `removed — go back and add it again if you still want it.`}\n </Alert>\n ) : (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Chip\n label={state.label}\n color={state.color}\n variant={state.sending ? 'filled' : 'outlined'}\n />\n {isSelected ? (\n <Chip label=\"This site sends as this\" color=\"primary\" size=\"small\" />\n ) : null}\n </Stack>\n\n <Alert severity={state.severity}>{state.text}</Alert>\n\n {/*\n The unanswered check, BESIDE the state above and never instead of\n it. Both are true at once: the record still says what it said, and\n the last thing we tried could not be completed.\n */}\n {unreachable ? (\n <Alert severity={INCONCLUSIVE_CHECK.severity} icon={false}>\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {INCONCLUSIVE_CHECK.label}\n </Typography>\n <Typography variant=\"body2\">{INCONCLUSIVE_CHECK.text}</Typography>\n </Alert>\n ) : null}\n\n {/*\n The customer's DMARC policy, read and never written. It decides\n whether unauthenticated mail from this domain is filed as spam or\n refused outright, which is something a person publishing these\n records should know before they finish rather than after.\n */}\n {record.dmarc ? (\n <Alert\n severity={\n record.dmarc.policy === 'reject' && record.status !== 'verified'\n ? 'error'\n : 'info'\n }\n >\n {record.dmarc.consequence}\n </Alert>\n ) : null}\n\n {/*\n SPF, DKIM and DMARC as a receiver reads them, and whether they\n align with the From domain (AGL-3328). The records table below is\n what to publish; this is what the published zone adds up to.\n */}\n {record.readiness ? (\n <SenderReadinessPanel readiness={record.readiness} />\n ) : null}\n {record.records?.length &&\n record.records.some((entry) => entry.value) ? (\n <>\n <Divider />\n <Typography variant=\"body2\" sx={{ fontWeight: 'bold' }}>\n {'Publish these records'}\n </Typography>\n <Stack spacing={2}>\n {record.records\n .filter((entry) => entry.value)\n .map((entry) => (\n <RecordRow\n key={`${entry.type}:${entry.name}`}\n record={entry}\n missing={missing.has(`${entry.type}:${entry.name}`)}\n />\n ))}\n {record.dmarcSuggestion &&\n record.dmarc?.policy === 'absent' ? (\n <RecordRow record={record.dmarcSuggestion} missing={false} />\n ) : null}\n </Stack>\n </>\n ) : null}\n\n <Divider />\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', gap: 1 }}>\n {record.status === 'requested' && canManage ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleRequestRecords()}\n >\n {busy ? 'Working…' : 'Request records'}\n </Button>\n ) : null}\n {record.status !== 'requested' ? (\n <Button\n variant=\"contained\"\n disabled={busy}\n onClick={() => void handleVerify()}\n >\n {busy ? 'Checking…' : 'Check DNS'}\n </Button>\n ) : null}\n {canManage && record.status === 'verified' && !isSelected ? (\n <Button\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiEmailCheckOutline.path} size={0.8} />}\n disabled={busy}\n onClick={() => void handleUse(domain)}\n >\n {'Send this site’s email as this domain'}\n </Button>\n ) : null}\n {canManage && isSelected ? (\n /*\n * Clearing the selection returns the site to the domain\n * PROVISIONED for it — `{label}.mail.aglyn.app` — not to the\n * shared pool, and the label says so. A site whose plan carries\n * no such domain is refused here with the reason, which is why\n * the control is worded as leaving this domain rather than\n * promising a destination it may not reach.\n */\n <Button\n variant=\"outlined\"\n disabled={busy}\n onClick={() => void handleUse('platform')}\n >\n {'Stop sending this site’s email as this domain'}\n </Button>\n ) : null}\n </Stack>\n </Stack>\n )}\n </CardDisplay>\n )\n\n return (\n <>\n {/* The page heading and the trail name the domain; this card is\n then free to say what it holds rather than repeating the title. */}\n <PageHeaderRecord title={domain} />\n {card}\n </>\n )\n}\nSendingDomainDetail.displayName = 'SendingDomainDetail'\n\nexport default SendingDomainDetail\n"],"names":["PageHeaderRecord","pluginDocsHelp","mdiDeleteOutline","mdiEmailCheckOutline","CardDisplay","MdiIcon","useConfirmationContext","RowActionsMenu","useSnackbar","Alert","Button","Chip","Divider","Stack","Typography","useRouter","useCallback","useEffect","useRef","useState","describeSendingDomain","describeSendingDomainRemoval","INCONCLUSIVE_CHECK","SenderReadinessPanel","useSendingApi","RecordRow","props","record","missing","target","priority","value","spacing","direction","sx","alignItems","size","label","type","variant","fontWeight","purpose","required","color","component","p","bgcolor","borderRadius","fontFamily","whiteSpace","overflowWrap","name","note","displayName","SendingDomainDetail","hostId","domain","basePath","call","router","enqueueSnackbar","notifyRef","current","confirm","identity","setIdentity","setRecord","loading","setLoading","busy","setBusy","unreachable","setUnreachable","load","view","domains","path","method","query","response","ok","payload","error","orgId","String","found","find","one","handleVerify","body","action","status","verified","handleRequestRecords","handleUse","next","from","handleRelease","selected","platformDomain","confirmationButtonProps","then","catch","push","state","pendingProvider","issueError","lastIssueError","lastMissing","Set","canManage","isSelected","actions","key","icon","destructive","onClick","card","header","help","anchor","contentGutterX","contentGutterY","contentBordered","HeaderProps","length","items","severity","sending","text","dmarc","policy","consequence","readiness","records","some","entry","filter","map","has","dmarcSuggestion","flexWrap","gap","disabled","startIcon","title"],"mappings":"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"}