chore(plugin-form-builder): eslint fix
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"ext": "ts",
|
||||
"exec": "ts-node src/server.ts"
|
||||
"exec": "ts-node src/server.ts",
|
||||
"ext": "ts"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { PluginConfig } from '../../../types'
|
||||
|
||||
const createCharge = async (beforeChangeData: any, formConfig: PluginConfig): Promise<any> => {
|
||||
const { operation, data } = beforeChangeData
|
||||
const { data, operation } = beforeChangeData
|
||||
|
||||
let dataWithPaymentDetails = data
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import type { Email, FormattedEmail, PluginConfig } from '../../../types'
|
||||
|
||||
import { replaceDoubleCurlys } from '../../../utilities/replaceDoubleCurlys'
|
||||
import { serialize } from '../../../utilities/serializeRichText'
|
||||
|
||||
const sendEmail = async (beforeChangeData: any, formConfig: PluginConfig): Promise<any> => {
|
||||
const { operation, data } = beforeChangeData
|
||||
const { data, operation } = beforeChangeData
|
||||
|
||||
if (operation === 'create') {
|
||||
const {
|
||||
data: { id: formSubmissionID },
|
||||
req: { payload, locale },
|
||||
req: { locale, payload },
|
||||
} = beforeChangeData
|
||||
|
||||
const { form: formID, submissionData } = data || {}
|
||||
@@ -28,13 +29,13 @@ const sendEmail = async (beforeChangeData: any, formConfig: PluginConfig): Promi
|
||||
const formattedEmails: FormattedEmail[] = emails.map(
|
||||
(email: Email): FormattedEmail | null => {
|
||||
const {
|
||||
message,
|
||||
subject,
|
||||
emailTo,
|
||||
cc: emailCC,
|
||||
bcc: emailBCC,
|
||||
cc: emailCC,
|
||||
emailFrom,
|
||||
emailTo,
|
||||
message,
|
||||
replyTo: emailReplyTo,
|
||||
subject,
|
||||
} = email
|
||||
|
||||
const to = replaceDoubleCurlys(emailTo, submissionData)
|
||||
@@ -44,13 +45,13 @@ const sendEmail = async (beforeChangeData: any, formConfig: PluginConfig): Promi
|
||||
const replyTo = replaceDoubleCurlys(emailReplyTo || emailFrom, submissionData)
|
||||
|
||||
return {
|
||||
to,
|
||||
from,
|
||||
cc,
|
||||
bcc,
|
||||
cc,
|
||||
from,
|
||||
html: `<div>${serialize(message, submissionData)}</div>`,
|
||||
replyTo,
|
||||
subject: replaceDoubleCurlys(subject, submissionData),
|
||||
html: `<div>${serialize(message, submissionData)}</div>`,
|
||||
to,
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -64,7 +65,7 @@ const sendEmail = async (beforeChangeData: any, formConfig: PluginConfig): Promi
|
||||
// const log = emailsToSend.map(({ html, ...rest }) => ({ ...rest }))
|
||||
|
||||
await Promise.all(
|
||||
emailsToSend.map(async email => {
|
||||
emailsToSend.map(async (email) => {
|
||||
const { to } = email
|
||||
try {
|
||||
const emailPromise = await payload.sendEmail(email)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { PluginConfig } from '../../types'
|
||||
|
||||
import createCharge from './hooks/createCharge'
|
||||
import sendEmail from './hooks/sendEmail'
|
||||
|
||||
@@ -8,51 +9,41 @@ import sendEmail from './hooks/sendEmail'
|
||||
export const generateSubmissionCollection = (formConfig: PluginConfig): CollectionConfig => {
|
||||
const newConfig: CollectionConfig = {
|
||||
...(formConfig?.formSubmissionOverrides || {}),
|
||||
slug: formConfig?.formSubmissionOverrides?.slug || 'form-submissions',
|
||||
access: {
|
||||
create: () => true,
|
||||
update: () => false,
|
||||
read: ({ req: { user } }) => !!user, // logged-in users,
|
||||
update: () => false,
|
||||
...(formConfig?.formSubmissionOverrides?.access || {}),
|
||||
},
|
||||
admin: {
|
||||
...(formConfig?.formSubmissionOverrides?.admin || {}),
|
||||
enableRichTextRelationship: false,
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
data => createCharge(data, formConfig),
|
||||
data => sendEmail(data, formConfig),
|
||||
...(formConfig?.formSubmissionOverrides?.hooks?.beforeChange || []),
|
||||
],
|
||||
...(formConfig?.formSubmissionOverrides?.hooks || {}),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'form',
|
||||
type: 'relationship',
|
||||
relationTo: formConfig?.formOverrides?.slug || 'forms',
|
||||
required: true,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
relationTo: formConfig?.formOverrides?.slug || 'forms',
|
||||
required: true,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'submissionData',
|
||||
type: 'array',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'field',
|
||||
type: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
validate: (value: unknown) => {
|
||||
// TODO:
|
||||
// create a validation function that dynamically
|
||||
@@ -72,9 +63,19 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'array',
|
||||
},
|
||||
...(formConfig?.formSubmissionOverrides?.fields || []),
|
||||
],
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
(data) => createCharge(data, formConfig),
|
||||
(data) => sendEmail(data, formConfig),
|
||||
...(formConfig?.formSubmissionOverrides?.hooks?.beforeChange || []),
|
||||
],
|
||||
...(formConfig?.formSubmissionOverrides?.hooks || {}),
|
||||
},
|
||||
slug: formConfig?.formSubmissionOverrides?.slug || 'form-submissions',
|
||||
}
|
||||
|
||||
const paymentFieldConfig = formConfig?.fields?.payment
|
||||
@@ -82,7 +83,6 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
if (paymentFieldConfig) {
|
||||
newConfig.fields.push({
|
||||
name: 'payment',
|
||||
type: 'group',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
@@ -99,10 +99,10 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
type: 'number',
|
||||
admin: {
|
||||
description: 'Amount in cents',
|
||||
},
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'paymentProcessor',
|
||||
@@ -110,8 +110,6 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
},
|
||||
{
|
||||
name: 'creditCard',
|
||||
label: 'Credit Card',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'token',
|
||||
@@ -129,8 +127,11 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
label: 'Credit Card',
|
||||
type: 'group',
|
||||
},
|
||||
],
|
||||
type: 'group',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import type { TextField } from 'payload/dist/fields/config/types'
|
||||
|
||||
import { Select, useForm } from 'payload/components/forms'
|
||||
import { TextField } from 'payload/dist/fields/config/types'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { SelectFieldOption } from '../../types'
|
||||
import type { SelectFieldOption } from '../../types'
|
||||
|
||||
export const DynamicFieldSelector: React.FC<TextField> = props => {
|
||||
export const DynamicFieldSelector: React.FC<TextField> = (props) => {
|
||||
const { fields, getDataByPath } = useForm()
|
||||
|
||||
const [options, setOptions] = useState<SelectFieldOption[]>([])
|
||||
@@ -18,7 +19,7 @@ export const DynamicFieldSelector: React.FC<TextField> = props => {
|
||||
if (fields) {
|
||||
const allNonPaymentFields = fields
|
||||
.map((block): SelectFieldOption | null => {
|
||||
const { name, label, blockType } = block
|
||||
const { name, blockType, label } = block
|
||||
|
||||
if (blockType !== 'payment') {
|
||||
return {
|
||||
@@ -29,7 +30,7 @@ export const DynamicFieldSelector: React.FC<TextField> = props => {
|
||||
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as SelectFieldOption[]
|
||||
.filter(Boolean)
|
||||
setOptions(allNonPaymentFields)
|
||||
}
|
||||
}, [fields, getDataByPath])
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
'use client'
|
||||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import type { Data } from 'payload/dist/admin/components/forms/Form/types'
|
||||
import type { Props as TextFieldType } from 'payload/dist/admin/components/forms/field-types/Text/types'
|
||||
|
||||
import { Text, useWatchForm } from 'payload/components/forms'
|
||||
import { useLocale } from 'payload/components/utilities'
|
||||
import { Props as TextFieldType } from 'payload/dist/admin/components/forms/field-types/Text/types'
|
||||
import { Data } from 'payload/dist/admin/components/forms/Form/types'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
type FieldWithID = {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export const DynamicPriceSelector: React.FC<TextFieldType> = props => {
|
||||
const { path, label } = props
|
||||
export const DynamicPriceSelector: React.FC<TextFieldType> = (props) => {
|
||||
const { label, path } = props
|
||||
|
||||
const { fields, getDataByPath, getData } = useWatchForm()
|
||||
const { fields, getData, getDataByPath } = useWatchForm()
|
||||
|
||||
const locale = useLocale()
|
||||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import type { Block, Field } from 'payload/types'
|
||||
|
||||
import type { FieldConfig, PaymentFieldConfig } from '../../types'
|
||||
|
||||
import { DynamicFieldSelector } from './DynamicFieldSelector'
|
||||
import { DynamicPriceSelector } from './DynamicPriceSelector'
|
||||
|
||||
const name: Field = {
|
||||
name: 'name',
|
||||
label: 'Name (lowercase, no special characters)',
|
||||
type: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
}
|
||||
|
||||
const label: Field = {
|
||||
name: 'label',
|
||||
label: 'Label',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
}
|
||||
|
||||
const required: Field = {
|
||||
@@ -31,14 +32,8 @@ const width: Field = {
|
||||
}
|
||||
|
||||
const Select: Block = {
|
||||
slug: 'select',
|
||||
labels: {
|
||||
singular: 'Select',
|
||||
plural: 'Select Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -53,9 +48,9 @@ const Select: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -65,63 +60,63 @@ const Select: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
label: 'Default Value',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
label: 'Select Attribute Options',
|
||||
type: 'array',
|
||||
labels: {
|
||||
singular: 'Option',
|
||||
plural: 'Options',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
label: 'Label',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Label',
|
||||
localized: true,
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Value',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
],
|
||||
label: 'Select Attribute Options',
|
||||
labels: {
|
||||
plural: 'Options',
|
||||
singular: 'Option',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
required,
|
||||
],
|
||||
labels: {
|
||||
plural: 'Select Fields',
|
||||
singular: 'Select',
|
||||
},
|
||||
slug: 'select',
|
||||
}
|
||||
|
||||
const Text: Block = {
|
||||
slug: 'text',
|
||||
labels: {
|
||||
singular: 'Text',
|
||||
plural: 'Text Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -136,9 +131,9 @@ const Text: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -148,28 +143,28 @@ const Text: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
label: 'Default Value',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
required,
|
||||
],
|
||||
labels: {
|
||||
plural: 'Text Fields',
|
||||
singular: 'Text',
|
||||
},
|
||||
slug: 'text',
|
||||
}
|
||||
|
||||
const TextArea: Block = {
|
||||
slug: 'textarea',
|
||||
labels: {
|
||||
singular: 'Text Area',
|
||||
plural: 'Text Area Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -184,9 +179,9 @@ const TextArea: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -196,28 +191,28 @@ const TextArea: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
required,
|
||||
],
|
||||
labels: {
|
||||
plural: 'Text Area Fields',
|
||||
singular: 'Text Area',
|
||||
},
|
||||
slug: 'textarea',
|
||||
}
|
||||
|
||||
const Number: Block = {
|
||||
slug: 'number',
|
||||
labels: {
|
||||
singular: 'Number',
|
||||
plural: 'Number Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -232,9 +227,9 @@ const Number: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -244,27 +239,27 @@ const Number: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
label: 'Default Value',
|
||||
type: 'number',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
required,
|
||||
],
|
||||
labels: {
|
||||
plural: 'Number Fields',
|
||||
singular: 'Number',
|
||||
},
|
||||
slug: 'number',
|
||||
}
|
||||
|
||||
const Email: Block = {
|
||||
slug: 'email',
|
||||
labels: {
|
||||
singular: 'Email',
|
||||
plural: 'Email Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -279,21 +274,21 @@ const Email: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
width,
|
||||
required,
|
||||
],
|
||||
labels: {
|
||||
plural: 'Email Fields',
|
||||
singular: 'Email',
|
||||
},
|
||||
slug: 'email',
|
||||
}
|
||||
|
||||
const State: Block = {
|
||||
slug: 'state',
|
||||
labels: {
|
||||
singular: 'State',
|
||||
plural: 'State Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -308,21 +303,21 @@ const State: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
width,
|
||||
required,
|
||||
],
|
||||
labels: {
|
||||
plural: 'State Fields',
|
||||
singular: 'State',
|
||||
},
|
||||
slug: 'state',
|
||||
}
|
||||
|
||||
const Country: Block = {
|
||||
slug: 'country',
|
||||
labels: {
|
||||
singular: 'Country',
|
||||
plural: 'Country Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -337,21 +332,21 @@ const Country: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
width,
|
||||
required,
|
||||
],
|
||||
labels: {
|
||||
plural: 'Country Fields',
|
||||
singular: 'Country',
|
||||
},
|
||||
slug: 'country',
|
||||
}
|
||||
|
||||
const Checkbox: Block = {
|
||||
slug: 'checkbox',
|
||||
labels: {
|
||||
singular: 'Checkbox',
|
||||
plural: 'Checkbox Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -366,9 +361,9 @@ const Checkbox: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -383,6 +378,7 @@ const Checkbox: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
@@ -390,29 +386,28 @@ const Checkbox: Block = {
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Checkbox Fields',
|
||||
singular: 'Checkbox',
|
||||
},
|
||||
slug: 'checkbox',
|
||||
}
|
||||
|
||||
const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
let paymentProcessorField = null
|
||||
if (fieldConfig?.paymentProcessor) {
|
||||
paymentProcessorField = {
|
||||
type: 'select',
|
||||
options: [],
|
||||
name: 'paymentProcessor',
|
||||
label: 'Payment Processor',
|
||||
options: [],
|
||||
type: 'select',
|
||||
...fieldConfig.paymentProcessor,
|
||||
}
|
||||
}
|
||||
|
||||
const fields = {
|
||||
slug: 'payment',
|
||||
labels: {
|
||||
singular: 'Payment',
|
||||
plural: 'Payment Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -427,9 +422,9 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -439,93 +434,87 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
},
|
||||
{
|
||||
name: 'basePrice',
|
||||
type: 'number',
|
||||
label: 'Base Price',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Base Price',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
paymentProcessorField,
|
||||
{
|
||||
name: 'priceConditions',
|
||||
labels: {
|
||||
singular: 'Price Condition',
|
||||
plural: 'Price Conditions',
|
||||
},
|
||||
type: 'array',
|
||||
label: 'Price Conditions',
|
||||
fields: [
|
||||
{
|
||||
name: 'fieldToUse',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicFieldSelector,
|
||||
},
|
||||
},
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'condition',
|
||||
label: 'Condition',
|
||||
type: 'select',
|
||||
defaultValue: 'hasValue',
|
||||
label: 'Condition',
|
||||
options: [
|
||||
{
|
||||
value: 'hasValue',
|
||||
label: 'Has Any Value',
|
||||
value: 'hasValue',
|
||||
},
|
||||
{
|
||||
value: 'equals',
|
||||
label: 'Equals',
|
||||
value: 'equals',
|
||||
},
|
||||
{
|
||||
value: 'notEquals',
|
||||
label: 'Does Not Equal',
|
||||
value: 'notEquals',
|
||||
},
|
||||
],
|
||||
type: 'select',
|
||||
},
|
||||
{
|
||||
name: 'valueForCondition',
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
admin: {
|
||||
condition: (_: any, { condition }: any) =>
|
||||
condition === 'equals' || condition === 'notEquals',
|
||||
},
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'operator',
|
||||
type: 'select',
|
||||
defaultValue: 'add',
|
||||
options: [
|
||||
{
|
||||
value: 'add',
|
||||
label: 'Add',
|
||||
value: 'add',
|
||||
},
|
||||
{
|
||||
value: 'subtract',
|
||||
label: 'Subtract',
|
||||
value: 'subtract',
|
||||
},
|
||||
{
|
||||
value: 'multiply',
|
||||
label: 'Multiply',
|
||||
value: 'multiply',
|
||||
},
|
||||
{
|
||||
value: 'divide',
|
||||
label: 'Divide',
|
||||
value: 'divide',
|
||||
},
|
||||
],
|
||||
type: 'select',
|
||||
},
|
||||
{
|
||||
name: 'valueType',
|
||||
label: 'Value Type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
width: '100%',
|
||||
},
|
||||
defaultValue: 'static',
|
||||
label: 'Value Type',
|
||||
options: [
|
||||
{
|
||||
label: 'Static Value',
|
||||
@@ -536,55 +525,67 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
value: 'valueOfField',
|
||||
},
|
||||
],
|
||||
type: 'radio',
|
||||
},
|
||||
{
|
||||
name: 'valueForOperator',
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicPriceSelector,
|
||||
},
|
||||
},
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
label: 'Price Conditions',
|
||||
labels: {
|
||||
plural: 'Price Conditions',
|
||||
singular: 'Price Condition',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
required,
|
||||
].filter(Boolean) as Field[],
|
||||
labels: {
|
||||
plural: 'Payment Fields',
|
||||
singular: 'Payment',
|
||||
},
|
||||
slug: 'payment',
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
const Message: Block = {
|
||||
slug: 'message',
|
||||
labels: {
|
||||
singular: 'Message',
|
||||
plural: 'Message Blocks',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'message',
|
||||
type: 'richText',
|
||||
localized: true,
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Message Blocks',
|
||||
singular: 'Message',
|
||||
},
|
||||
slug: 'message',
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
export const fields = {
|
||||
select: Select,
|
||||
checkbox: Checkbox,
|
||||
text: Text,
|
||||
textarea: TextArea,
|
||||
country: Country,
|
||||
email: Email,
|
||||
message: Message,
|
||||
number: Number,
|
||||
country: Country,
|
||||
state: State,
|
||||
payment: Payment,
|
||||
select: Select,
|
||||
state: State,
|
||||
text: Text,
|
||||
textarea: TextArea,
|
||||
} as {
|
||||
[key: string]: Block | ((fieldConfig?: boolean | FieldConfig) => Block)
|
||||
[key: string]: ((fieldConfig?: FieldConfig | boolean) => Block) | Block
|
||||
}
|
||||
|
||||
export default fields
|
||||
|
||||
@@ -1,44 +1,49 @@
|
||||
import merge from 'deepmerge'
|
||||
import type { Block, CollectionConfig, Field } from 'payload/types'
|
||||
|
||||
import merge from 'deepmerge'
|
||||
|
||||
import type { FieldConfig, PluginConfig } from '../../types'
|
||||
|
||||
import { fields } from './fields'
|
||||
|
||||
// all settings can be overridden by the config
|
||||
export const generateFormCollection = (formConfig: PluginConfig): CollectionConfig => {
|
||||
const redirect: Field = {
|
||||
name: 'redirect',
|
||||
type: 'group',
|
||||
admin: {
|
||||
hideGutter: true,
|
||||
condition: (_, siblingData) => siblingData?.confirmationType === 'redirect',
|
||||
hideGutter: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'url',
|
||||
label: 'URL to redirect to',
|
||||
type: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'group',
|
||||
}
|
||||
|
||||
if (formConfig.redirectRelationships) {
|
||||
redirect.fields.unshift({
|
||||
name: 'reference',
|
||||
label: 'Document to link to',
|
||||
type: 'relationship',
|
||||
relationTo: formConfig.redirectRelationships,
|
||||
required: true,
|
||||
maxDepth: 2,
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.type === 'reference',
|
||||
},
|
||||
label: 'Document to link to',
|
||||
maxDepth: 2,
|
||||
relationTo: formConfig.redirectRelationships,
|
||||
required: true,
|
||||
type: 'relationship',
|
||||
})
|
||||
|
||||
redirect.fields.unshift({
|
||||
name: 'type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
layout: 'horizontal',
|
||||
},
|
||||
defaultValue: 'reference',
|
||||
options: [
|
||||
{
|
||||
label: 'Internal link',
|
||||
@@ -49,10 +54,7 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
defaultValue: 'reference',
|
||||
admin: {
|
||||
layout: 'horizontal',
|
||||
},
|
||||
type: 'radio',
|
||||
})
|
||||
|
||||
if (redirect.fields[2].type !== 'row') redirect.fields[2].label = 'Custom URL'
|
||||
@@ -64,30 +66,28 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
|
||||
const config: CollectionConfig = {
|
||||
...(formConfig?.formOverrides || {}),
|
||||
slug: formConfig?.formOverrides?.slug || 'forms',
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
enableRichTextRelationship: false,
|
||||
...(formConfig?.formOverrides?.admin || {}),
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
...(formConfig?.formOverrides?.access || {}),
|
||||
},
|
||||
admin: {
|
||||
enableRichTextRelationship: false,
|
||||
useAsTitle: 'title',
|
||||
...(formConfig?.formOverrides?.admin || {}),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'fields',
|
||||
type: 'blocks',
|
||||
blocks: Object.entries(formConfig?.fields || {})
|
||||
.map(([fieldKey, fieldConfig]) => {
|
||||
// let the config enable/disable fields with either boolean values or objects
|
||||
if (fieldConfig !== false) {
|
||||
let block = fields[fieldKey]
|
||||
const block = fields[fieldKey]
|
||||
|
||||
if (block === undefined && typeof fieldConfig === 'object') {
|
||||
return fieldConfig
|
||||
@@ -109,20 +109,21 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as Block[],
|
||||
type: 'blocks',
|
||||
},
|
||||
{
|
||||
name: 'submitButtonLabel',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'confirmationType',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
description:
|
||||
'Choose whether to display an on-page message or redirect to a different page after they submit the form.',
|
||||
layout: 'horizontal',
|
||||
},
|
||||
defaultValue: 'message',
|
||||
options: [
|
||||
{
|
||||
label: 'Message',
|
||||
@@ -133,100 +134,101 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
value: 'redirect',
|
||||
},
|
||||
],
|
||||
defaultValue: 'message',
|
||||
type: 'radio',
|
||||
},
|
||||
{
|
||||
name: 'confirmationMessage',
|
||||
type: 'richText',
|
||||
localized: true,
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.confirmationType === 'message',
|
||||
},
|
||||
localized: true,
|
||||
required: true,
|
||||
type: 'richText',
|
||||
},
|
||||
redirect,
|
||||
{
|
||||
name: 'emails',
|
||||
type: 'array',
|
||||
admin: {
|
||||
description:
|
||||
"Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}.",
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'emailTo',
|
||||
label: 'Email To',
|
||||
admin: {
|
||||
width: '100%',
|
||||
placeholder: '"Email Sender" <sender@email.com>',
|
||||
width: '100%',
|
||||
},
|
||||
label: 'Email To',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'cc',
|
||||
label: 'CC',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'CC',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'bcc',
|
||||
label: 'BCC',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'BCC',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'replyTo',
|
||||
label: 'Reply To',
|
||||
admin: {
|
||||
width: '50%',
|
||||
placeholder: '"Reply To" <reply-to@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Reply To',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'emailFrom',
|
||||
label: 'Email From',
|
||||
admin: {
|
||||
width: '50%',
|
||||
placeholder: '"Email From" <email-from@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Email From',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'subject',
|
||||
label: 'Subject',
|
||||
defaultValue: "You've received a new message.",
|
||||
required: true,
|
||||
label: 'Subject',
|
||||
localized: true,
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'richText',
|
||||
name: 'message',
|
||||
label: 'Message',
|
||||
localized: true,
|
||||
admin: {
|
||||
description: 'Enter the message that should be sent in this email.',
|
||||
},
|
||||
label: 'Message',
|
||||
localized: true,
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
type: 'array',
|
||||
},
|
||||
...(formConfig?.formOverrides?.fields || []),
|
||||
],
|
||||
slug: formConfig?.formOverrides?.slug || 'forms',
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { Config } from 'payload/config'
|
||||
|
||||
import { generateFormCollection } from './collections/Forms'
|
||||
import { generateSubmissionCollection } from './collections/FormSubmissions'
|
||||
import type { PluginConfig } from './types'
|
||||
|
||||
import { generateSubmissionCollection } from './collections/FormSubmissions'
|
||||
import { generateFormCollection } from './collections/Forms'
|
||||
|
||||
export { fields } from './collections/Forms/fields'
|
||||
export { getPaymentTotal } from './utilities/getPaymentTotal'
|
||||
|
||||
@@ -13,16 +14,16 @@ const FormBuilder =
|
||||
const formConfig: PluginConfig = {
|
||||
...incomingFormConfig,
|
||||
fields: {
|
||||
checkbox: true,
|
||||
country: true,
|
||||
email: true,
|
||||
message: true,
|
||||
number: true,
|
||||
payment: false,
|
||||
select: true,
|
||||
state: true,
|
||||
text: true,
|
||||
textarea: true,
|
||||
select: true,
|
||||
email: true,
|
||||
state: true,
|
||||
country: true,
|
||||
number: true,
|
||||
checkbox: true,
|
||||
message: true,
|
||||
payment: false,
|
||||
...incomingFormConfig.fields,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export function isValidBlockConfig(blockConfig: BlockConfig | string): blockConf
|
||||
}
|
||||
|
||||
export interface FieldValues {
|
||||
[key: string]: string | number | boolean | null | undefined
|
||||
[key: string]: boolean | null | number | string | undefined
|
||||
}
|
||||
|
||||
export type PaymentFieldConfig = Partial<Field> & {
|
||||
@@ -24,49 +24,49 @@ export type PaymentFieldConfig = Partial<Field> & {
|
||||
export type FieldConfig = Partial<Field> | PaymentFieldConfig
|
||||
|
||||
export interface FieldsConfig {
|
||||
select?: boolean | FieldConfig
|
||||
text?: boolean | FieldConfig
|
||||
textarea?: boolean | FieldConfig
|
||||
email?: boolean | FieldConfig
|
||||
state?: boolean | FieldConfig
|
||||
country?: boolean | FieldConfig
|
||||
checkbox?: boolean | FieldConfig
|
||||
number?: boolean | FieldConfig
|
||||
message?: boolean | FieldConfig
|
||||
payment?: boolean | FieldConfig
|
||||
[key: string]: boolean | FieldConfig | undefined
|
||||
[key: string]: FieldConfig | boolean | undefined
|
||||
checkbox?: FieldConfig | boolean
|
||||
country?: FieldConfig | boolean
|
||||
email?: FieldConfig | boolean
|
||||
message?: FieldConfig | boolean
|
||||
number?: FieldConfig | boolean
|
||||
payment?: FieldConfig | boolean
|
||||
select?: FieldConfig | boolean
|
||||
state?: FieldConfig | boolean
|
||||
text?: FieldConfig | boolean
|
||||
textarea?: FieldConfig | boolean
|
||||
}
|
||||
|
||||
export type BeforeEmail = (emails: FormattedEmail[]) => FormattedEmail[] | Promise<FormattedEmail[]>
|
||||
export type HandlePayment = (data: any) => void
|
||||
|
||||
export interface PluginConfig {
|
||||
fields?: FieldsConfig
|
||||
formSubmissionOverrides?: Partial<CollectionConfig>
|
||||
formOverrides?: Partial<CollectionConfig>
|
||||
beforeEmail?: BeforeEmail
|
||||
fields?: FieldsConfig
|
||||
formOverrides?: Partial<CollectionConfig>
|
||||
formSubmissionOverrides?: Partial<CollectionConfig>
|
||||
handlePayment?: HandlePayment
|
||||
redirectRelationships?: string[]
|
||||
}
|
||||
|
||||
export interface TextField {
|
||||
blockType: 'text'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
blockType: 'text'
|
||||
defaultValue?: string
|
||||
label?: string
|
||||
name: string
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface TextAreaField {
|
||||
blockType: 'textarea'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
blockType: 'textarea'
|
||||
defaultValue?: string
|
||||
label?: string
|
||||
name: string
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface SelectFieldOption {
|
||||
@@ -75,133 +75,133 @@ export interface SelectFieldOption {
|
||||
}
|
||||
|
||||
export interface SelectField {
|
||||
blockType: 'select'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
blockType: 'select'
|
||||
defaultValue?: string
|
||||
required?: boolean
|
||||
label?: string
|
||||
name: string
|
||||
options: SelectFieldOption[]
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface PriceCondition {
|
||||
condition: 'equals' | 'hasValue' | 'notEquals'
|
||||
fieldToUse: string
|
||||
condition: 'equals' | 'notEquals' | 'hasValue'
|
||||
operator: 'add' | 'divide' | 'multiply' | 'subtract'
|
||||
valueForCondition: string
|
||||
operator: 'add' | 'subtract' | 'multiply' | 'divide'
|
||||
valueForOperator: number | string // TODO: make this a number, see ./collections/Forms/DynamicPriceSelector.tsx
|
||||
valueType: 'static' | 'valueOfField'
|
||||
valueForOperator: string | number // TODO: make this a number, see ./collections/Forms/DynamicPriceSelector.tsx
|
||||
}
|
||||
|
||||
export interface PaymentField {
|
||||
blockType: 'payment'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
defaultValue?: string
|
||||
required?: boolean
|
||||
paymentProcessor: string
|
||||
basePrice: number
|
||||
blockName?: string
|
||||
blockType: 'payment'
|
||||
defaultValue?: string
|
||||
label?: string
|
||||
name: string
|
||||
paymentProcessor: string
|
||||
priceConditions: PriceCondition[]
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface EmailField {
|
||||
blockType: 'email'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
blockType: 'email'
|
||||
defaultValue?: string
|
||||
label?: string
|
||||
name: string
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface StateField {
|
||||
blockType: 'state'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
blockType: 'state'
|
||||
defaultValue?: string
|
||||
label?: string
|
||||
name: string
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface CountryField {
|
||||
blockType: 'country'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
blockType: 'country'
|
||||
defaultValue?: string
|
||||
label?: string
|
||||
name: string
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface CheckboxField {
|
||||
blockType: 'checkbox'
|
||||
blockName?: string
|
||||
width?: number
|
||||
name: string
|
||||
label?: string
|
||||
blockType: 'checkbox'
|
||||
defaultValue?: boolean
|
||||
label?: string
|
||||
name: string
|
||||
required?: boolean
|
||||
width?: number
|
||||
}
|
||||
|
||||
export interface MessageField {
|
||||
blockType: 'message'
|
||||
blockName?: string
|
||||
blockType: 'message'
|
||||
message: unknown
|
||||
}
|
||||
|
||||
export type FormFieldBlock =
|
||||
| TextField
|
||||
| TextAreaField
|
||||
| SelectField
|
||||
| EmailField
|
||||
| StateField
|
||||
| CountryField
|
||||
| CheckboxField
|
||||
| CountryField
|
||||
| EmailField
|
||||
| MessageField
|
||||
| PaymentField
|
||||
| SelectField
|
||||
| StateField
|
||||
| TextAreaField
|
||||
| TextField
|
||||
|
||||
export interface Email {
|
||||
emailTo: string
|
||||
emailFrom: string
|
||||
cc?: string
|
||||
bcc?: string
|
||||
cc?: string
|
||||
emailFrom: string
|
||||
emailTo: string
|
||||
message?: any // TODO: configure rich text type
|
||||
replyTo?: string
|
||||
subject: string
|
||||
message?: any // TODO: configure rich text type
|
||||
}
|
||||
|
||||
export interface FormattedEmail {
|
||||
to: string
|
||||
cc?: string
|
||||
bcc?: string
|
||||
cc?: string
|
||||
from: string
|
||||
subject: string
|
||||
html: string
|
||||
replyTo: string
|
||||
subject: string
|
||||
to: string
|
||||
}
|
||||
|
||||
export interface Redirect {
|
||||
type: 'reference' | 'custom'
|
||||
reference?: {
|
||||
relationTo: string
|
||||
value: string | unknown
|
||||
}
|
||||
type: 'custom' | 'reference'
|
||||
url: string
|
||||
}
|
||||
|
||||
export interface Form {
|
||||
id: string
|
||||
title: string
|
||||
fields: FormFieldBlock[]
|
||||
submitButtonLabel?: string
|
||||
confirmationType: 'message' | 'redirect'
|
||||
confirmationMessage?: any // TODO: configure rich text type
|
||||
redirect?: Redirect
|
||||
confirmationType: 'message' | 'redirect'
|
||||
emails: Email[]
|
||||
fields: FormFieldBlock[]
|
||||
id: string
|
||||
redirect?: Redirect
|
||||
submitButtonLabel?: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface SubmissionValue {
|
||||
@@ -210,6 +210,6 @@ export interface SubmissionValue {
|
||||
}
|
||||
|
||||
export interface FormSubmission {
|
||||
form: string | Form
|
||||
form: Form | string
|
||||
submissionData: SubmissionValue[]
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ export const getPaymentTotal = (
|
||||
fieldValues: FieldValues
|
||||
},
|
||||
): number => {
|
||||
const { basePrice = 0, priceConditions, fieldValues } = args
|
||||
const { basePrice = 0, fieldValues, priceConditions } = args
|
||||
|
||||
let total = basePrice
|
||||
|
||||
if (Array.isArray(priceConditions) && priceConditions.length > 0) {
|
||||
priceConditions.forEach((priceCondition: PriceCondition) => {
|
||||
const { condition, valueForCondition, fieldToUse, operator, valueType, valueForOperator } =
|
||||
const { condition, fieldToUse, operator, valueForCondition, valueForOperator, valueType } =
|
||||
priceCondition
|
||||
|
||||
const valueOfField = fieldValues?.[fieldToUse]
|
||||
|
||||
@@ -6,7 +6,7 @@ interface EmailVariable {
|
||||
type EmailVariables = EmailVariable[]
|
||||
|
||||
export const replaceDoubleCurlys = (str: string, variables?: EmailVariables): string => {
|
||||
const regex = /{{(.+?)}}/g
|
||||
const regex = /\{\{(.+?)\}\}/g
|
||||
if (str && variables) {
|
||||
return str.replace(regex, (_, variable) => {
|
||||
const foundVariable = variables.find(({ field: fieldName }) => variable === fieldName)
|
||||
|
||||
@@ -5,11 +5,11 @@ import { replaceDoubleCurlys } from './replaceDoubleCurlys'
|
||||
|
||||
interface Node {
|
||||
bold?: boolean
|
||||
children?: Node[]
|
||||
code?: boolean
|
||||
italic?: boolean
|
||||
type?: string
|
||||
url?: string
|
||||
children?: Node[]
|
||||
}
|
||||
|
||||
export const serialize = (children?: Node[], submissionData?: any): string | undefined =>
|
||||
|
||||
Reference in New Issue
Block a user