payload-plugin-newsletter 0.14.3 → 0.15.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fields/emailContent.ts"],"sourcesContent":["import { \n BoldFeature,\n ItalicFeature,\n UnderlineFeature,\n StrikethroughFeature,\n LinkFeature,\n OrderedListFeature,\n UnorderedListFeature,\n HeadingFeature,\n ParagraphFeature,\n AlignFeature,\n BlockquoteFeature,\n BlocksFeature,\n UploadFeature,\n FixedToolbarFeature,\n InlineToolbarFeature,\n lexicalEditor,\n} from '@payloadcms/richtext-lexical'\nimport type { RichTextField } from 'payload'\n\n/**\n * Email-safe features for Lexical editor\n * Only includes features that render consistently across email clients\n */\n// Using any[] here because Payload's FeatureProviderServer type is complex\n// and varies between versions. The features are properly typed by Payload internally.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const emailSafeFeatures: any[] = [\n // Toolbars\n FixedToolbarFeature(), // Fixed toolbar at the top\n InlineToolbarFeature(), // Floating toolbar when text is selected\n \n // Basic text formatting\n BoldFeature(),\n ItalicFeature(),\n UnderlineFeature(),\n StrikethroughFeature(),\n \n // Links with enhanced configuration\n LinkFeature({\n fields: [\n {\n name: 'url',\n type: 'text',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'newTab',\n type: 'checkbox',\n label: 'Open in new tab',\n defaultValue: false,\n },\n ],\n }),\n \n // Lists\n OrderedListFeature(),\n UnorderedListFeature(),\n \n // Headings - limited to h1, h2, h3 for email compatibility\n HeadingFeature({\n enabledHeadingSizes: ['h1', 'h2', 'h3'],\n }),\n \n // Basic paragraph and alignment\n ParagraphFeature(),\n AlignFeature(),\n \n // Blockquotes\n BlockquoteFeature(),\n \n // Upload feature for images\n UploadFeature({\n collections: {\n media: {\n fields: [\n {\n name: 'caption',\n type: 'text',\n admin: {\n description: 'Optional caption for the image',\n },\n },\n {\n name: 'altText',\n type: 'text',\n label: 'Alt Text',\n required: true,\n admin: {\n description: 'Alternative text for accessibility and when image cannot be displayed',\n },\n },\n ],\n },\n },\n }),\n \n // Custom blocks for email-specific content\n BlocksFeature({\n blocks: [\n {\n slug: 'button',\n fields: [\n {\n name: 'text',\n type: 'text',\n label: 'Button Text',\n required: true,\n },\n {\n name: 'url',\n type: 'text',\n label: 'Button URL',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'style',\n type: 'select',\n label: 'Button Style',\n defaultValue: 'primary',\n options: [\n { label: 'Primary', value: 'primary' },\n { label: 'Secondary', value: 'secondary' },\n { label: 'Outline', value: 'outline' },\n ],\n },\n ],\n interfaceName: 'EmailButton',\n labels: {\n singular: 'Button',\n plural: 'Buttons',\n },\n },\n {\n slug: 'divider',\n fields: [\n {\n name: 'style',\n type: 'select',\n label: 'Divider Style',\n defaultValue: 'solid',\n options: [\n { label: 'Solid', value: 'solid' },\n { label: 'Dashed', value: 'dashed' },\n { label: 'Dotted', value: 'dotted' },\n ],\n },\n ],\n interfaceName: 'EmailDivider',\n labels: {\n singular: 'Divider',\n plural: 'Dividers',\n },\n },\n ],\n }),\n]\n\n/**\n * Creates an email-safe rich text field configuration\n */\nexport const createEmailContentField = (overrides?: Partial<RichTextField>): RichTextField => {\n return {\n name: 'content',\n type: 'richText',\n required: true,\n editor: lexicalEditor({\n features: emailSafeFeatures,\n }),\n admin: {\n description: 'Email content with limited formatting for compatibility',\n ...overrides?.admin,\n },\n ...overrides,\n }\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/fields/emailContent.ts","../src/utils/blockValidation.ts","../src/fields/broadcastInlinePreview.ts","../src/fields/newsletterScheduling.ts"],"sourcesContent":["import { \n BoldFeature,\n ItalicFeature,\n UnderlineFeature,\n StrikethroughFeature,\n LinkFeature,\n OrderedListFeature,\n UnorderedListFeature,\n HeadingFeature,\n ParagraphFeature,\n AlignFeature,\n BlockquoteFeature,\n BlocksFeature,\n UploadFeature,\n FixedToolbarFeature,\n InlineToolbarFeature,\n lexicalEditor,\n} from '@payloadcms/richtext-lexical'\nimport type { RichTextField, Block } from 'payload'\nimport { createEmailSafeBlocks } from '../utils/blockValidation'\n\n/**\n * Creates email-safe features for Lexical editor with optional additional blocks\n * Only includes features that render consistently across email clients\n */\n// Using any[] here because Payload's FeatureProviderServer type is complex\n// and varies between versions. The features are properly typed by Payload internally.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const createEmailSafeFeatures = (additionalBlocks?: Block[]): any[] => {\n // Base email-safe blocks\n const baseBlocks = [\n {\n slug: 'button',\n fields: [\n {\n name: 'text',\n type: 'text',\n label: 'Button Text',\n required: true,\n },\n {\n name: 'url',\n type: 'text',\n label: 'Button URL',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'style',\n type: 'select',\n label: 'Button Style',\n defaultValue: 'primary',\n options: [\n { label: 'Primary', value: 'primary' },\n { label: 'Secondary', value: 'secondary' },\n { label: 'Outline', value: 'outline' },\n ],\n },\n ],\n interfaceName: 'EmailButton',\n labels: {\n singular: 'Button',\n plural: 'Buttons',\n },\n },\n {\n slug: 'divider',\n fields: [\n {\n name: 'style',\n type: 'select',\n label: 'Divider Style',\n defaultValue: 'solid',\n options: [\n { label: 'Solid', value: 'solid' },\n { label: 'Dashed', value: 'dashed' },\n { label: 'Dotted', value: 'dotted' },\n ],\n },\n ],\n interfaceName: 'EmailDivider',\n labels: {\n singular: 'Divider',\n plural: 'Dividers',\n },\n },\n ] as Block[]\n\n // Merge additional blocks if provided\n const allBlocks = [\n ...baseBlocks,\n ...(additionalBlocks || [])\n ]\n\n return [\n // Toolbars\n FixedToolbarFeature(), // Fixed toolbar at the top\n InlineToolbarFeature(), // Floating toolbar when text is selected\n \n // Basic text formatting\n BoldFeature(),\n ItalicFeature(),\n UnderlineFeature(),\n StrikethroughFeature(),\n \n // Links with enhanced configuration\n LinkFeature({\n fields: [\n {\n name: 'url',\n type: 'text',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'newTab',\n type: 'checkbox',\n label: 'Open in new tab',\n defaultValue: false,\n },\n ],\n }),\n \n // Lists\n OrderedListFeature(),\n UnorderedListFeature(),\n \n // Headings - limited to h1, h2, h3 for email compatibility\n HeadingFeature({\n enabledHeadingSizes: ['h1', 'h2', 'h3'],\n }),\n \n // Basic paragraph and alignment\n ParagraphFeature(),\n AlignFeature(),\n \n // Blockquotes\n BlockquoteFeature(),\n \n // Upload feature for images\n UploadFeature({\n collections: {\n media: {\n fields: [\n {\n name: 'caption',\n type: 'text',\n admin: {\n description: 'Optional caption for the image',\n },\n },\n {\n name: 'altText',\n type: 'text',\n label: 'Alt Text',\n required: true,\n admin: {\n description: 'Alternative text for accessibility and when image cannot be displayed',\n },\n },\n ],\n },\n },\n }),\n \n // Custom blocks for email-specific content\n BlocksFeature({\n blocks: allBlocks,\n }),\n ]\n}\n\n/**\n * Creates a Lexical editor configured specifically for email content\n * Processes blocks server-side to avoid client serialization issues\n */\nexport const createEmailLexicalEditor = (customBlocks: Block[] = []): any => {\n const emailSafeBlocks = createEmailSafeBlocks(customBlocks)\n \n return lexicalEditor({\n features: [\n // Toolbars\n FixedToolbarFeature(),\n InlineToolbarFeature(),\n \n // Basic text formatting\n BoldFeature(),\n ItalicFeature(),\n UnderlineFeature(),\n StrikethroughFeature(),\n \n // Links with enhanced configuration\n LinkFeature({\n fields: [\n {\n name: 'url',\n type: 'text',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'newTab',\n type: 'checkbox',\n label: 'Open in new tab',\n defaultValue: false,\n },\n ],\n }),\n \n // Lists\n OrderedListFeature(),\n UnorderedListFeature(),\n \n // Headings - limited to h1, h2, h3 for email compatibility\n HeadingFeature({\n enabledHeadingSizes: ['h1', 'h2', 'h3'],\n }),\n \n // Basic paragraph and alignment\n ParagraphFeature(),\n AlignFeature(),\n \n // Blockquotes\n BlockquoteFeature(),\n \n // Upload feature for images\n UploadFeature({\n collections: {\n media: {\n fields: [\n {\n name: 'caption',\n type: 'text',\n admin: {\n description: 'Optional caption for the image',\n },\n },\n {\n name: 'altText',\n type: 'text',\n label: 'Alt Text',\n required: true,\n admin: {\n description: 'Alternative text for accessibility and when image cannot be displayed',\n },\n },\n ],\n },\n },\n }),\n \n // Email-safe blocks (processed server-side)\n BlocksFeature({\n blocks: emailSafeBlocks,\n }),\n ],\n })\n}\n\n/**\n * Legacy export for backward compatibility\n */\nexport const emailSafeFeatures = createEmailSafeFeatures()\n\n/**\n * Creates an email-safe rich text field configuration\n */\nexport const createEmailContentField = (\n overrides?: Partial<RichTextField> & {\n additionalBlocks?: Block[]\n editor?: any // Lexical editor instance\n }\n): RichTextField => {\n // Use provided editor or create one with blocks\n const editor = overrides?.editor || createEmailLexicalEditor(overrides?.additionalBlocks)\n\n return {\n name: 'content',\n type: 'richText',\n required: true,\n editor,\n admin: {\n description: 'Email content with limited formatting for compatibility',\n ...overrides?.admin,\n },\n ...overrides,\n }\n}","import type { Block } from 'payload'\n\n/**\n * Email-incompatible block types that should be warned about\n */\nconst EMAIL_INCOMPATIBLE_TYPES = [\n 'chart',\n 'dataTable', \n 'interactive',\n 'streamable',\n 'video',\n 'iframe',\n 'form',\n 'carousel',\n 'tabs',\n 'accordion',\n 'map'\n]\n\n/**\n * Validates that blocks are email-compatible and warns about potential issues\n */\nexport const validateEmailBlocks = (blocks: Block[]): void => {\n blocks.forEach(block => {\n if (EMAIL_INCOMPATIBLE_TYPES.includes(block.slug)) {\n console.warn(`⚠️ Block \"${block.slug}\" may not be email-compatible. Consider creating an email-specific version.`)\n }\n \n // Check for complex field types that might not work in emails\n const hasComplexFields = block.fields?.some(field => {\n const complexTypes = ['code', 'json', 'richText', 'blocks', 'array']\n return complexTypes.includes(field.type)\n })\n \n if (hasComplexFields) {\n console.warn(`⚠️ Block \"${block.slug}\" contains complex field types that may not render consistently in email clients.`)\n }\n })\n}\n\n/**\n * Creates email-safe block configurations by filtering and validating blocks\n */\nexport const createEmailSafeBlocks = (customBlocks: Block[] = []): Block[] => {\n // Validate blocks\n validateEmailBlocks(customBlocks)\n \n // Base email-safe blocks that come with the plugin\n const baseBlocks: Block[] = [\n {\n slug: 'button',\n fields: [\n {\n name: 'text',\n type: 'text',\n label: 'Button Text',\n required: true,\n },\n {\n name: 'url',\n type: 'text',\n label: 'Button URL',\n required: true,\n admin: {\n description: 'Enter the full URL (including https://)',\n },\n },\n {\n name: 'style',\n type: 'select',\n label: 'Button Style',\n defaultValue: 'primary',\n options: [\n { label: 'Primary', value: 'primary' },\n { label: 'Secondary', value: 'secondary' },\n { label: 'Outline', value: 'outline' },\n ],\n },\n ],\n interfaceName: 'EmailButton',\n labels: {\n singular: 'Button',\n plural: 'Buttons',\n },\n },\n {\n slug: 'divider',\n fields: [\n {\n name: 'style',\n type: 'select',\n label: 'Divider Style',\n defaultValue: 'solid',\n options: [\n { label: 'Solid', value: 'solid' },\n { label: 'Dashed', value: 'dashed' },\n { label: 'Dotted', value: 'dotted' },\n ],\n },\n ],\n interfaceName: 'EmailDivider',\n labels: {\n singular: 'Divider',\n plural: 'Dividers',\n },\n },\n ]\n \n // Combine base blocks with custom blocks\n return [\n ...baseBlocks,\n ...customBlocks\n ]\n}","import type { Field } from 'payload'\n\nexport const createBroadcastInlinePreviewField = (): Field => {\n return {\n name: 'broadcastInlinePreview',\n type: 'ui',\n admin: {\n components: {\n Field: 'payload-plugin-newsletter/components#BroadcastInlinePreview',\n },\n },\n }\n}","import type { Field } from 'payload'\nimport type { NewsletterPluginConfig } from '../types'\n\nexport function createNewsletterSchedulingFields(\n config: NewsletterPluginConfig\n): Field[] {\n const groupName = config.features?.newsletterScheduling?.fields?.groupName || 'newsletterScheduling'\n const contentField = config.features?.newsletterScheduling?.fields?.contentField || 'content'\n const createMarkdownField = config.features?.newsletterScheduling?.fields?.createMarkdownField !== false\n\n const fields: Field[] = [\n {\n name: groupName,\n type: 'group',\n label: 'Newsletter Scheduling',\n admin: {\n condition: (data, { user }) => user?.collection === 'users', // Only show for admin users\n },\n fields: [\n {\n name: 'scheduled',\n type: 'checkbox',\n label: 'Schedule for Newsletter',\n defaultValue: false,\n admin: {\n description: 'Schedule this content to be sent as a newsletter',\n },\n },\n {\n name: 'scheduledDate',\n type: 'date',\n label: 'Send Date',\n required: true,\n admin: {\n date: {\n pickerAppearance: 'dayAndTime',\n },\n condition: (data) => data?.[groupName]?.scheduled,\n description: 'When to send this newsletter',\n },\n },\n {\n name: 'sentDate',\n type: 'date',\n label: 'Sent Date',\n admin: {\n readOnly: true,\n condition: (data) => data?.[groupName]?.sendStatus === 'sent',\n description: 'When this newsletter was sent',\n },\n },\n {\n name: 'sendStatus',\n type: 'select',\n label: 'Status',\n options: [\n { label: 'Draft', value: 'draft' },\n { label: 'Scheduled', value: 'scheduled' },\n { label: 'Sending', value: 'sending' },\n { label: 'Sent', value: 'sent' },\n { label: 'Failed', value: 'failed' },\n ],\n defaultValue: 'draft',\n admin: {\n readOnly: true,\n description: 'Current send status',\n },\n },\n {\n name: 'emailSubject',\n type: 'text',\n label: 'Email Subject',\n required: true,\n admin: {\n condition: (data) => data?.[groupName]?.scheduled,\n description: 'Subject line for the newsletter email',\n },\n },\n {\n name: 'preheader',\n type: 'text',\n label: 'Email Preheader',\n admin: {\n condition: (data) => data?.[groupName]?.scheduled,\n description: 'Preview text that appears after the subject line',\n },\n },\n {\n name: 'segments',\n type: 'select',\n label: 'Target Segments',\n hasMany: true,\n options: [\n { label: 'All Subscribers', value: 'all' },\n ...(config.i18n?.locales?.map(locale => ({\n label: `${locale.toUpperCase()} Subscribers`,\n value: locale,\n })) || []),\n ],\n defaultValue: ['all'],\n admin: {\n condition: (data) => data?.[groupName]?.scheduled,\n description: 'Which subscriber segments to send to',\n },\n },\n {\n name: 'testEmails',\n type: 'array',\n label: 'Test Email Recipients',\n admin: {\n condition: (data) => data?.[groupName]?.scheduled && data?.[groupName]?.sendStatus === 'draft',\n description: 'Send test emails before scheduling',\n },\n fields: [\n {\n name: 'email',\n type: 'email',\n required: true,\n },\n ],\n },\n ],\n },\n ]\n\n // Add markdown companion field if requested\n if (createMarkdownField) {\n fields.push(createMarkdownFieldInternal({\n name: `${contentField}Markdown`,\n richTextField: contentField,\n label: 'Email Content (Markdown)',\n admin: {\n position: 'sidebar',\n condition: (data: any) => Boolean(data?.[contentField] && data?.[groupName]?.scheduled),\n description: 'Markdown version for email rendering',\n readOnly: true,\n },\n }))\n }\n\n return fields\n}\n\n/**\n * Create a markdown companion field for rich text\n * This creates a virtual field that converts rich text to markdown\n */\nfunction createMarkdownFieldInternal(config: {\n name: string\n richTextField: string\n label?: string\n admin?: any\n}): Field {\n return {\n name: config.name,\n type: 'textarea',\n label: config.label || 'Markdown',\n admin: {\n ...config.admin,\n description: config.admin?.description || 'Auto-generated from rich text content',\n },\n hooks: {\n afterRead: [\n async ({ data }) => {\n // Convert rich text to markdown on read\n if (data?.[config.richTextField]) {\n try {\n const { convertLexicalToMarkdown } = await import('@payloadcms/richtext-lexical')\n return convertLexicalToMarkdown({\n data: data[config.richTextField],\n } as any)\n } catch {\n return ''\n }\n }\n return ''\n },\n ],\n beforeChange: [\n () => {\n // Don't save markdown to database\n return null\n },\n ],\n },\n }\n}"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -3761,144 +3761,323 @@ init_types();
3761
3761
 
