feat: builds demo
This commit is contained in:
10
packages/plugin-form-builder/.editorconfig
Normal file
10
packages/plugin-form-builder/.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = null
|
||||
3
packages/plugin-form-builder/.gitignore
vendored
3
packages/plugin-form-builder/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
node_modules
|
||||
node_modules
|
||||
.env
|
||||
|
||||
2
packages/plugin-form-builder/demo/.env
Normal file
2
packages/plugin-form-builder/demo/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
MONGODB_URI=mongodb://localhost/payload-form-builder-demo
|
||||
PAYLOAD_SECRET=kajsnfkjhabndsfgseaniluanbsrkdgbhyasfg
|
||||
4
packages/plugin-form-builder/demo/nodemon.json
Normal file
4
packages/plugin-form-builder/demo/nodemon.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"ext": "ts",
|
||||
"exec": "ts-node src/server.ts"
|
||||
}
|
||||
27
packages/plugin-form-builder/demo/package.json
Normal file
27
packages/plugin-form-builder/demo/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "payload-starter-typescript",
|
||||
"description": "Blank template - no collections",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/server.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"build:server": "tsc",
|
||||
"build": "yarn build:payload && yarn build:server",
|
||||
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
|
||||
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types"
|
||||
},
|
||||
"dependencies": {
|
||||
"payload": "^0.14.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.9",
|
||||
"cross-env": "^7.0.3",
|
||||
"nodemon": "^2.0.6",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// const payload = require('payload');
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
labels: {
|
||||
singular: 'Page',
|
||||
plural: 'Pages',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
// Email added by default
|
||||
// Add more fields as needed
|
||||
],
|
||||
};
|
||||
42
packages/plugin-form-builder/demo/src/payload.config.ts
Normal file
42
packages/plugin-form-builder/demo/src/payload.config.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { buildConfig } from 'payload/config';
|
||||
import path from 'path';
|
||||
import formBuilderPlugin from '../../src';
|
||||
import { Users } from './collections/Users';
|
||||
import { Pages } from './collections/Pages';
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
},
|
||||
collections: [
|
||||
Users,
|
||||
Pages
|
||||
],
|
||||
plugins: [
|
||||
formBuilderPlugin({
|
||||
redirectRelationships: [
|
||||
'pages'
|
||||
],
|
||||
fields: {
|
||||
payment: true,
|
||||
// payment: {
|
||||
// paymentProcessor: {
|
||||
// options: [
|
||||
// {
|
||||
// label: 'Stripe',
|
||||
// value: 'stripe'
|
||||
// },
|
||||
// ],
|
||||
// defaultValue: 'stripe',
|
||||
// },
|
||||
// },
|
||||
// handlePayment: handleFormPayments,
|
||||
// beforeEmail: prepareFormEmails,
|
||||
},
|
||||
}),
|
||||
],
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts')
|
||||
},
|
||||
});
|
||||
24
packages/plugin-form-builder/demo/src/server.ts
Normal file
24
packages/plugin-form-builder/demo/src/server.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import express from 'express';
|
||||
import payload from 'payload';
|
||||
|
||||
require('dotenv').config();
|
||||
const app = express();
|
||||
|
||||
// Redirect root to Admin panel
|
||||
app.get('/', (_, res) => {
|
||||
res.redirect('/admin');
|
||||
});
|
||||
|
||||
// Initialize Payload
|
||||
payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
||||
},
|
||||
});
|
||||
|
||||
// Add your own express routes here
|
||||
|
||||
app.listen(3000);
|
||||
19
packages/plugin-form-builder/demo/tsconfig.json
Normal file
19
packages/plugin-form-builder/demo/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "../",
|
||||
"jsx": "react",
|
||||
},
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
}
|
||||
11026
packages/plugin-form-builder/demo/yarn.lock
Normal file
11026
packages/plugin-form-builder/demo/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,14 @@ import { FormConfig } from '../../types';
|
||||
import deepMerge from '../../utilities/deepMerge';
|
||||
import sendEmail from './hooks/sendEmail';
|
||||
import createCharge from './hooks/createCharge';
|
||||
import loggedInUsers from '../../../../collections/User/access/loggedInUsers';
|
||||
|
||||
// all settings can be overridden by the config
|
||||
export const generateSubmissionCollection = (formConfig: FormConfig): CollectionConfig => deepMerge({
|
||||
slug: formConfig?.formsOverrides?.slug || 'formSubmissions',
|
||||
slug: formConfig?.formSubmissionOverrides?.slug || 'formSubmissions',
|
||||
access: {
|
||||
create: () => true,
|
||||
update: () => false,
|
||||
read: loggedInUsers
|
||||
read: ({ req: { user } }) => !!user // logged-in users
|
||||
},
|
||||
admin: {
|
||||
enableRichTextRelationship: false
|
||||
@@ -127,4 +127,4 @@ export const generateSubmissionCollection = (formConfig: FormConfig): Collection
|
||||
]
|
||||
}
|
||||
],
|
||||
}, formConfig.formSubmissionsOverrides || {});
|
||||
}, formConfig.formSubmissionOverrides || {});
|
||||
|
||||
@@ -340,177 +340,183 @@ const Checkbox: Block = {
|
||||
],
|
||||
};
|
||||
|
||||
const Payment = (fieldConfig: FieldConfig): Block => ({
|
||||
slug: 'payment',
|
||||
labels: {
|
||||
singular: 'Payment',
|
||||
plural: 'Payment Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...label,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
{
|
||||
...width,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
],
|
||||
const Payment = (fieldConfig: FieldConfig): Block => {
|
||||
|
||||
let paymentProcessorField = null;
|
||||
if (fieldConfig?.paymentProcessor) {
|
||||
paymentProcessorField = {
|
||||
type: 'select',
|
||||
options: [],
|
||||
name: 'paymentProcessor',
|
||||
label: 'Payment Processor',
|
||||
...fieldConfig.paymentProcessor,
|
||||
}
|
||||
}
|
||||
|
||||
return ({
|
||||
slug: 'payment',
|
||||
labels: {
|
||||
singular: 'Payment',
|
||||
plural: 'Payment Fields',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'paymentProcessor',
|
||||
type: 'select',
|
||||
options: [],
|
||||
admin: {
|
||||
width: '100%',
|
||||
},
|
||||
...fieldConfig?.paymentProcessor || {}
|
||||
},
|
||||
{
|
||||
name: 'priceType',
|
||||
label: 'Price Type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
width: '100%',
|
||||
},
|
||||
defaultValue: 'static',
|
||||
options: [
|
||||
{
|
||||
label: 'Static Price',
|
||||
value: 'static'
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...label,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
{
|
||||
label: 'Dynamic Price',
|
||||
value: 'dynamic'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'staticPrice',
|
||||
type: 'number',
|
||||
label: 'Price',
|
||||
admin: {
|
||||
condition: (_, { priceType }) => priceType === 'static'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'dynamicPrice',
|
||||
labels: {
|
||||
singular: 'Condition',
|
||||
plural: 'Conditions',
|
||||
{
|
||||
...width,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
type: 'array',
|
||||
label: 'Price',
|
||||
admin: {
|
||||
condition: (_, { priceType }) => priceType === 'dynamic'
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'fieldToUse',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicFieldSelector,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
paymentProcessorField,
|
||||
{
|
||||
name: 'priceType',
|
||||
label: 'Price Type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
width: '100%',
|
||||
},
|
||||
defaultValue: 'static',
|
||||
options: [
|
||||
{
|
||||
label: 'Static Price',
|
||||
value: 'static'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'condition',
|
||||
label: 'Condition',
|
||||
type: 'select',
|
||||
defaultValue: 'hasValue',
|
||||
options: [
|
||||
{
|
||||
value: 'hasValue',
|
||||
label: 'Has Any Value'
|
||||
},
|
||||
{
|
||||
value: 'equals',
|
||||
label: 'Equals'
|
||||
},
|
||||
{
|
||||
value: 'notEquals',
|
||||
label: 'Does Not Equal'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'valueForCondition',
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
admin: {
|
||||
condition: (_, { condition }) => condition === 'equals' || condition === 'notEquals'
|
||||
{
|
||||
label: 'Dynamic Price',
|
||||
value: 'dynamic'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'staticPrice',
|
||||
type: 'number',
|
||||
label: 'Price',
|
||||
admin: {
|
||||
condition: (_, { priceType }) => priceType === 'static'
|
||||
},
|
||||
{
|
||||
name: 'operator',
|
||||
type: 'select',
|
||||
defaultValue: 'add',
|
||||
options: [
|
||||
{
|
||||
value: 'add',
|
||||
label: 'Add'
|
||||
},
|
||||
{
|
||||
value: 'subtract',
|
||||
label: 'Subtract'
|
||||
},
|
||||
{
|
||||
value: 'multiply',
|
||||
label: 'Multiply'
|
||||
},
|
||||
{
|
||||
value: 'divide',
|
||||
label: 'Divide'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'dynamicPrice',
|
||||
labels: {
|
||||
singular: 'Condition',
|
||||
plural: 'Conditions',
|
||||
},
|
||||
{
|
||||
name: 'valueType',
|
||||
label: 'Value Type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
width: '100%',
|
||||
},
|
||||
defaultValue: 'static',
|
||||
options: [
|
||||
{
|
||||
label: 'Static Value',
|
||||
value: 'static'
|
||||
},
|
||||
{
|
||||
label: 'Dynamic Value',
|
||||
value: 'dynamic'
|
||||
}
|
||||
]
|
||||
type: 'array',
|
||||
label: 'Price',
|
||||
admin: {
|
||||
condition: (_, { priceType }) => priceType === 'dynamic'
|
||||
},
|
||||
{
|
||||
name: 'valueForOperator',
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicPriceSelector,
|
||||
fields: [
|
||||
{
|
||||
name: 'fieldToUse',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicFieldSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
required,
|
||||
],
|
||||
});
|
||||
{
|
||||
name: 'condition',
|
||||
label: 'Condition',
|
||||
type: 'select',
|
||||
defaultValue: 'hasValue',
|
||||
options: [
|
||||
{
|
||||
value: 'hasValue',
|
||||
label: 'Has Any Value'
|
||||
},
|
||||
{
|
||||
value: 'equals',
|
||||
label: 'Equals'
|
||||
},
|
||||
{
|
||||
value: 'notEquals',
|
||||
label: 'Does Not Equal'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'valueForCondition',
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
admin: {
|
||||
condition: (_, { condition }) => condition === 'equals' || condition === 'notEquals'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'operator',
|
||||
type: 'select',
|
||||
defaultValue: 'add',
|
||||
options: [
|
||||
{
|
||||
value: 'add',
|
||||
label: 'Add'
|
||||
},
|
||||
{
|
||||
value: 'subtract',
|
||||
label: 'Subtract'
|
||||
},
|
||||
{
|
||||
value: 'multiply',
|
||||
label: 'Multiply'
|
||||
},
|
||||
{
|
||||
value: 'divide',
|
||||
label: 'Divide'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'valueType',
|
||||
label: 'Value Type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
width: '100%',
|
||||
},
|
||||
defaultValue: 'static',
|
||||
options: [
|
||||
{
|
||||
label: 'Static Value',
|
||||
value: 'static'
|
||||
},
|
||||
{
|
||||
label: 'Dynamic Value',
|
||||
value: 'dynamic'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'valueForOperator',
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicPriceSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
].filter(Boolean),
|
||||
},
|
||||
required,
|
||||
]
|
||||
})
|
||||
};
|
||||
|
||||
const Message: Block = {
|
||||
slug: 'message',
|
||||
|
||||
@@ -3,8 +3,9 @@ import { FormConfig } from '../../types';
|
||||
import fields from './fields';
|
||||
import deepMerge from '../../utilities/deepMerge';
|
||||
|
||||
// all settings can be overridden by the config
|
||||
export const generateFormCollection = (formConfig: FormConfig): CollectionConfig => deepMerge({
|
||||
slug: formConfig?.formsOverrides?.slug || 'forms',
|
||||
slug: formConfig?.formOverrides?.slug || 'forms',
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
enableRichTextRelationship: false,
|
||||
@@ -95,7 +96,7 @@ export const generateFormCollection = (formConfig: FormConfig): CollectionConfig
|
||||
name: 'reference',
|
||||
label: 'Document to link to',
|
||||
type: 'relationship',
|
||||
relationTo: ['pages', 'posts', 'housing'],
|
||||
relationTo: formConfig.redirectRelationships || [],
|
||||
required: true,
|
||||
maxDepth: 2,
|
||||
admin: {
|
||||
@@ -182,4 +183,4 @@ export const generateFormCollection = (formConfig: FormConfig): CollectionConfig
|
||||
],
|
||||
},
|
||||
],
|
||||
}, formConfig.formsOverrides || {});
|
||||
}, formConfig.formOverrides || {});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { RichText } from '@trbl/hope-types';
|
||||
import { Block, CollectionConfig, Field } from 'payload/types';
|
||||
|
||||
export type BlockConfig = {
|
||||
@@ -34,10 +33,11 @@ export type HandlePayment = (data) => void;
|
||||
|
||||
export type FormConfig = {
|
||||
fields?: FieldsConfig
|
||||
formSubmissionsOverrides?: CollectionConfig
|
||||
formsOverrides?: CollectionConfig
|
||||
formSubmissionOverrides?: Partial<CollectionConfig>
|
||||
formOverrides?: Partial<CollectionConfig>
|
||||
beforeEmail?: BeforeEmail
|
||||
handlePayment?: HandlePayment
|
||||
redirectRelationships?: string[]
|
||||
}
|
||||
|
||||
export type TextField = {
|
||||
@@ -120,7 +120,7 @@ export type Email = {
|
||||
bcc?: string
|
||||
replyTo?: string
|
||||
subject: string
|
||||
message?: RichText
|
||||
message?: any // TODO: configure rich text type
|
||||
}
|
||||
|
||||
export type FormattedEmail = {
|
||||
@@ -144,7 +144,7 @@ export type Form = {
|
||||
fields: FormFieldBlock[]
|
||||
submitButtonLabel?: string
|
||||
confirmationType: 'message' | 'redirect'
|
||||
confirmationMessage?: RichText
|
||||
confirmationMessage?: any // TODO: configure rich text type
|
||||
redirect?: Redirect
|
||||
emails: Email[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user