mirror of
https://github.com/xtr-dev/payload-mailing.git
synced 2025-12-10 08:13:23 +00:00
Add fromName field support to emails collection
- Add fromName field to Emails collection schema for sender display name - Update BaseEmailDocument and QueuedEmail interfaces to include fromName - Add SendEmailTaskInput support for fromName field in job tasks - Update MailingService to combine fromName and from into proper "Name <email>" format - Add fromName, from, and replyTo fields to job input schema for admin UI - Update field copying logic to handle new sender-related fields Users can now specify a display name for emails (e.g., "John Doe <john@example.com>"). 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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<Email>(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<MyEmail>(payload, {
|
|
||||||
data: {
|
|
||||||
to: 'user@example.com',
|
|
||||||
subject: 'Hello',
|
|
||||||
html: '<p>Hello World</p>',
|
|
||||||
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.
|
|
||||||
@@ -49,6 +49,13 @@ const Emails: CollectionConfig = {
|
|||||||
description: 'Sender email address (optional, uses default if not provided)',
|
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 <john@example.com>")',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'replyTo',
|
name: 'replyTo',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ export interface SendEmailTaskInput {
|
|||||||
to: string | string[]
|
to: string | string[]
|
||||||
cc?: string | string[]
|
cc?: string | string[]
|
||||||
bcc?: string | string[]
|
bcc?: string | string[]
|
||||||
|
from?: string
|
||||||
|
fromName?: string
|
||||||
|
replyTo?: string
|
||||||
scheduledAt?: string | Date // ISO date string or Date object
|
scheduledAt?: string | Date // ISO date string or Date object
|
||||||
priority?: number
|
priority?: number
|
||||||
|
|
||||||
@@ -39,7 +42,7 @@ function transformTaskInputToSendEmailOptions(taskInput: SendEmailTaskInput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Standard email fields that should be copied to data
|
// 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
|
// Template-specific fields that should not be copied to data
|
||||||
const templateFields = ['templateSlug', 'variables']
|
const templateFields = ['templateSlug', 'variables']
|
||||||
@@ -135,6 +138,30 @@ export const sendEmailJob = {
|
|||||||
description: 'Optional comma-separated list of BCC email addresses'
|
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',
|
name: 'scheduledAt',
|
||||||
type: 'date' as const,
|
type: 'date' as const,
|
||||||
|
|||||||
@@ -238,8 +238,16 @@ export class MailingService implements IMailingService {
|
|||||||
id: emailId,
|
id: emailId,
|
||||||
}) as BaseEmailDocument
|
}) 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 = {
|
const mailOptions = {
|
||||||
from: email.from,
|
from: fromField,
|
||||||
to: email.to,
|
to: email.to,
|
||||||
cc: email.cc || undefined,
|
cc: email.cc || undefined,
|
||||||
bcc: email.bcc || undefined,
|
bcc: email.bcc || undefined,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface BaseEmailDocument {
|
|||||||
cc?: string[] | null
|
cc?: string[] | null
|
||||||
bcc?: string[] | null
|
bcc?: string[] | null
|
||||||
from?: string | null
|
from?: string | null
|
||||||
|
fromName?: string | null
|
||||||
replyTo?: string | null
|
replyTo?: string | null
|
||||||
subject: string
|
subject: string
|
||||||
html: string
|
html: string
|
||||||
@@ -83,6 +84,7 @@ export interface QueuedEmail {
|
|||||||
cc?: string[] | null
|
cc?: string[] | null
|
||||||
bcc?: string[] | null
|
bcc?: string[] | null
|
||||||
from?: string | null
|
from?: string | null
|
||||||
|
fromName?: string | null
|
||||||
replyTo?: string | null
|
replyTo?: string | null
|
||||||
subject: string
|
subject: string
|
||||||
html: string
|
html: string
|
||||||
|
|||||||
Reference in New Issue
Block a user