chore: style refinements
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Learn with Jason CMS",
|
||||
"name": "Form Builder Example CMS",
|
||||
"program": "${workspaceFolder}/src/server.ts",
|
||||
"preLaunchTask": "npm: build:server",
|
||||
"env": {
|
||||
@@ -18,4 +18,4 @@
|
||||
// ]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export const advancedForm = {
|
||||
blockType: 'number',
|
||||
},
|
||||
{
|
||||
name: 'county',
|
||||
name: 'country',
|
||||
label: 'Country',
|
||||
width: 50,
|
||||
required: true,
|
||||
@@ -92,6 +92,7 @@ export const advancedForm = {
|
||||
text: 'Your shipping information submission was successful.',
|
||||
},
|
||||
],
|
||||
type: 'h2',
|
||||
},
|
||||
],
|
||||
emails: [
|
||||
|
||||
@@ -78,6 +78,7 @@ export const basicForm = {
|
||||
text: 'The basic form has been submitted successfully.',
|
||||
},
|
||||
],
|
||||
type: 'h2',
|
||||
},
|
||||
],
|
||||
emails: [
|
||||
|
||||
@@ -48,6 +48,7 @@ export const contactForm = {
|
||||
text: 'The contact form has been submitted successfully.',
|
||||
},
|
||||
],
|
||||
type: 'h2',
|
||||
},
|
||||
],
|
||||
emails: [
|
||||
|
||||
@@ -60,6 +60,7 @@ export const signUpForm = {
|
||||
text: 'Your sign up submission was successful.',
|
||||
},
|
||||
],
|
||||
type: 'h2',
|
||||
},
|
||||
],
|
||||
emails: [
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.hasSubmitted {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 80vh;
|
||||
|
||||
@include small-break {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@@ -13,6 +23,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.confirmationMessage {
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fieldWrap {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { fields } from './fields';
|
||||
import { Form as FormType } from 'payload-plugin-form-builder/dist/types';
|
||||
import RichText from '../../RichText';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Gutter } from '../../Gutter';
|
||||
import { Button } from '../../Button';
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import { buildInitialFormState } from './buildInitialFormState'
|
||||
import { fields } from './fields'
|
||||
import { Form as FormType } from 'payload-plugin-form-builder/dist/types'
|
||||
import RichText from '../../RichText'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Gutter } from '../../Gutter'
|
||||
import { Button } from '../../Button'
|
||||
|
||||
import classes from './index.module.scss';
|
||||
import { buildInitialFormState } from './buildInitialFormState';
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type Value = unknown
|
||||
|
||||
@@ -26,157 +26,147 @@ export type FormBlockType = {
|
||||
enableIntro: Boolean
|
||||
form: FormType
|
||||
introContent?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
}
|
||||
|
||||
export const FormBlock: React.FC<FormBlockType & {
|
||||
id?: string,
|
||||
}> = (props) => {
|
||||
export const FormBlock: React.FC<
|
||||
FormBlockType & {
|
||||
id?: string
|
||||
}
|
||||
> = props => {
|
||||
const {
|
||||
enableIntro,
|
||||
introContent,
|
||||
form: formFromProps,
|
||||
form: {
|
||||
id: formID,
|
||||
submitButtonLabel,
|
||||
confirmationType,
|
||||
redirect,
|
||||
confirmationMessage
|
||||
} = {},
|
||||
} = props;
|
||||
form: { id: formID, submitButtonLabel, confirmationType, redirect, confirmationMessage } = {},
|
||||
} = props
|
||||
|
||||
const formMethods = useForm({
|
||||
defaultValues: buildInitialFormState(formFromProps.fields)
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors }, control, setValue, getValues } = formMethods;
|
||||
defaultValues: buildInitialFormState(formFromProps.fields),
|
||||
})
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
control,
|
||||
setValue,
|
||||
getValues,
|
||||
} = formMethods
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [hasSubmitted, setHasSubmitted] = useState<boolean>();
|
||||
const [error, setError] = useState<{ status?: string, message: string } | undefined>();
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [hasSubmitted, setHasSubmitted] = useState<boolean>()
|
||||
const [error, setError] = useState<{ status?: string; message: string } | undefined>()
|
||||
const router = useRouter()
|
||||
|
||||
const onSubmit = useCallback((data: Data) => {
|
||||
let loadingTimerID: NodeJS.Timer;
|
||||
const onSubmit = useCallback(
|
||||
(data: Data) => {
|
||||
let loadingTimerID: NodeJS.Timer
|
||||
|
||||
const submitForm = async () => {
|
||||
setError(undefined);
|
||||
const submitForm = async () => {
|
||||
setError(undefined)
|
||||
|
||||
const dataToSend = Object.entries(data).map(([name, value]) => ({
|
||||
field: name,
|
||||
value
|
||||
}));
|
||||
const dataToSend = Object.entries(data).map(([name, value]) => ({
|
||||
field: name,
|
||||
value,
|
||||
}))
|
||||
|
||||
// delay loading indicator by 1s
|
||||
loadingTimerID = setTimeout(() => {
|
||||
setIsLoading(true);
|
||||
}, 1000);
|
||||
// delay loading indicator by 1s
|
||||
loadingTimerID = setTimeout(() => {
|
||||
setIsLoading(true)
|
||||
}, 1000)
|
||||
|
||||
try {
|
||||
const req = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/form-submissions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
form: formID,
|
||||
submissionData: dataToSend,
|
||||
try {
|
||||
const req = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/form-submissions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
form: formID,
|
||||
submissionData: dataToSend,
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
const res = await req.json();
|
||||
const res = await req.json()
|
||||
|
||||
clearTimeout(loadingTimerID);
|
||||
clearTimeout(loadingTimerID)
|
||||
|
||||
if (req.status >= 400) {
|
||||
setIsLoading(false);
|
||||
if (req.status >= 400) {
|
||||
setIsLoading(false)
|
||||
setError({
|
||||
status: res.status,
|
||||
message: res.errors?.[0]?.message || 'Internal Server Error',
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoading(false)
|
||||
setHasSubmitted(true)
|
||||
|
||||
if (confirmationType === 'redirect' && redirect) {
|
||||
const { url } = redirect
|
||||
|
||||
const redirectUrl = url
|
||||
|
||||
if (redirectUrl) router.push(redirectUrl)
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err)
|
||||
setIsLoading(false)
|
||||
setError({
|
||||
status: res.status,
|
||||
message: res.errors?.[0]?.message || 'Internal Server Error',
|
||||
});
|
||||
|
||||
return;
|
||||
message: 'Something went wrong.',
|
||||
})
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
setHasSubmitted(true);
|
||||
|
||||
if (confirmationType === 'redirect' && redirect) {
|
||||
const {
|
||||
url
|
||||
} = redirect;
|
||||
|
||||
const redirectUrl = url;
|
||||
|
||||
if (redirectUrl) router.push(redirectUrl);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
setIsLoading(false);
|
||||
setError({
|
||||
message: 'Something went wrong.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
submitForm();
|
||||
}, [
|
||||
router,
|
||||
formID,
|
||||
redirect,
|
||||
confirmationType,
|
||||
]);
|
||||
submitForm()
|
||||
},
|
||||
[router, formID, redirect, confirmationType],
|
||||
)
|
||||
|
||||
return (
|
||||
<Gutter>
|
||||
<div className={classes.form}>
|
||||
<div
|
||||
className={[
|
||||
classes.form,
|
||||
hasSubmitted && classes.hasSubmitted,
|
||||
].filter(Boolean).join(' ')}
|
||||
>
|
||||
{enableIntro && introContent && !hasSubmitted && (
|
||||
<RichText
|
||||
className={classes.intro}
|
||||
content={introContent}
|
||||
/>
|
||||
<RichText className={classes.intro} content={introContent} />
|
||||
)}
|
||||
{!isLoading && hasSubmitted && confirmationType === 'message' && (
|
||||
<RichText content={confirmationMessage} />
|
||||
)}
|
||||
{isLoading && !hasSubmitted && (
|
||||
<p>
|
||||
Loading, please wait...
|
||||
</p>
|
||||
)}
|
||||
{error && (
|
||||
<div>
|
||||
{`${error.status || '500'}: ${error.message || ''}`}
|
||||
</div>
|
||||
<RichText className={classes.confirmationMessage} content={confirmationMessage} />
|
||||
)}
|
||||
{isLoading && !hasSubmitted && <p>Loading, please wait...</p>}
|
||||
{error && <div>{`${error.status || '500'}: ${error.message || ''}`}</div>}
|
||||
{!hasSubmitted && (
|
||||
<form id={formID} onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className={classes.fieldWrap}>
|
||||
{formFromProps && formFromProps.fields && formFromProps.fields.map((field, index) => {
|
||||
const Field: React.FC<any> = fields?.[field.blockType];
|
||||
if (Field) {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<Field
|
||||
form={formFromProps}
|
||||
{...field}
|
||||
{...formMethods}
|
||||
register={register}
|
||||
errors={errors}
|
||||
control={control}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{formFromProps &&
|
||||
formFromProps.fields &&
|
||||
formFromProps.fields.map((field, index) => {
|
||||
const Field: React.FC<any> = fields?.[field.blockType]
|
||||
if (Field) {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<Field
|
||||
form={formFromProps}
|
||||
{...field}
|
||||
{...formMethods}
|
||||
register={register}
|
||||
errors={errors}
|
||||
control={control}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
label={submitButtonLabel}
|
||||
appearance="primary"
|
||||
el="button"
|
||||
form={formID}
|
||||
/>
|
||||
<Button label={submitButtonLabel} appearance="primary" el="button" form={formID} />
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -38,6 +38,7 @@ const Blocks: React.FC<{
|
||||
<VerticalPadding
|
||||
key={isFormBlock ? formID : index}
|
||||
top='small'
|
||||
bottom='small'
|
||||
>
|
||||
{/*@ts-ignore*/}
|
||||
<Block
|
||||
|
||||
@@ -34,11 +34,23 @@
|
||||
.appearance--primary {
|
||||
background-color: var(--color-black);
|
||||
color: var(--color-white);
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-black);
|
||||
box-shadow: inset 0 0 0 1px var(--color-black);
|
||||
}
|
||||
}
|
||||
|
||||
.appearance--secondary {
|
||||
background-color: var(--color-white);
|
||||
box-shadow: inset 0 0 0 1px var(--color-black);
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: var(--color-black);
|
||||
color: var(--color-white);
|
||||
box-shadow: inset 0 0 0 1px var(--color-black);
|
||||
}
|
||||
}
|
||||
|
||||
.appearance--default {
|
||||
|
||||
Reference in New Issue
Block a user