mirror of
https://fast.feibisi.com/https://github.com/parcelvoy/platform.git
synced 2025-09-01 12:26:08 +08:00
Adds from name and auto text email (#116)
This commit is contained in:
parent
4fe89013a5
commit
b3b3e7d1e2
8 changed files with 59 additions and 27 deletions
|
@ -1,6 +1,8 @@
|
|||
export type NamedEmail = { name: string, address: string }
|
||||
|
||||
export interface Email {
|
||||
to: string
|
||||
from: string
|
||||
from: string | NamedEmail
|
||||
cc?: string
|
||||
bcc?: string
|
||||
reply_to?: string
|
||||
|
|
|
@ -3,6 +3,8 @@ import { Webhook } from '../providers/webhook/Webhook'
|
|||
import { ChannelType } from '../config/channels'
|
||||
import Model, { ModelParams } from '../core/Model'
|
||||
import { isValid, IsValidSchema } from '../core/validate'
|
||||
import { Email, NamedEmail } from '../providers/email/Email'
|
||||
import { htmlToText } from 'html-to-text'
|
||||
|
||||
export default class Template extends Model {
|
||||
project_id!: number
|
||||
|
@ -44,26 +46,17 @@ export type TemplateParams = Omit<Template, ModelParams | 'map' | 'screenshotUrl
|
|||
export type TemplateUpdateParams = Pick<Template, 'type' | 'data'>
|
||||
export type TemplateType = EmailTemplate | TextTemplate | PushTemplate | WebhookTemplate
|
||||
|
||||
export interface CompiledEmail {
|
||||
from: string
|
||||
cc?: string
|
||||
bcc?: string
|
||||
reply_to?: string
|
||||
subject: string
|
||||
preheader?: string
|
||||
text: string
|
||||
html: string
|
||||
}
|
||||
type CompiledEmail = Omit<Email, 'to' | 'headers'> & { preheader?: string }
|
||||
|
||||
export class EmailTemplate extends Template {
|
||||
declare type: 'email'
|
||||
from!: string
|
||||
from!: string | NamedEmail
|
||||
cc?: string
|
||||
bcc?: string
|
||||
reply_to?: string
|
||||
subject!: string
|
||||
preheader?: string
|
||||
text!: string
|
||||
text?: string
|
||||
html!: string
|
||||
|
||||
parseJson(json: any) {
|
||||
|
@ -75,17 +68,28 @@ export class EmailTemplate extends Template {
|
|||
this.reply_to = json?.data.reply_to
|
||||
this.subject = json?.data.subject ?? ''
|
||||
this.preheader = json?.data.preheader
|
||||
this.text = json?.data.text ?? ''
|
||||
this.text = json?.data.text
|
||||
this.html = json?.data.html ?? ''
|
||||
}
|
||||
|
||||
compile(variables: Variables): CompiledEmail {
|
||||
const html = Render(this.html, variables)
|
||||
const email: CompiledEmail = {
|
||||
subject: Render(this.subject, variables),
|
||||
from: Render(this.from, variables),
|
||||
html: Render(this.html, variables),
|
||||
text: Render(this.text, variables),
|
||||
from: typeof this.from === 'string'
|
||||
? Render(this.from, variables)
|
||||
: {
|
||||
name: Render(this.from.name, variables),
|
||||
address: Render(this.from.address, variables),
|
||||
},
|
||||
html,
|
||||
|
||||
// If the text copy has been left empty, generate from HTML
|
||||
text: this.text !== undefined && this.text !== ''
|
||||
? Render(this.text, variables)
|
||||
: htmlToText(html),
|
||||
}
|
||||
|
||||
if (this.preheader) email.preheader = Render(this.preheader, variables)
|
||||
if (this.reply_to) email.reply_to = Render(this.reply_to, variables)
|
||||
if (this.cc) email.cc = Render(this.cc, variables)
|
||||
|
|
|
@ -24,8 +24,18 @@ const templateDataEmailParams = {
|
|||
type: 'object',
|
||||
properties: {
|
||||
from: {
|
||||
type: 'string',
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
cc: {
|
||||
type: 'string',
|
||||
|
|
|
@ -300,7 +300,7 @@ export type CampaignLaunchParams = Pick<Campaign, 'send_at' | 'send_in_user_time
|
|||
export type CampaignUser = User & { state: CampaignSendState, send_at: string }
|
||||
|
||||
export interface EmailTemplateData {
|
||||
from: string
|
||||
from: { name: string, address: string }
|
||||
cc?: string
|
||||
bcc?: string
|
||||
reply_to?: string
|
||||
|
|
|
@ -12,7 +12,7 @@ export default function Preview({ template }: PreviewProps) {
|
|||
|
||||
const EmailFrame = () => <div className="email-frame">
|
||||
<div className="email-frame-header">
|
||||
<span className="email-from">{data.from}</span>
|
||||
<span className="email-from">{data.from.name} <{data.from.address}></span>
|
||||
<span className="email-subject">{data.subject}</span>
|
||||
</div>
|
||||
<Iframe content={data.html ?? ''} />
|
||||
|
|
|
@ -27,6 +27,7 @@ interface ListSelectionProps extends SelectionProps<CampaignCreateParams> {
|
|||
project: Project
|
||||
title: string
|
||||
value?: List[]
|
||||
required: boolean
|
||||
}
|
||||
|
||||
const ListSelection = ({
|
||||
|
@ -35,6 +36,7 @@ const ListSelection = ({
|
|||
name,
|
||||
title,
|
||||
value,
|
||||
required,
|
||||
}: ListSelectionProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [lists, setLists] = useState<List[]>(value ?? [])
|
||||
|
@ -57,7 +59,7 @@ const ListSelection = ({
|
|||
control,
|
||||
name,
|
||||
rules: {
|
||||
required: true,
|
||||
required,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -243,6 +245,7 @@ export function CampaignForm({ campaign, disableListSelection, onSave }: Campaig
|
|||
name="list_ids"
|
||||
value={campaign?.lists}
|
||||
control={form.control}
|
||||
required={true}
|
||||
/>
|
||||
<ListSelection
|
||||
project={project}
|
||||
|
@ -250,6 +253,7 @@ export function CampaignForm({ campaign, disableListSelection, onSave }: Campaig
|
|||
name="exclusion_list_ids"
|
||||
value={campaign?.exclusion_lists}
|
||||
control={form.control}
|
||||
required={false}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -17,6 +17,7 @@ import ImageGalleryModal from './ImageGalleryModal'
|
|||
import Modal from '../../ui/Modal'
|
||||
import { ImageIcon } from '../../ui/icons'
|
||||
import EditLocalesModal from './EditLocalesModal'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
const HtmlEditor = ({ template, setTemplate }: { template: Template, setTemplate: (template: Template) => void }) => {
|
||||
|
||||
|
@ -26,6 +27,7 @@ const HtmlEditor = ({ template, setTemplate }: { template: Template, setTemplate
|
|||
const [data, setData] = useState(template.data)
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const [showImages, setShowImages] = useState(false)
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
|
||||
function handleMount(editor: Editor.IStandaloneCodeEditor) {
|
||||
setMonaco(editor)
|
||||
|
@ -63,9 +65,17 @@ const HtmlEditor = ({ template, setTemplate }: { template: Template, setTemplate
|
|||
}
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
template.data = data
|
||||
setTemplate(template)
|
||||
async function handleSave() {
|
||||
try {
|
||||
setIsSaving(true)
|
||||
template.data = data
|
||||
await setTemplate(template)
|
||||
toast.success('Saved!')
|
||||
} catch (error) {
|
||||
toast.error('Unable to save template.')
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -120,7 +130,7 @@ const HtmlEditor = ({ template, setTemplate }: { template: Template, setTemplate
|
|||
<span className="publish-label">Last updated at</span>
|
||||
<span className="publish-date">{formatDate(preferences, template.updated_at)}</span>
|
||||
</div>
|
||||
<Button onClick={() => handleSave()}>Save</Button>
|
||||
<Button onClick={async () => await handleSave()} isLoading={isSaving}>Save</Button>
|
||||
</div>
|
||||
|
||||
<ImageGalleryModal
|
||||
|
|
|
@ -13,7 +13,8 @@ import TextInput from '../../ui/form/TextInput'
|
|||
import api from '../../api'
|
||||
|
||||
const EmailTable = ({ data }: { data: EmailTemplateData }) => <InfoTable rows={{
|
||||
'From Email': data.from,
|
||||
'From Email': data.from?.address,
|
||||
'From Name': data.from?.name,
|
||||
'Reply To': data.reply_to,
|
||||
CC: data.cc,
|
||||
BCC: data.bcc,
|
||||
|
@ -33,7 +34,8 @@ const EmailForm = ({ form }: { form: UseFormReturn<TemplateUpdateParams, any> })
|
|||
name="data.preheader"
|
||||
label="Preheader"
|
||||
textarea />
|
||||
<TextInput.Field form={form} name="data.from" label="From Email" required />
|
||||
<TextInput.Field form={form} name="data.from.address" label="From Email" required />
|
||||
<TextInput.Field form={form} name="data.from.name" label="From Name" required />
|
||||
<TextInput.Field form={form} name="data.reply_to" label="Reply To" />
|
||||
<TextInput.Field form={form} name="data.cc" label="CC" />
|
||||
<TextInput.Field form={form} name="data.bcc" label="BCC" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue