diff --git a/CUSTOM-TYPES.md b/CUSTOM-TYPES.md deleted file mode 100644 index 602deea..0000000 --- a/CUSTOM-TYPES.md +++ /dev/null @@ -1,95 +0,0 @@ -# Using Custom ID Types - -The mailing plugin now supports both `string` and `number` ID types. By default, it works with the generic `BaseEmailDocument` interface, but you can provide your own types for full type safety. - -## Usage with Your Generated Types - -When you have your own generated Payload types (e.g., from `payload generate:types`), you can use them with the mailing plugin: - -```typescript -import { sendEmail, BaseEmailDocument } from '@xtr-dev/payload-mailing' -import { Email } from './payload-types' // Your generated types - -// Option 1: Use your specific Email type -const email = await sendEmail(payload, { - template: { - slug: 'welcome', - variables: { name: 'John' } - }, - data: { - to: 'user@example.com', - // All your custom fields are now type-safe - } -}) - -// Option 2: Extend BaseEmailDocument for custom fields -interface MyEmail extends BaseEmailDocument { - customField: string - anotherField?: number -} - -const customEmail = await sendEmail(payload, { - data: { - to: 'user@example.com', - subject: 'Hello', - html: '

Hello World

', - customField: 'my value', // Type-safe! - } -}) -``` - -## Compatibility - -The plugin works with: -- **String IDs**: `id: string` -- **Number IDs**: `id: number` -- **Nullable fields**: Fields can be `null`, `undefined`, or have values -- **Date fields**: Timestamp fields support both `Date` objects and `string` (ISO) formats -- **JSON variables**: Variables field supports any JSON-compatible value type -- **Generated types**: Works with `payload generate:types` output - -Your Payload configuration determines which types are used. The plugin automatically adapts to your setup. - -## Type Definitions - -The base interfaces provided by the plugin: - -```typescript -// JSON value type that matches Payload's JSON field type -type JSONValue = string | number | boolean | { [k: string]: unknown } | unknown[] | null | undefined - -interface BaseEmailDocument { - id: string | number - template?: any - to: string[] - cc?: string[] | null - bcc?: string[] | null - from?: string | null - replyTo?: string | null - subject: string - html: string - text?: string | null - variables?: JSONValue // Supports any JSON-compatible value - scheduledAt?: string | Date | null - sentAt?: string | Date | null - status?: 'pending' | 'processing' | 'sent' | 'failed' | null - attempts?: number | null - lastAttemptAt?: string | Date | null - error?: string | null - priority?: number | null - createdAt?: string | Date | null - updatedAt?: string | Date | null -} - -interface BaseEmailTemplateDocument { - id: string | number - name: string - slug: string - subject?: string | null - content?: any - createdAt?: string | Date | null - updatedAt?: string | Date | null -} -``` - -These provide a foundation that works with any ID type while maintaining type safety for the core email functionality. \ No newline at end of file diff --git a/src/collections/Emails.ts b/src/collections/Emails.ts index f1ddd50..d86ba6d 100644 --- a/src/collections/Emails.ts +++ b/src/collections/Emails.ts @@ -49,6 +49,13 @@ const Emails: CollectionConfig = { description: 'Sender email address (optional, uses default if not provided)', }, }, + { + name: 'fromName', + type: 'text', + admin: { + description: 'Sender display name (optional, e.g., "John Doe" for "John Doe ")', + }, + }, { name: 'replyTo', type: 'text', diff --git a/src/jobs/sendEmailTask.ts b/src/jobs/sendEmailTask.ts index 45b254e..14f20e8 100644 --- a/src/jobs/sendEmailTask.ts +++ b/src/jobs/sendEmailTask.ts @@ -15,6 +15,9 @@ export interface SendEmailTaskInput { to: string | string[] cc?: string | string[] bcc?: string | string[] + from?: string + fromName?: string + replyTo?: string scheduledAt?: string | Date // ISO date string or Date object priority?: number @@ -39,7 +42,7 @@ function transformTaskInputToSendEmailOptions(taskInput: SendEmailTaskInput) { } // Standard email fields that should be copied to data - const standardFields = ['to', 'cc', 'bcc', 'subject', 'html', 'text', 'scheduledAt', 'priority'] + const standardFields = ['to', 'cc', 'bcc', 'from', 'fromName', 'replyTo', 'subject', 'html', 'text', 'scheduledAt', 'priority'] // Template-specific fields that should not be copied to data const templateFields = ['templateSlug', 'variables'] @@ -135,6 +138,30 @@ export const sendEmailJob = { description: 'Optional comma-separated list of BCC email addresses' } }, + { + name: 'from', + type: 'text' as const, + label: 'From Email', + admin: { + description: 'Optional sender email address (uses default if not provided)' + } + }, + { + name: 'fromName', + type: 'text' as const, + label: 'From Name', + admin: { + description: 'Optional sender display name (e.g., "John Doe")' + } + }, + { + name: 'replyTo', + type: 'text' as const, + label: 'Reply To', + admin: { + description: 'Optional reply-to email address' + } + }, { name: 'scheduledAt', type: 'date' as const, diff --git a/src/services/MailingService.ts b/src/services/MailingService.ts index fb28379..a41d9b6 100644 --- a/src/services/MailingService.ts +++ b/src/services/MailingService.ts @@ -238,8 +238,16 @@ export class MailingService implements IMailingService { id: emailId, }) as BaseEmailDocument + // Combine from and fromName for nodemailer + let fromField = email.from + if (email.fromName && email.from) { + fromField = `"${email.fromName}" <${email.from}>` + } else if (email.from) { + fromField = email.from + } + const mailOptions = { - from: email.from, + from: fromField, to: email.to, cc: email.cc || undefined, bcc: email.bcc || undefined, diff --git a/src/types/index.ts b/src/types/index.ts index bcd947f..88e44cb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -13,6 +13,7 @@ export interface BaseEmailDocument { cc?: string[] | null bcc?: string[] | null from?: string | null + fromName?: string | null replyTo?: string | null subject: string html: string @@ -83,6 +84,7 @@ export interface QueuedEmail { cc?: string[] | null bcc?: string[] | null from?: string | null + fromName?: string | null replyTo?: string | null subject: string html: string