chore: adds README

This commit is contained in:
Jacob Fletcher
2022-02-18 18:14:32 -05:00
parent f0cc05ab91
commit fbc216e667
13 changed files with 330 additions and 197 deletions

BIN
packages/plugin-form-builder/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,217 @@
# Payload Form Builder Plugin
[![NPM](https://img.shields.io/npm/v/payload-plugin-form-builder)](https://www.npmjs.com/package/payload-plugin-form-builder)
A plugin for [Payload CMS](https://github.com/payloadcms/payload) to easily allow your admin editors to build and manage forms from the admin panel.
Core features:
- Creates a `forms` collection where you can:
- Build dynamic forms with any number of fields
- Add payment fields that can handle dynamic prices
- Build completely custom and dynamic emails
- Creates a `formSubmissions` collection that:
- Validates and saves the form data submitted by your frontend
- Sends emails (if applicable)
- Handles payment processing (if applicable)
## Installation
```bash
yarn add payload-plugin-form-builder
# OR
npm i payload-plugin-form-builder
```
## Basic Usage
In the `plugins` array of your [Payload config](https://payloadcms.com/docs/configuration/overview), call the plugin with [options](#options):
```js
import { buildConfig } from 'payload/config';
import formBuilder from 'payload-plugin-form-builder';
const config = buildConfig({
collections: [
{
slug: 'pages',
fields: []
}
],
plugins: [
formBuilder()
]
});
export default config;
```
### Options
- `fields`
An object of field types to allow your admin editors to build forms with. Pass either a boolean value or a partial [Payload Block](https://payloadcms.com/docs/fields/blocks#block-configs) to override default settings. See [Fields](#fields) for more details.
```
fields: {
text: true,
select: true,
email: true,
state: true,
country: true,
checkbox: true,
number: true,
message: true,
payment: false
}
```
You can also provide your own custom field definitions by passing a new [Payload Block](https://payloadcms.com/docs/fields/blocks#block-configs) object into `fields`.
- `redirectRelationships`
An array of collection slugs that, when enabled, are populated as options in form redirect fields.
```
redirectRelationships: ['pages']
```
- `handlePayment`
A [beforeChange]([beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)) hook that is called upon form submissions. You can integrate into any third-party payment processing API here. There is a `getPaymentTotal` function that will calculate the total cost after all conditions have been applied.
```
import { getPaymentTotal } from 'payload-plugin-form-builder';
...
handlePayment: async (beforeChangeData) => {
// asynchronously process payments here
const price = getPaymentTotal({
...paymentField, // you can get this from somewhere in the beforeChangeData
fieldValues: {}, // an object of kay:value pairs for every field in the form
});
}
```
- `beforeEmail`
A [beforeChange]([beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
```
beforeEmails: (emailsToSend) => {
// modify the emails in any way before they are sent
return emails.map((email) => ({
...email,
html: email.html // transform the html in any way you'd like (maybe wrap it in an html template?)
}))
};
```
- `formOverrides`
Override anything on the form collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections).
```
formSubmissionOverrides: {
slug: 'contact-forms'
}
```
- `formSubmissionOverrides`
By default, this plugin relies on [Payload access control](https://payloadcms.com/docs/access-control/collections) to restrict the `update` and `read` operations. This is because anyone should be able to create a form submission, even from a public-facing website - but no one should be able to update a submission one it has been created, or read a submission unless they have permission.
You can verride access control and anything else on the form submission collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections).
```
formSubmissionOverrides: {
slug: 'leads'
}
```
## Fields
Each field is a [Payload Block](https://payloadcms.com/docs/fields/blocks) with the following fields:
- Text
- `name`: string
- `label`: string
- `defaultValue`: string
- `width`: string
- `required`: checkbox
- Select
- `name`: string
- `label`: string
- `defaultValue`: string
- `width`: string
- `options`: array
- `required`: checkbox
- Email
- `name`: string
- `label`: string
- `defaultValue`: string
- `width`: string
- `required`: checkbox
- State
- `name`: string
- `label`: string
- `defaultValue`: string
- `width`: string
- `required`: checkbox
- Country
- `name`: string
- `label`: string
- `defaultValue`: string
- `width`: string
- `required`: checkbox
- Checkbox
- `name`: string
- `label`: string
- `defaultValue`: checkbox
- `width`: string
- `required`: checkbox
- Number
- `name`: string
- `label`: string
- `defaultValue`: number
- `width`: string
- `required`: checkbox
- Message
- `message`: richText
- Payment
- `name`: string
- `label`: string
- `defaultValue`: number
- `width`: string
- `required`: checkbox
- `priceConditions`: array
- `fieldToUse`: relationship, dynamically populated based on the fields in your form
- `condition`: string - `equals`, `notEquals` | `hasValue`
- `valueForOperator`: string - only if `condition` is `equals` or `notEquals`
- `operator`: string - `add`, `subtract`, `multiply`, `divide`
- `valueType`: string - `static`, `valueOfField`
- `value`: string - only if `valueType` is `static`
## Email
This plugin relies on the [email configuration](https://payloadcms.com/docs/email/overview) defined in your `payload.init()`. It will read from your config and attempt to send your emails using the credentials provided.
## TypeScript
All types can be directly imported:
```js
import {
FormConfig,
Form,
FormSubmission,
FieldsConfig,
BeforePayment,
HandlePayment
} from 'payload-plugin-form-builder/dist/types';
```
## Screenshots
![screenshot 1](https://github.com/trouble/payload-plugin-form-builder/blob/main/images/screenshot-1.jpg?raw=true)
![screenshot 2](https://github.com/trouble/payload-plugin-form-builder/blob/main/images/screenshot-2.jpg?raw=true)
![screenshot 3](https://github.com/trouble/payload-plugin-form-builder/blob/main/images/screenshot-3.jpg?raw=true)
![screenshot 4](https://github.com/trouble/payload-plugin-form-builder/blob/main/images/screenshot-4.jpg?raw=true)
![screenshot 5](https://github.com/trouble/payload-plugin-form-builder/blob/main/images/screenshot-5.jpg?raw=true)
![screenshot 6](https://github.com/trouble/payload-plugin-form-builder/blob/main/images/screenshot-6.jpg?raw=true)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,96 +0,0 @@
# Form Builder
This plugin adds full, headless, form-building functionality to Payload CMS.
### Example Installation
```js
import { buildConfig } from 'payload/config';
import payloadFormBuilder from '@payloadcms/plugin-form-builder';
export default buildConfig({
serverURL: 'https://localhost:3000',
plugins: [
payloadFormBuilder({
fields: [
'text',
'select',
'checkbox',
// Custom field example
{
block: {
slug: 'CustomField',
fields: [
{
name: 'name',
type: 'text',
},
{
name: 'label',
type: 'text',
},
],
},
validate: (value) => {
if (value === 'what we want it to be') return true;
// Otherwise, this field data is invalid.
return 'This field is incorrect'.
}
},
],
}),
],
})
```
## How it works
This plugin injects two new collections:
### Forms
Forms provide a form-building mechanism where your admins can self-build forms to suit their use cases. The Forms collection provides a `blocks` field type that contains a variety of built-in fields, including:
- Typical text field
- Select field that offers specific options
- Email that validates an incoming email address
- Checkbox
- State (US-based list of states)
- Country (Common country list)
### Form Submissions
Form Submissions validate incoming form submissions and track them over time right in Payload.
## Options
| Option | Description |
| -------------------- | ----------- |
| **`fields`** | An array of field slugs or custom field definitions which define the fields you allow your admin editors to build forms with. [More](#specifying-field-types) |
| **`formsOverrides`** | Provide any overrides that you need to the `Forms` collection config. They will be merged in to the injected `Forms` collection. |
| **`formSubmissionsOverrides`** | Provide any overrides that you need to the `Form Submissions` collection config. They will be merged in to the injected `Form Submissions` collection. |
*\* An asterisk denotes that a property is required.*
#### Specifying field types
You can specify which fields you want to use, as well as inject your own custom fields.
##### Custom fields
You can also provide your own custom field definitions. A custom field is simply a [Payload block definition](https://payloadcms.com/docs/fields/blocks#block-configs) - nothing more. To define a custom field, pass an object into the `fields` option with the following properties:
| Property | Description |
| ---------------- | ----------- |
| **`block`** * | A Payload block config that defines the fields that will be presented to your admin user if they choose to use this field. |
| **`validate`** | An optional validation function to use when data for this field is received. Return either `true` or a `string` equal to an appropriate error message. |
*\* An asterisk denotes that a property is required.*
## Default Access Control
By default, this plugin relies on the default Payload access control function for all collection access control, except the `Form Submissions` `create` and `update` operations. This is because anyone should be able to create a form submission, even from a public-facing website - but no one should be able to update a submission one it has been created.
If this is not suitable for your use case, you can override access control of both `Forms` and `Form Submissions` by passing in overrides for either collection config.

View File

@@ -1,5 +1,5 @@
import { Block, Field } from 'payload/types';
import { FieldConfig, TextField } from '../../types';
import { FieldConfig, PaymentFieldConfig, TextField } from '../../types';
import { DynamicFieldSelector } from './DynamicFieldSelector';
import { DynamicPriceSelector } from './DynamicPriceSelector';
@@ -340,7 +340,7 @@ const Checkbox: Block = {
],
};
const Payment = (fieldConfig: FieldConfig): Block => {
const Payment = (fieldConfig: PaymentFieldConfig): Block => {
let paymentProcessorField = null;
if (fieldConfig?.paymentProcessor) {
@@ -364,13 +364,13 @@ const Payment = (fieldConfig: FieldConfig): Block => {
type: 'row',
fields: [
{
...label,
...name,
admin: {
width: '50%',
},
},
{
...width,
...label,
admin: {
width: '50%',
},
@@ -380,116 +380,125 @@ const Payment = (fieldConfig: FieldConfig): Block => {
{
type: 'row',
fields: [
paymentProcessorField,
{
...width,
admin: {
width: '50%',
},
},
{
name: 'basePrice',
type: 'number',
label: 'Base Price',
admin: {
width: '50%',
},
},
],
},
paymentProcessorField,
{
name: 'priceConditions',
labels: {
singular: 'Price Condition',
plural: 'Price Conditions',
},
type: 'array',
label: 'Price Conditions',
fields: [
{
name: 'fieldToUse',
type: 'text',
admin: {
components: {
Field: DynamicFieldSelector,
},
},
},
{
name: 'priceConditions',
labels: {
singular: 'Price Condition',
plural: 'Price Conditions',
},
type: 'array',
label: 'Price Conditions',
fields: [
name: 'condition',
label: 'Condition',
type: 'select',
defaultValue: 'hasValue',
options: [
{
name: 'fieldToUse',
type: 'text',
admin: {
components: {
Field: DynamicFieldSelector,
},
},
value: 'hasValue',
label: 'Has Any Value'
},
{
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'
}
]
value: 'equals',
label: 'Equals'
},
{
name: 'valueForCondition',
label: 'Value',
type: 'text',
admin: {
condition: (_: any, { condition }: any) => 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: 'Value Of Field',
value: 'valueOfField'
}
]
},
{
name: 'valueForOperator',
label: 'Value',
type: 'text',
admin: {
components: {
Field: DynamicPriceSelector,
},
},
},
value: 'notEquals',
label: 'Does Not Equal'
}
]
},
].filter(Boolean) as Field[],
{
name: 'valueForCondition',
label: 'Value',
type: 'text',
admin: {
condition: (_: any, { condition }: any) => 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: 'Value Of Field',
value: 'valueOfField'
}
]
},
{
name: 'valueForOperator',
label: 'Value',
type: 'text',
admin: {
components: {
Field: DynamicPriceSelector,
},
},
},
]
},
required,
]
].filter(Boolean) as Field[]
})
};

View File

@@ -4,6 +4,8 @@ import { generateSubmissionCollection } from './collections/FormSubmissions';
import { FormConfig } from './types';
// import path from 'path';
export { getPaymentTotal } from './utilities/getPaymentTotal'
const FormBuilder = (incomingFormConfig: FormConfig) => (config: Config): Config => {
const formConfig: FormConfig = {
...incomingFormConfig,

View File

@@ -15,11 +15,12 @@ export type FieldValues = {
[key: string]: string | number | boolean | null | undefined
}
export type FieldConfig = {
[key: string]: Partial<Field>
export type PaymentFieldConfig = Partial<Field> & {
paymentProcessor: Partial<SelectField>
}
export type FieldConfig = Partial<Field> | PaymentFieldConfig;
export type FieldsConfig = {
select?: boolean | FieldConfig
text?: boolean | FieldConfig