3762
3762
  // src/fields/emailContent.ts
3763
3763
  var import_richtext_lexical = require("@payloadcms/richtext-lexical");
3764
- var emailSafeFeatures = [
3765
- // Toolbars
3766
- (0, import_richtext_lexical.FixedToolbarFeature)(),
3767
- // Fixed toolbar at the top
3768
- (0, import_richtext_lexical.InlineToolbarFeature)(),
3769
- // Floating toolbar when text is selected
3770
- // Basic text formatting
3771
- (0, import_richtext_lexical.BoldFeature)(),
3772
- (0, import_richtext_lexical.ItalicFeature)(),
3773
- (0, import_richtext_lexical.UnderlineFeature)(),
3774
- (0, import_richtext_lexical.StrikethroughFeature)(),
3775
- // Links with enhanced configuration
3776
- (0, import_richtext_lexical.LinkFeature)({
3777
- fields: [
3778
- {
3779
- name: "url",
3780
- type: "text",
3781
- required: true,
3782
- admin: {
3783
- description: "Enter the full URL (including https://)"
3764
+
3765
+ // src/utils/blockValidation.ts
3766
+ var EMAIL_INCOMPATIBLE_TYPES = [
3767
+ "chart",
3768
+ "dataTable",
3769
+ "interactive",
3770
+ "streamable",
3771
+ "video",
3772
+ "iframe",
3773
+ "form",
3774
+ "carousel",
3775
+ "tabs",
3776
+ "accordion",
3777
+ "map"
3778
+ ];
3779
+ var validateEmailBlocks = (blocks) => {
3780
+ blocks.forEach((block) => {
3781
+ if (EMAIL_INCOMPATIBLE_TYPES.includes(block.slug)) {
3782
+ console.warn(`\u26A0\uFE0F Block "${block.slug}" may not be email-compatible. Consider creating an email-specific version.`);
3783
+ }
3784
+ const hasComplexFields = block.fields?.some((field) => {
3785
+ const complexTypes = ["code", "json", "richText", "blocks", "array"];
3786
+ return complexTypes.includes(field.type);
3787
+ });
3788
+ if (hasComplexFields) {
3789
+ console.warn(`\u26A0\uFE0F Block "${block.slug}" contains complex field types that may not render consistently in email clients.`);
3790
+ }
3791
+ });
3792
+ };
3793
+ var createEmailSafeBlocks = (customBlocks = []) => {
3794
+ validateEmailBlocks(customBlocks);
3795
+ const baseBlocks = [
3796
+ {
3797
+ slug: "button",
3798
+ fields: [
3799
+ {
3800
+ name: "text",
3801
+ type: "text",
3802
+ label: "Button Text",
3803
+ required: true
3804
+ },
3805
+ {
3806
+ name: "url",
3807
+ type: "text",
3808
+ label: "Button URL",
3809
+ required: true,
3810
+ admin: {
3811
+ description: "Enter the full URL (including https://)"
3812
+ }
3813
+ },
3814
+ {
3815
+ name: "style",
3816
+ type: "select",
3817
+ label: "Button Style",
3818
+ defaultValue: "primary",
3819
+ options: [
3820
+ { label: "Primary", value: "primary" },
3821
+ { label: "Secondary", value: "secondary" },
3822
+ { label: "Outline", value: "outline" }
3823
+ ]
3784
3824
  }
3785
- },
3786
- {
3787
- name: "newTab",
3788
- type: "checkbox",
3789
- label: "Open in new tab",
3790
- defaultValue: false
3825
+ ],
3826
+ interfaceName: "EmailButton",
3827
+ labels: {
3828
+ singular: "Button",
3829
+ plural: "Buttons"
3791
3830
  }
3792
- ]
3793
- }),
3794
- // Lists
3795
- (0, import_richtext_lexical.OrderedListFeature)(),
3796
- (0, import_richtext_lexical.UnorderedListFeature)(),
3797
- // Headings - limited to h1, h2, h3 for email compatibility
3798
- (0, import_richtext_lexical.HeadingFeature)({
3799
- enabledHeadingSizes: ["h1", "h2", "h3"]
3800
- }),
3801
- // Basic paragraph and alignment
3802
- (0, import_richtext_lexical.ParagraphFeature)(),
3803
- (0, import_richtext_lexical.AlignFeature)(),
3804
- // Blockquotes
3805
- (0, import_richtext_lexical.BlockquoteFeature)(),
3806
- // Upload feature for images
3807
- (0, import_richtext_lexical.UploadFeature)({
3808
- collections: {
3809
- media: {
3810
- fields: [
3811
- {
3812
- name: "caption",
3813
- type: "text",
3814
- admin: {
3815
- description: "Optional caption for the image"
3816
- }
3817
- },
3818
- {
3819
- name: "altText",
3820
- type: "text",
3821
- label: "Alt Text",
3822
- required: true,
3823
- admin: {
3824
- description: "Alternative text for accessibility and when image cannot be displayed"
3825
- }
3831
+ },
3832
+ {
3833
+ slug: "divider",
3834
+ fields: [
3835
+ {
3836
+ name: "style",
3837
+ type: "select",
3838
+ label: "Divider Style",
3839
+ defaultValue: "solid",
3840
+ options: [
3841
+ { label: "Solid", value: "solid" },
3842
+ { label: "Dashed", value: "dashed" },
3843
+ { label: "Dotted", value: "dotted" }
3844
+ ]
3845
+ }
3846
+ ],
3847
+ interfaceName: "EmailDivider",
3848
+ labels: {
3849
+ singular: "Divider",
3850
+ plural: "Dividers"
3851
+ }
3852
+ }
3853
+ ];
3854
+ return [
3855
+ ...baseBlocks,
3856
+ ...customBlocks
3857
+ ];
3858
+ };
3859
+
3860
+ // src/fields/emailContent.ts
3861
+ var createEmailSafeFeatures = (additionalBlocks) => {
3862
+ const baseBlocks = [
3863
+ {
3864
+ slug: "button",
3865
+ fields: [
3866
+ {
3867
+ name: "text",
3868
+ type: "text",
3869
+ label: "Button Text",
3870
+ required: true
3871
+ },
3872
+ {
3873
+ name: "url",
3874
+ type: "text",
3875
+ label: "Button URL",
3876
+ required: true,
3877
+ admin: {
3878
+ description: "Enter the full URL (including https://)"
3826
3879
  }
3827
- ]
3880
+ },
3881
+ {
3882
+ name: "style",
3883
+ type: "select",
3884
+ label: "Button Style",
3885
+ defaultValue: "primary",
3886
+ options: [
3887
+ { label: "Primary", value: "primary" },
3888
+ { label: "Secondary", value: "secondary" },
3889
+ { label: "Outline", value: "outline" }
3890
+ ]
3891
+ }
3892
+ ],
3893
+ interfaceName: "EmailButton",
3894
+ labels: {
3895
+ singular: "Button",
3896
+ plural: "Buttons"
3897
+ }
3898
+ },
3899
+ {
3900
+ slug: "divider",
3901
+ fields: [
3902
+ {
3903
+ name: "style",
3904
+ type: "select",
3905
+ label: "Divider Style",
3906
+ defaultValue: "solid",
3907
+ options: [
3908
+ { label: "Solid", value: "solid" },
3909
+ { label: "Dashed", value: "dashed" },
3910
+ { label: "Dotted", value: "dotted" }
3911
+ ]
3912
+ }
3913
+ ],
3914
+ interfaceName: "EmailDivider",
3915
+ labels: {
3916
+ singular: "Divider",
3917
+ plural: "Dividers"
3828
3918
  }
3829
3919
  }
3830
- }),
3831
- // Custom blocks for email-specific content
3832
- (0, import_richtext_lexical.BlocksFeature)({
3833
- blocks: [
3834
- {
3835
- slug: "button",
3920
+ ];
3921
+ const allBlocks = [
3922
+ ...baseBlocks,
3923
+ ...additionalBlocks || []
3924
+ ];
3925
+ return [
3926
+ // Toolbars
3927
+ (0, import_richtext_lexical.FixedToolbarFeature)(),
3928
+ // Fixed toolbar at the top
3929
+ (0, import_richtext_lexical.InlineToolbarFeature)(),
3930
+ // Floating toolbar when text is selected
3931
+ // Basic text formatting
3932
+ (0, import_richtext_lexical.BoldFeature)(),
3933
+ (0, import_richtext_lexical.ItalicFeature)(),
3934
+ (0, import_richtext_lexical.UnderlineFeature)(),
3935
+ (0, import_richtext_lexical.StrikethroughFeature)(),
3936
+ // Links with enhanced configuration
3937
+ (0, import_richtext_lexical.LinkFeature)({
3938
+ fields: [
3939
+ {
3940
+ name: "url",
3941
+ type: "text",
3942
+ required: true,
3943
+ admin: {
3944
+ description: "Enter the full URL (including https://)"
3945
+ }
3946
+ },
3947
+ {
3948
+ name: "newTab",
3949
+ type: "checkbox",
3950
+ label: "Open in new tab",
3951
+ defaultValue: false
3952
+ }
3953
+ ]
3954
+ }),
3955
+ // Lists
3956
+ (0, import_richtext_lexical.OrderedListFeature)(),
3957
+ (0, import_richtext_lexical.UnorderedListFeature)(),
3958
+ // Headings - limited to h1, h2, h3 for email compatibility
3959
+ (0, import_richtext_lexical.HeadingFeature)({
3960
+ enabledHeadingSizes: ["h1", "h2", "h3"]
3961
+ }),
3962
+ // Basic paragraph and alignment
3963
+ (0, import_richtext_lexical.ParagraphFeature)(),
3964
+ (0, import_richtext_lexical.AlignFeature)(),
3965
+ // Blockquotes
3966
+ (0, import_richtext_lexical.BlockquoteFeature)(),
3967
+ // Upload feature for images
3968
+ (0, import_richtext_lexical.UploadFeature)({
3969
+ collections: {
3970
+ media: {
3971
+ fields: [
3972
+ {
3973
+ name: "caption",
3974
+ type: "text",
3975
+ admin: {
3976
+ description: "Optional caption for the image"
3977
+ }
3978
+ },
3979
+ {
3980
+ name: "altText",
3981
+ type: "text",
3982
+ label: "Alt Text",
3983
+ required: true,
3984
+ admin: {
3985
+ description: "Alternative text for accessibility and when image cannot be displayed"
3986
+ }
3987
+ }
3988
+ ]
3989
+ }
3990
+ }
3991
+ }),
3992
+ // Custom blocks for email-specific content
3993
+ (0, import_richtext_lexical.BlocksFeature)({
3994
+ blocks: allBlocks
3995
+ })
3996
+ ];
3997
+ };
3998
+ var createEmailLexicalEditor = (customBlocks = []) => {
3999
+ const emailSafeBlocks = createEmailSafeBlocks(customBlocks);
4000
+ return (0, import_richtext_lexical.lexicalEditor)({
4001
+ features: [
4002
+ // Toolbars
4003
+ (0, import_richtext_lexical.FixedToolbarFeature)(),
4004
+ (0, import_richtext_lexical.InlineToolbarFeature)(),
4005
+ // Basic text formatting
4006
+ (0, import_richtext_lexical.BoldFeature)(),
4007
+ (0, import_richtext_lexical.ItalicFeature)(),
4008
+ (0, import_richtext_lexical.UnderlineFeature)(),
4009
+ (0, import_richtext_lexical.StrikethroughFeature)(),
4010
+ // Links with enhanced configuration
4011
+ (0, import_richtext_lexical.LinkFeature)({
3836
4012
  fields: [
3837
- {
3838
- name: "text",
3839
- type: "text",
3840
- label: "Button Text",
3841
- required: true
3842
- },
3843
4013
  {
3844
4014
  name: "url",
3845
4015
  type: "text",
3846
- label: "Button URL",
3847
4016
  required: true,
3848
4017
  admin: {
3849
4018
  description: "Enter the full URL (including https://)"
3850
4019
  }
3851
4020
  },
3852
4021
  {
3853
- name: "style",
3854
- type: "select",
3855
- label: "Button Style",
3856
- defaultValue: "primary",
3857
- options: [
3858
- { label: "Primary", value: "primary" },
3859
- { label: "Secondary", value: "secondary" },
3860
- { label: "Outline", value: "outline" }
3861
- ]
4022
+ name: "newTab",
4023
+ type: "checkbox",
4024
+ label: "Open in new tab",
4025
+ defaultValue: false
3862
4026
  }
3863
- ],
3864
- interfaceName: "EmailButton",
3865
- labels: {
3866
- singular: "Button",
3867
- plural: "Buttons"
3868
- }
3869
- },
3870
- {
3871
- slug: "divider",
3872
- fields: [
3873
- {
3874
- name: "style",
3875
- type: "select",
3876
- label: "Divider Style",
3877
- defaultValue: "solid",
3878
- options: [
3879
- { label: "Solid", value: "solid" },
3880
- { label: "Dashed", value: "dashed" },
3881
- { label: "Dotted", value: "dotted" }
4027
+ ]
4028
+ }),
4029
+ // Lists
4030
+ (0, import_richtext_lexical.OrderedListFeature)(),
4031
+ (0, import_richtext_lexical.UnorderedListFeature)(),
4032
+ // Headings - limited to h1, h2, h3 for email compatibility
4033
+ (0, import_richtext_lexical.HeadingFeature)({
4034
+ enabledHeadingSizes: ["h1", "h2", "h3"]
4035
+ }),
4036
+ // Basic paragraph and alignment
4037
+ (0, import_richtext_lexical.ParagraphFeature)(),
4038
+ (0, import_richtext_lexical.AlignFeature)(),
4039
+ // Blockquotes
4040
+ (0, import_richtext_lexical.BlockquoteFeature)(),
4041
+ // Upload feature for images
4042
+ (0, import_richtext_lexical.UploadFeature)({
4043
+ collections: {
4044
+ media: {
4045
+ fields: [
4046
+ {
4047
+ name: "caption",
4048
+ type: "text",
4049
+ admin: {
4050
+ description: "Optional caption for the image"
4051
+ }
4052
+ },
4053
+ {
4054
+ name: "altText",
4055
+ type: "text",
4056
+ label: "Alt Text",
4057
+ required: true,
4058
+ admin: {
4059
+ description: "Alternative text for accessibility and when image cannot be displayed"
4060
+ }
4061
+ }
3882
4062
  ]
3883
4063
  }
3884
- ],
3885
- interfaceName: "EmailDivider",
3886
- labels: {
3887
- singular: "Divider",
3888
- plural: "Dividers"
3889
4064
  }
3890
- }
4065
+ }),
4066
+ // Email-safe blocks (processed server-side)
4067
+ (0, import_richtext_lexical.BlocksFeature)({
4068
+ blocks: emailSafeBlocks
4069
+ })
3891
4070
  ]
3892
- })
3893
- ];
4071
+ });
4072
+ };
4073
+ var emailSafeFeatures = createEmailSafeFeatures();
3894
4074
  var createEmailContentField = (overrides) => {
4075
+ const editor = overrides?.editor || createEmailLexicalEditor(overrides?.additionalBlocks);
3895
4076
  return {
3896
4077
  name: "content",
3897
4078
  type: "richText",
3898
4079
  required: true,
3899
- editor: (0, import_richtext_lexical.lexicalEditor)({
3900
- features: emailSafeFeatures
3901
- }),
4080
+ editor,
3902
4081
  admin: {
3903
4082
  description: "Email content with limited formatting for compatibility",
3904
4083
  ...overrides?.admin
@@ -3923,6 +4102,7 @@ var createBroadcastInlinePreviewField = () => {
3923
4102
  // src/collections/Broadcasts.ts
3924
4103
  var createBroadcastsCollection = (pluginConfig) => {
3925
4104
  const hasProviders = !!(pluginConfig.providers?.broadcast || pluginConfig.providers?.resend);
4105
+ const customizations = pluginConfig.customizations?.broadcasts;
3926
4106
  return {
3927
4107
  slug: "broadcasts",
3928
4108
  labels: {
@@ -3943,6 +4123,8 @@ var createBroadcastsCollection = (pluginConfig) => {
3943
4123
  description: "Email subject line"
3944
4124
  }
3945
4125
  },
4126
+ // Add any additional fields from customizations after subject
4127
+ ...customizations?.additionalFields || [],
3946
4128
  {
3947
4129
  type: "row",
3948
4130
  fields: [
@@ -3964,11 +4146,16 @@ var createBroadcastsCollection = (pluginConfig) => {
3964
4146
  description: "Preview text shown in email clients"
3965
4147
  }
3966
4148
  },
3967
- createEmailContentField({
3968
- admin: {
3969
- description: "Email content"
3970
- }
3971
- })
4149
+ // Apply content field customization if provided
4150
+ // Process blocks server-side to avoid client serialization issues
4151
+ (() => {
4152
+ const emailEditor = createEmailLexicalEditor(customizations?.customBlocks);
4153
+ const baseField = createEmailContentField({
4154
+ admin: { description: "Email content" },
4155
+ editor: emailEditor
4156
+ });
4157
+ return customizations?.fieldOverrides?.content ? customizations.fieldOverrides.content(baseField) : baseField;
4158
+ })()
3972
4159
  ]
3973
4160
  },
3974
4161
  {