chore: style refinements

This commit is contained in:
PatrikKozak
2023-03-15 11:58:40 -04:00
parent b459277c72
commit 8e814b1edd
9 changed files with 154 additions and 131 deletions

View File

@@ -7,7 +7,7 @@
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "Debug Learn with Jason CMS", "name": "Form Builder Example CMS",
"program": "${workspaceFolder}/src/server.ts", "program": "${workspaceFolder}/src/server.ts",
"preLaunchTask": "npm: build:server", "preLaunchTask": "npm: build:server",
"env": { "env": {
@@ -18,4 +18,4 @@
// ] // ]
}, },
] ]
} }

View File

@@ -74,7 +74,7 @@ export const advancedForm = {
blockType: 'number', blockType: 'number',
}, },
{ {
name: 'county', name: 'country',
label: 'Country', label: 'Country',
width: 50, width: 50,
required: true, required: true,
@@ -92,6 +92,7 @@ export const advancedForm = {
text: 'Your shipping information submission was successful.', text: 'Your shipping information submission was successful.',
}, },
], ],
type: 'h2',
}, },
], ],
emails: [ emails: [

View File

@@ -78,6 +78,7 @@ export const basicForm = {
text: 'The basic form has been submitted successfully.', text: 'The basic form has been submitted successfully.',
}, },
], ],
type: 'h2',
}, },
], ],
emails: [ emails: [

View File

@@ -48,6 +48,7 @@ export const contactForm = {
text: 'The contact form has been submitted successfully.', text: 'The contact form has been submitted successfully.',
}, },
], ],
type: 'h2',
}, },
], ],
emails: [ emails: [

View File

@@ -60,6 +60,7 @@ export const signUpForm = {
text: 'Your sign up submission was successful.', text: 'Your sign up submission was successful.',
}, },
], ],
type: 'h2',
}, },
], ],
emails: [ emails: [

View File

@@ -5,6 +5,16 @@
flex-direction: column; flex-direction: column;
} }
.hasSubmitted {
align-items: center;
justify-content: center;
height: 80vh;
@include small-break {
height: 100%;
}
}
.intro { .intro {
margin-bottom: var(--base); margin-bottom: var(--base);
@@ -13,6 +23,12 @@
} }
} }
.confirmationMessage {
* {
margin: 0;
}
}
.fieldWrap { .fieldWrap {
position: relative; position: relative;
z-index: 2; z-index: 2;

View File

@@ -1,14 +1,14 @@
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react'
import { fields } from './fields'; import { buildInitialFormState } from './buildInitialFormState'
import { Form as FormType } from 'payload-plugin-form-builder/dist/types'; import { fields } from './fields'
import RichText from '../../RichText'; import { Form as FormType } from 'payload-plugin-form-builder/dist/types'
import { useForm } from 'react-hook-form'; import RichText from '../../RichText'
import { useRouter } from 'next/router'; import { useForm } from 'react-hook-form'
import { Gutter } from '../../Gutter'; import { useRouter } from 'next/router'
import { Button } from '../../Button'; import { Gutter } from '../../Gutter'
import { Button } from '../../Button'
import classes from './index.module.scss'; import classes from './index.module.scss'
import { buildInitialFormState } from './buildInitialFormState';
export type Value = unknown export type Value = unknown
@@ -26,157 +26,147 @@ export type FormBlockType = {
enableIntro: Boolean enableIntro: Boolean
form: FormType form: FormType
introContent?: { introContent?: {
[k: string]: unknown; [k: string]: unknown
}[]; }[]
} }
export const FormBlock: React.FC<FormBlockType & { export const FormBlock: React.FC<
id?: string, FormBlockType & {
}> = (props) => { id?: string
}
> = props => {
const { const {
enableIntro, enableIntro,
introContent, introContent,
form: formFromProps, form: formFromProps,
form: { form: { id: formID, submitButtonLabel, confirmationType, redirect, confirmationMessage } = {},
id: formID, } = props
submitButtonLabel,
confirmationType,
redirect,
confirmationMessage
} = {},
} = props;
const formMethods = useForm({ const formMethods = useForm({
defaultValues: buildInitialFormState(formFromProps.fields) defaultValues: buildInitialFormState(formFromProps.fields),
}); })
const { register, handleSubmit, formState: { errors }, control, setValue, getValues } = formMethods; const {
register,
handleSubmit,
formState: { errors },
control,
setValue,
getValues,
} = formMethods
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false)
const [hasSubmitted, setHasSubmitted] = useState<boolean>(); const [hasSubmitted, setHasSubmitted] = useState<boolean>()
const [error, setError] = useState<{ status?: string, message: string } | undefined>(); const [error, setError] = useState<{ status?: string; message: string } | undefined>()
const router = useRouter(); const router = useRouter()
const onSubmit = useCallback((data: Data) => { const onSubmit = useCallback(
let loadingTimerID: NodeJS.Timer; (data: Data) => {
let loadingTimerID: NodeJS.Timer
const submitForm = async () => { const submitForm = async () => {
setError(undefined); setError(undefined)
const dataToSend = Object.entries(data).map(([name, value]) => ({ const dataToSend = Object.entries(data).map(([name, value]) => ({
field: name, field: name,
value value,
})); }))
// delay loading indicator by 1s // delay loading indicator by 1s
loadingTimerID = setTimeout(() => { loadingTimerID = setTimeout(() => {
setIsLoading(true); setIsLoading(true)
}, 1000); }, 1000)
try { try {
const req = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/form-submissions`, { const req = await fetch(`${process.env.NEXT_PUBLIC_CMS_URL}/api/form-submissions`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
form: formID, form: formID,
submissionData: dataToSend, submissionData: dataToSend,
}),
}) })
})
const res = await req.json(); const res = await req.json()
clearTimeout(loadingTimerID); clearTimeout(loadingTimerID)
if (req.status >= 400) { if (req.status >= 400) {
setIsLoading(false); 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({ setError({
status: res.status, message: 'Something went wrong.',
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({
message: 'Something went wrong.'
});
} }
}
submitForm(); submitForm()
}, [ },
router, [router, formID, redirect, confirmationType],
formID, )
redirect,
confirmationType,
]);
return ( return (
<Gutter> <Gutter>
<div className={classes.form}> <div
className={[
classes.form,
hasSubmitted && classes.hasSubmitted,
].filter(Boolean).join(' ')}
>
{enableIntro && introContent && !hasSubmitted && ( {enableIntro && introContent && !hasSubmitted && (
<RichText <RichText className={classes.intro} content={introContent} />
className={classes.intro}
content={introContent}
/>
)} )}
{!isLoading && hasSubmitted && confirmationType === 'message' && ( {!isLoading && hasSubmitted && confirmationType === 'message' && (
<RichText content={confirmationMessage} /> <RichText className={classes.confirmationMessage} content={confirmationMessage} />
)}
{isLoading && !hasSubmitted && (
<p>
Loading, please wait...
</p>
)}
{error && (
<div>
{`${error.status || '500'}: ${error.message || ''}`}
</div>
)} )}
{isLoading && !hasSubmitted && <p>Loading, please wait...</p>}
{error && <div>{`${error.status || '500'}: ${error.message || ''}`}</div>}
{!hasSubmitted && ( {!hasSubmitted && (
<form id={formID} onSubmit={handleSubmit(onSubmit)}> <form id={formID} onSubmit={handleSubmit(onSubmit)}>
<div className={classes.fieldWrap}> <div className={classes.fieldWrap}>
{formFromProps && formFromProps.fields && formFromProps.fields.map((field, index) => { {formFromProps &&
const Field: React.FC<any> = fields?.[field.blockType]; formFromProps.fields &&
if (Field) { formFromProps.fields.map((field, index) => {
return ( const Field: React.FC<any> = fields?.[field.blockType]
<React.Fragment key={index}> if (Field) {
<Field return (
form={formFromProps} <React.Fragment key={index}>
{...field} <Field
{...formMethods} form={formFromProps}
register={register} {...field}
errors={errors} {...formMethods}
control={control} register={register}
/> errors={errors}
</React.Fragment> control={control}
) />
} </React.Fragment>
return null; )
})} }
return null
})}
</div> </div>
<Button <Button label={submitButtonLabel} appearance="primary" el="button" form={formID} />
label={submitButtonLabel}
appearance="primary"
el="button"
form={formID}
/>
</form> </form>
)} )}
</div> </div>

View File

@@ -38,6 +38,7 @@ const Blocks: React.FC<{
<VerticalPadding <VerticalPadding
key={isFormBlock ? formID : index} key={isFormBlock ? formID : index}
top='small' top='small'
bottom='small'
> >
{/*@ts-ignore*/} {/*@ts-ignore*/}
<Block <Block

View File

@@ -34,11 +34,23 @@
.appearance--primary { .appearance--primary {
background-color: var(--color-black); background-color: var(--color-black);
color: var(--color-white); 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 { .appearance--secondary {
background-color: var(--color-white); background-color: var(--color-white);
box-shadow: inset 0 0 0 1px var(--color-black); 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 { .appearance--default {