feat(contact): integrate contact with payload form builder
This commit is contained in:
parent
20ec5e90b1
commit
518aff8c5c
1
env
1
env
@ -13,3 +13,4 @@ S3_ACCESS_KEY_ID=97394cd503ae9973efa25168ed980e51
|
|||||||
S3_SECRET_ACCESS_KEY=9b4e412850582aab32ffdadbe23bb7ea972b39aaf79c34da0c19d3fcafe11dd0
|
S3_SECRET_ACCESS_KEY=9b4e412850582aab32ffdadbe23bb7ea972b39aaf79c34da0c19d3fcafe11dd0
|
||||||
S3_REGION=ap-southeast-1
|
S3_REGION=ap-southeast-1
|
||||||
S3_ENDPOINT=https://jswmbraeandqttpcdfmj.supabase.co/storage/v1/s3
|
S3_ENDPOINT=https://jswmbraeandqttpcdfmj.supabase.co/storage/v1/s3
|
||||||
|
NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000
|
||||||
|
33
package-lock.json
generated
33
package-lock.json
generated
@ -11,6 +11,7 @@
|
|||||||
"@payloadcms/db-postgres": "^3.20.0",
|
"@payloadcms/db-postgres": "^3.20.0",
|
||||||
"@payloadcms/next": "^3.20.0",
|
"@payloadcms/next": "^3.20.0",
|
||||||
"@payloadcms/payload-cloud": "^3.20.0",
|
"@payloadcms/payload-cloud": "^3.20.0",
|
||||||
|
"@payloadcms/plugin-form-builder": "^3.20.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.20.0",
|
"@payloadcms/richtext-lexical": "^3.20.0",
|
||||||
"@payloadcms/storage-s3": "^3.20.0",
|
"@payloadcms/storage-s3": "^3.20.0",
|
||||||
"@popperjs/core": "2.11.8",
|
"@popperjs/core": "2.11.8",
|
||||||
@ -25,6 +26,7 @@
|
|||||||
"photoswipe": "^5.4.4",
|
"photoswipe": "^5.4.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"react-hook-form": "^7.54.2",
|
||||||
"react-modal-video": "^2.0.2",
|
"react-modal-video": "^2.0.2",
|
||||||
"react-photoswipe-gallery": "^3.0.1",
|
"react-photoswipe-gallery": "^3.0.1",
|
||||||
"rellax": "^1.12.1",
|
"rellax": "^1.12.1",
|
||||||
@ -3369,6 +3371,21 @@
|
|||||||
"payload": "3.20.0"
|
"payload": "3.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@payloadcms/plugin-form-builder": {
|
||||||
|
"version": "3.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@payloadcms/plugin-form-builder/-/plugin-form-builder-3.20.0.tgz",
|
||||||
|
"integrity": "sha512-te29bejvFeH2xw4HtE9JA5DDZycKPuLZOPTGnoShwj+DeqZn5k3SfzewLam1fxPqxHHJhNzTO273lkPsCSxlPg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@payloadcms/ui": "3.20.0",
|
||||||
|
"escape-html": "^1.0.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"payload": "3.20.0",
|
||||||
|
"react": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020",
|
||||||
|
"react-dom": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@payloadcms/richtext-lexical": {
|
"node_modules/@payloadcms/richtext-lexical": {
|
||||||
"version": "3.20.0",
|
"version": "3.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/@payloadcms/richtext-lexical/-/richtext-lexical-3.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@payloadcms/richtext-lexical/-/richtext-lexical-3.20.0.tgz",
|
||||||
@ -10532,6 +10549,22 @@
|
|||||||
"react": ">=16.13.1"
|
"react": ">=16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-hook-form": {
|
||||||
|
"version": "7.54.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz",
|
||||||
|
"integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/react-hook-form"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17 || ^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-image-crop": {
|
"node_modules/react-image-crop": {
|
||||||
"version": "10.1.8",
|
"version": "10.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-10.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/react-image-crop/-/react-image-crop-10.1.8.tgz",
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"@payloadcms/db-postgres": "^3.20.0",
|
"@payloadcms/db-postgres": "^3.20.0",
|
||||||
"@payloadcms/next": "^3.20.0",
|
"@payloadcms/next": "^3.20.0",
|
||||||
"@payloadcms/payload-cloud": "^3.20.0",
|
"@payloadcms/payload-cloud": "^3.20.0",
|
||||||
|
"@payloadcms/plugin-form-builder": "^3.20.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.20.0",
|
"@payloadcms/richtext-lexical": "^3.20.0",
|
||||||
"@payloadcms/storage-s3": "^3.20.0",
|
"@payloadcms/storage-s3": "^3.20.0",
|
||||||
"@popperjs/core": "2.11.8",
|
"@popperjs/core": "2.11.8",
|
||||||
@ -30,6 +31,7 @@
|
|||||||
"photoswipe": "^5.4.4",
|
"photoswipe": "^5.4.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"react-hook-form": "^7.54.2",
|
||||||
"react-modal-video": "^2.0.2",
|
"react-modal-video": "^2.0.2",
|
||||||
"react-photoswipe-gallery": "^3.0.1",
|
"react-photoswipe-gallery": "^3.0.1",
|
||||||
"rellax": "^1.12.1",
|
"rellax": "^1.12.1",
|
||||||
|
@ -2494,9 +2494,9 @@ textarea:-webkit-autofill:focus {
|
|||||||
color: var(--color-dark-mode-gray-1) !important;
|
color: var(--color-dark-mode-gray-1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
/* .form-control {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
} */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Input underline
|
* Input underline
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Contact from "@/components/Contacts/Contact";
|
import Contact from "@/components/Contacts/Contact";
|
||||||
|
import { fetchForm } from "@/services/payload/form";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
@ -6,7 +7,9 @@ export const metadata = {
|
|||||||
description: "Contact - Cochise Oncology",
|
description: "Contact - Cochise Oncology",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function SlickContactPageDark() {
|
export default async function SlickContactPageDark() {
|
||||||
|
const formRes = await fetchForm(1);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section
|
<section
|
||||||
@ -36,7 +39,7 @@ export default function SlickContactPageDark() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="page-section scrollSpysection" id="contact">
|
<section className="page-section scrollSpysection" id="contact">
|
||||||
<Contact />
|
<Contact form={formRes} />
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -4,13 +4,13 @@ import { useEffect } from "react";
|
|||||||
import { init_wow } from "@/utils/initWow";
|
import { init_wow } from "@/utils/initWow";
|
||||||
import { parallaxMouseMovement, parallaxScroll } from "@/utils/parallax";
|
import { parallaxMouseMovement, parallaxScroll } from "@/utils/parallax";
|
||||||
import { headerChangeOnScroll } from "@/utils/changeHeaderOnScroll";
|
import { headerChangeOnScroll } from "@/utils/changeHeaderOnScroll";
|
||||||
|
import "@/app/globals.css";
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
import "jarallax/dist/jarallax.min.css";
|
import "jarallax/dist/jarallax.min.css";
|
||||||
import "swiper/css/effect-fade";
|
import "swiper/css/effect-fade";
|
||||||
import "react-modal-video/css/modal-video.css";
|
import "react-modal-video/css/modal-video.css";
|
||||||
import "photoswipe/dist/photoswipe.css";
|
import "photoswipe/dist/photoswipe.css";
|
||||||
import "tippy.js/dist/tippy.css";
|
import "tippy.js/dist/tippy.css";
|
||||||
|
|
||||||
import "@public/assets/css/styles.css";
|
import "@public/assets/css/styles.css";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Header from "@/components/Header";
|
import Header from "@/components/Header";
|
||||||
|
@ -20,6 +20,7 @@ import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93
|
|||||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { FixedToolbarFeatureClient as FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { default as default_382980007b1adf7b8e8832707f1169fb } from '@/components/Logo/AdminLogo'
|
import { default as default_382980007b1adf7b8e8832707f1169fb } from '@/components/Logo/AdminLogo'
|
||||||
|
|
||||||
export const importMap = {
|
export const importMap = {
|
||||||
@ -45,5 +46,6 @@ export const importMap = {
|
|||||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#FixedToolbarFeatureClient": FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"/components/Logo/AdminLogo#default": default_382980007b1adf7b8e8832707f1169fb
|
"/components/Logo/AdminLogo#default": default_382980007b1adf7b8e8832707f1169fb
|
||||||
}
|
}
|
||||||
|
33
src/blocks/Form.ts
Normal file
33
src/blocks/Form.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import type { Block } from "payload";
|
||||||
|
|
||||||
|
export const FormBlock: Block = {
|
||||||
|
slug: "formBlock",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "form",
|
||||||
|
type: "relationship",
|
||||||
|
relationTo: "forms",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "enableIntro",
|
||||||
|
type: "checkbox",
|
||||||
|
label: "Enable Intro Content",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "introContent",
|
||||||
|
type: "richText",
|
||||||
|
admin: {
|
||||||
|
condition: (_, { enableIntro }) => Boolean(enableIntro),
|
||||||
|
},
|
||||||
|
label: "Intro Content",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
graphQL: {
|
||||||
|
singularName: "FormBlock",
|
||||||
|
},
|
||||||
|
labels: {
|
||||||
|
plural: "Form Blocks",
|
||||||
|
singular: "Form Block",
|
||||||
|
},
|
||||||
|
};
|
54
src/components/Blocks/Form/Button/index.tsx
Normal file
54
src/components/Blocks/Form/Button/index.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import type { ElementType } from "react";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export type Props = {
|
||||||
|
appearance?: "default" | "primary" | "secondary";
|
||||||
|
className?: string;
|
||||||
|
el?: "a" | "button" | "link";
|
||||||
|
form?: string;
|
||||||
|
href?: string;
|
||||||
|
label?: string;
|
||||||
|
newTab?: boolean | null;
|
||||||
|
onClick?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Button: React.FC<Props> = ({
|
||||||
|
className: classNameFromProps,
|
||||||
|
el = "button",
|
||||||
|
form,
|
||||||
|
href,
|
||||||
|
label,
|
||||||
|
newTab,
|
||||||
|
}) => {
|
||||||
|
const newTabProps = newTab ? { rel: "noopener noreferrer", target: "_blank" } : {};
|
||||||
|
const Element: ElementType = el;
|
||||||
|
const className = [classNameFromProps].filter(Boolean).join(" ");
|
||||||
|
|
||||||
|
const elementProps = {
|
||||||
|
...newTabProps,
|
||||||
|
className,
|
||||||
|
form,
|
||||||
|
href,
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<div className="btn btn-mod btn-color btn-large btn-full btn-circle btn-hover-anim">
|
||||||
|
<span>{label}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Element {...elementProps}>
|
||||||
|
<React.Fragment>
|
||||||
|
{(el === "link" || el === "a") && (
|
||||||
|
<Link {...newTabProps} href={href || ""}>
|
||||||
|
{content}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
{el === "button" && content}
|
||||||
|
</React.Fragment>
|
||||||
|
</Element>
|
||||||
|
);
|
||||||
|
};
|
38
src/components/Blocks/Form/Checkbox/index.tsx
Normal file
38
src/components/Blocks/Form/Checkbox/index.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { CheckboxField } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
import type { FieldErrorsImpl, FieldValues, UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Error } from "../Error";
|
||||||
|
import { Width } from "../Width";
|
||||||
|
|
||||||
|
export const Checkbox: React.FC<
|
||||||
|
{
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
|
getValues: any;
|
||||||
|
register: UseFormRegister<any & FieldValues>;
|
||||||
|
setValue: any;
|
||||||
|
} & CheckboxField
|
||||||
|
> = ({ name, errors, label, register, required: requiredFromProps, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className="form-group pr-4">
|
||||||
|
<div className="flex">
|
||||||
|
<input
|
||||||
|
className="form-check-input mr-2"
|
||||||
|
type="checkbox"
|
||||||
|
{...register(name, { required: requiredFromProps })}
|
||||||
|
/>
|
||||||
|
<label className="form-check-label" htmlFor={name}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{requiredFromProps && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
);
|
||||||
|
};
|
123
src/components/Blocks/Form/Country/index.module.scss
Normal file
123
src/components/Blocks/Form/Country/index.module.scss
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
@use '../shared.scss';
|
||||||
|
|
||||||
|
.select {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: var(--base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reactSelect {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
div.rs__control {
|
||||||
|
@include shared.formInput;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__input-container {
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__value-container {
|
||||||
|
padding: 0;
|
||||||
|
> * {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__single-value {
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__indicators {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--base) * 0.9);
|
||||||
|
right: calc(var(--base) * 0.9);
|
||||||
|
.arrow {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__indicator {
|
||||||
|
padding: 0px 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
svg path {
|
||||||
|
fill: var(--color-dark-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
svg path {
|
||||||
|
fill: var(--color-dark-gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__indicator-separator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__menu {
|
||||||
|
color: var(--color-black);
|
||||||
|
background-color: var(--color-white);
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: 0 4px 11px hsl(0deg 0% 0% / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__menu-list {
|
||||||
|
padding: calc(var(--base) / 4) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__group-heading {
|
||||||
|
margin-bottom: calc(var(--base) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__option {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: calc(var(--base) / 2) var(--base);
|
||||||
|
|
||||||
|
&--is-focused {
|
||||||
|
background-color: var(--color-light-gray);
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--is-selected {
|
||||||
|
background-color: var(--color-light-gray);
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__multi-value {
|
||||||
|
padding: 0;
|
||||||
|
background: var(--color-light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__multi-value__label {
|
||||||
|
max-width: 150px;
|
||||||
|
color: var(--color-black);
|
||||||
|
padding: calc(var(--base) / 8) calc(var(--base) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__multi-value__remove {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-black);
|
||||||
|
background: var(--color-light-gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__clear-indicator {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/components/Blocks/Form/Country/index.tsx
Normal file
50
src/components/Blocks/Form/Country/index.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import type { CountryField } from '@payloadcms/plugin-form-builder/types'
|
||||||
|
import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { Controller } from 'react-hook-form'
|
||||||
|
import ReactSelect from 'react-select'
|
||||||
|
|
||||||
|
import { Error } from '../Error'
|
||||||
|
import { Width } from '../Width'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
import { countryOptions } from './options'
|
||||||
|
|
||||||
|
export const Country: React.FC<
|
||||||
|
{
|
||||||
|
control: Control<FieldValues, any>
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any
|
||||||
|
}>
|
||||||
|
>
|
||||||
|
} & CountryField
|
||||||
|
> = ({ name, control, errors, label, required, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className={classes.select}>
|
||||||
|
<label className={classes.label} htmlFor={name}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name={name}
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<ReactSelect
|
||||||
|
className={classes.reactSelect}
|
||||||
|
classNamePrefix="rs"
|
||||||
|
inputId={name}
|
||||||
|
instanceId={name}
|
||||||
|
onChange={(val) => onChange(val ? val.value : '')}
|
||||||
|
options={countryOptions}
|
||||||
|
value={countryOptions.find((c) => c.value === value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
rules={{ required }}
|
||||||
|
/>
|
||||||
|
{required && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
)
|
||||||
|
}
|
982
src/components/Blocks/Form/Country/options.ts
Normal file
982
src/components/Blocks/Form/Country/options.ts
Normal file
@ -0,0 +1,982 @@
|
|||||||
|
export const countryOptions = [
|
||||||
|
{
|
||||||
|
label: 'Afghanistan',
|
||||||
|
value: 'AF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Åland Islands',
|
||||||
|
value: 'AX',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Albania',
|
||||||
|
value: 'AL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Algeria',
|
||||||
|
value: 'DZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'American Samoa',
|
||||||
|
value: 'AS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Andorra',
|
||||||
|
value: 'AD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Angola',
|
||||||
|
value: 'AO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Anguilla',
|
||||||
|
value: 'AI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Antarctica',
|
||||||
|
value: 'AQ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Antigua and Barbuda',
|
||||||
|
value: 'AG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Argentina',
|
||||||
|
value: 'AR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Armenia',
|
||||||
|
value: 'AM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Aruba',
|
||||||
|
value: 'AW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Australia',
|
||||||
|
value: 'AU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Austria',
|
||||||
|
value: 'AT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Azerbaijan',
|
||||||
|
value: 'AZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bahamas',
|
||||||
|
value: 'BS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bahrain',
|
||||||
|
value: 'BH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bangladesh',
|
||||||
|
value: 'BD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Barbados',
|
||||||
|
value: 'BB',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Belarus',
|
||||||
|
value: 'BY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Belgium',
|
||||||
|
value: 'BE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Belize',
|
||||||
|
value: 'BZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Benin',
|
||||||
|
value: 'BJ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bermuda',
|
||||||
|
value: 'BM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bhutan',
|
||||||
|
value: 'BT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bolivia',
|
||||||
|
value: 'BO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bosnia and Herzegovina',
|
||||||
|
value: 'BA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Botswana',
|
||||||
|
value: 'BW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bouvet Island',
|
||||||
|
value: 'BV',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Brazil',
|
||||||
|
value: 'BR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'British Indian Ocean Territory',
|
||||||
|
value: 'IO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Brunei Darussalam',
|
||||||
|
value: 'BN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Bulgaria',
|
||||||
|
value: 'BG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Burkina Faso',
|
||||||
|
value: 'BF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Burundi',
|
||||||
|
value: 'BI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cambodia',
|
||||||
|
value: 'KH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cameroon',
|
||||||
|
value: 'CM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Canada',
|
||||||
|
value: 'CA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cape Verde',
|
||||||
|
value: 'CV',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cayman Islands',
|
||||||
|
value: 'KY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Central African Republic',
|
||||||
|
value: 'CF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Chad',
|
||||||
|
value: 'TD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Chile',
|
||||||
|
value: 'CL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'China',
|
||||||
|
value: 'CN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Christmas Island',
|
||||||
|
value: 'CX',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cocos (Keeling) Islands',
|
||||||
|
value: 'CC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Colombia',
|
||||||
|
value: 'CO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Comoros',
|
||||||
|
value: 'KM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Congo',
|
||||||
|
value: 'CG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Congo, The Democratic Republic of the',
|
||||||
|
value: 'CD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cook Islands',
|
||||||
|
value: 'CK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Costa Rica',
|
||||||
|
value: 'CR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Cote D'Ivoire",
|
||||||
|
value: 'CI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Croatia',
|
||||||
|
value: 'HR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cuba',
|
||||||
|
value: 'CU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cyprus',
|
||||||
|
value: 'CY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Czech Republic',
|
||||||
|
value: 'CZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Denmark',
|
||||||
|
value: 'DK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Djibouti',
|
||||||
|
value: 'DJ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Dominica',
|
||||||
|
value: 'DM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Dominican Republic',
|
||||||
|
value: 'DO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Ecuador',
|
||||||
|
value: 'EC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Egypt',
|
||||||
|
value: 'EG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'El Salvador',
|
||||||
|
value: 'SV',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Equatorial Guinea',
|
||||||
|
value: 'GQ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Eritrea',
|
||||||
|
value: 'ER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Estonia',
|
||||||
|
value: 'EE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Ethiopia',
|
||||||
|
value: 'ET',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Falkland Islands (Malvinas)',
|
||||||
|
value: 'FK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Faroe Islands',
|
||||||
|
value: 'FO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Fiji',
|
||||||
|
value: 'FJ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Finland',
|
||||||
|
value: 'FI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'France',
|
||||||
|
value: 'FR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'French Guiana',
|
||||||
|
value: 'GF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'French Polynesia',
|
||||||
|
value: 'PF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'French Southern Territories',
|
||||||
|
value: 'TF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Gabon',
|
||||||
|
value: 'GA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Gambia',
|
||||||
|
value: 'GM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Georgia',
|
||||||
|
value: 'GE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Germany',
|
||||||
|
value: 'DE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Ghana',
|
||||||
|
value: 'GH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Gibraltar',
|
||||||
|
value: 'GI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Greece',
|
||||||
|
value: 'GR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Greenland',
|
||||||
|
value: 'GL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Grenada',
|
||||||
|
value: 'GD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Guadeloupe',
|
||||||
|
value: 'GP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Guam',
|
||||||
|
value: 'GU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Guatemala',
|
||||||
|
value: 'GT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Guernsey',
|
||||||
|
value: 'GG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Guinea',
|
||||||
|
value: 'GN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Guinea-Bissau',
|
||||||
|
value: 'GW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Guyana',
|
||||||
|
value: 'GY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Haiti',
|
||||||
|
value: 'HT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Heard Island and Mcdonald Islands',
|
||||||
|
value: 'HM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Holy See (Vatican City State)',
|
||||||
|
value: 'VA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Honduras',
|
||||||
|
value: 'HN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hong Kong',
|
||||||
|
value: 'HK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Hungary',
|
||||||
|
value: 'HU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Iceland',
|
||||||
|
value: 'IS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'India',
|
||||||
|
value: 'IN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Indonesia',
|
||||||
|
value: 'ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Iran, Islamic Republic Of',
|
||||||
|
value: 'IR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Iraq',
|
||||||
|
value: 'IQ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Ireland',
|
||||||
|
value: 'IE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Isle of Man',
|
||||||
|
value: 'IM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Israel',
|
||||||
|
value: 'IL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Italy',
|
||||||
|
value: 'IT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Jamaica',
|
||||||
|
value: 'JM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Japan',
|
||||||
|
value: 'JP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Jersey',
|
||||||
|
value: 'JE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Jordan',
|
||||||
|
value: 'JO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kazakhstan',
|
||||||
|
value: 'KZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kenya',
|
||||||
|
value: 'KE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kiribati',
|
||||||
|
value: 'KI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Democratic People's Republic of Korea",
|
||||||
|
value: 'KP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Korea, Republic of',
|
||||||
|
value: 'KR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kosovo',
|
||||||
|
value: 'XK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kuwait',
|
||||||
|
value: 'KW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kyrgyzstan',
|
||||||
|
value: 'KG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Lao People's Democratic Republic",
|
||||||
|
value: 'LA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Latvia',
|
||||||
|
value: 'LV',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Lebanon',
|
||||||
|
value: 'LB',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Lesotho',
|
||||||
|
value: 'LS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Liberia',
|
||||||
|
value: 'LR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Libyan Arab Jamahiriya',
|
||||||
|
value: 'LY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Liechtenstein',
|
||||||
|
value: 'LI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Lithuania',
|
||||||
|
value: 'LT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Luxembourg',
|
||||||
|
value: 'LU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Macao',
|
||||||
|
value: 'MO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Macedonia, The Former Yugoslav Republic of',
|
||||||
|
value: 'MK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Madagascar',
|
||||||
|
value: 'MG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Malawi',
|
||||||
|
value: 'MW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Malaysia',
|
||||||
|
value: 'MY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Maldives',
|
||||||
|
value: 'MV',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mali',
|
||||||
|
value: 'ML',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Malta',
|
||||||
|
value: 'MT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Marshall Islands',
|
||||||
|
value: 'MH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Martinique',
|
||||||
|
value: 'MQ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mauritania',
|
||||||
|
value: 'MR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mauritius',
|
||||||
|
value: 'MU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mayotte',
|
||||||
|
value: 'YT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mexico',
|
||||||
|
value: 'MX',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Micronesia, Federated States of',
|
||||||
|
value: 'FM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Moldova, Republic of',
|
||||||
|
value: 'MD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Monaco',
|
||||||
|
value: 'MC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mongolia',
|
||||||
|
value: 'MN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Montenegro',
|
||||||
|
value: 'ME',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Montserrat',
|
||||||
|
value: 'MS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Morocco',
|
||||||
|
value: 'MA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mozambique',
|
||||||
|
value: 'MZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Myanmar',
|
||||||
|
value: 'MM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Namibia',
|
||||||
|
value: 'NA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Nauru',
|
||||||
|
value: 'NR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Nepal',
|
||||||
|
value: 'NP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Netherlands',
|
||||||
|
value: 'NL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Netherlands Antilles',
|
||||||
|
value: 'AN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'New Caledonia',
|
||||||
|
value: 'NC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'New Zealand',
|
||||||
|
value: 'NZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Nicaragua',
|
||||||
|
value: 'NI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Niger',
|
||||||
|
value: 'NE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Nigeria',
|
||||||
|
value: 'NG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Niue',
|
||||||
|
value: 'NU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Norfolk Island',
|
||||||
|
value: 'NF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Northern Mariana Islands',
|
||||||
|
value: 'MP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Norway',
|
||||||
|
value: 'NO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Oman',
|
||||||
|
value: 'OM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Pakistan',
|
||||||
|
value: 'PK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Palau',
|
||||||
|
value: 'PW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Palestinian Territory, Occupied',
|
||||||
|
value: 'PS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Panama',
|
||||||
|
value: 'PA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Papua New Guinea',
|
||||||
|
value: 'PG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Paraguay',
|
||||||
|
value: 'PY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Peru',
|
||||||
|
value: 'PE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Philippines',
|
||||||
|
value: 'PH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Pitcairn',
|
||||||
|
value: 'PN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Poland',
|
||||||
|
value: 'PL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Portugal',
|
||||||
|
value: 'PT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Puerto Rico',
|
||||||
|
value: 'PR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Qatar',
|
||||||
|
value: 'QA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Reunion',
|
||||||
|
value: 'RE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Romania',
|
||||||
|
value: 'RO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Russian Federation',
|
||||||
|
value: 'RU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Rwanda',
|
||||||
|
value: 'RW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Saint Helena',
|
||||||
|
value: 'SH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Saint Kitts and Nevis',
|
||||||
|
value: 'KN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Saint Lucia',
|
||||||
|
value: 'LC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Saint Pierre and Miquelon',
|
||||||
|
value: 'PM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Saint Vincent and the Grenadines',
|
||||||
|
value: 'VC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Samoa',
|
||||||
|
value: 'WS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'San Marino',
|
||||||
|
value: 'SM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sao Tome and Principe',
|
||||||
|
value: 'ST',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Saudi Arabia',
|
||||||
|
value: 'SA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Senegal',
|
||||||
|
value: 'SN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Serbia',
|
||||||
|
value: 'RS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Seychelles',
|
||||||
|
value: 'SC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sierra Leone',
|
||||||
|
value: 'SL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Singapore',
|
||||||
|
value: 'SG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Slovakia',
|
||||||
|
value: 'SK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Slovenia',
|
||||||
|
value: 'SI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Solomon Islands',
|
||||||
|
value: 'SB',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Somalia',
|
||||||
|
value: 'SO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'South Africa',
|
||||||
|
value: 'ZA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'South Georgia and the South Sandwich Islands',
|
||||||
|
value: 'GS',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Spain',
|
||||||
|
value: 'ES',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sri Lanka',
|
||||||
|
value: 'LK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sudan',
|
||||||
|
value: 'SD',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Suriname',
|
||||||
|
value: 'SR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Svalbard and Jan Mayen',
|
||||||
|
value: 'SJ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Swaziland',
|
||||||
|
value: 'SZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sweden',
|
||||||
|
value: 'SE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Switzerland',
|
||||||
|
value: 'CH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Syrian Arab Republic',
|
||||||
|
value: 'SY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Taiwan',
|
||||||
|
value: 'TW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tajikistan',
|
||||||
|
value: 'TJ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tanzania, United Republic of',
|
||||||
|
value: 'TZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Thailand',
|
||||||
|
value: 'TH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Timor-Leste',
|
||||||
|
value: 'TL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Togo',
|
||||||
|
value: 'TG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tokelau',
|
||||||
|
value: 'TK',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tonga',
|
||||||
|
value: 'TO',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Trinidad and Tobago',
|
||||||
|
value: 'TT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tunisia',
|
||||||
|
value: 'TN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Turkey',
|
||||||
|
value: 'TR',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Turkmenistan',
|
||||||
|
value: 'TM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Turks and Caicos Islands',
|
||||||
|
value: 'TC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tuvalu',
|
||||||
|
value: 'TV',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Uganda',
|
||||||
|
value: 'UG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Ukraine',
|
||||||
|
value: 'UA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'United Arab Emirates',
|
||||||
|
value: 'AE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'United Kingdom',
|
||||||
|
value: 'GB',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'United States',
|
||||||
|
value: 'US',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'United States Minor Outlying Islands',
|
||||||
|
value: 'UM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Uruguay',
|
||||||
|
value: 'UY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Uzbekistan',
|
||||||
|
value: 'UZ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Vanuatu',
|
||||||
|
value: 'VU',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Venezuela',
|
||||||
|
value: 'VE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Viet Nam',
|
||||||
|
value: 'VN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Virgin Islands, British',
|
||||||
|
value: 'VG',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Virgin Islands, U.S.',
|
||||||
|
value: 'VI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Wallis and Futuna',
|
||||||
|
value: 'WF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Western Sahara',
|
||||||
|
value: 'EH',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Yemen',
|
||||||
|
value: 'YE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Zambia',
|
||||||
|
value: 'ZM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Zimbabwe',
|
||||||
|
value: 'ZW',
|
||||||
|
},
|
||||||
|
]
|
33
src/components/Blocks/Form/Email/index.tsx
Normal file
33
src/components/Blocks/Form/Email/index.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import type { EmailField } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
import type { FieldErrorsImpl, FieldValues, UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Error } from "../Error";
|
||||||
|
import { Width } from "../Width";
|
||||||
|
|
||||||
|
export const Email: React.FC<
|
||||||
|
{
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
|
register: UseFormRegister<any & FieldValues>;
|
||||||
|
} & EmailField
|
||||||
|
> = ({ name, errors, label, register, required: requiredFromProps, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className="form-group pr-4">
|
||||||
|
<label htmlFor={name}>{label}</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id={name}
|
||||||
|
className="input-lg input-circle form-control"
|
||||||
|
{...register(name, { pattern: /^\S[^\s@]*@\S+$/, required: requiredFromProps })}
|
||||||
|
/>
|
||||||
|
{requiredFromProps && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
);
|
||||||
|
};
|
4
src/components/Blocks/Form/Error/index.module.scss
Normal file
4
src/components/Blocks/Form/Error/index.module.scss
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.error {
|
||||||
|
margin-top: 5px;
|
||||||
|
color: var(--color-red);
|
||||||
|
}
|
5
src/components/Blocks/Form/Error/index.tsx
Normal file
5
src/components/Blocks/Form/Error/index.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export const Error: React.FC = () => {
|
||||||
|
return <div className="text-danger">This field is required</div>;
|
||||||
|
};
|
7
src/components/Blocks/Form/Gutter/index.module.scss
Normal file
7
src/components/Blocks/Form/Gutter/index.module.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.gutterLeft {
|
||||||
|
padding-left: var(--gutter-h);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gutterRight {
|
||||||
|
padding-right: var(--gutter-h);
|
||||||
|
}
|
26
src/components/Blocks/Form/Gutter/index.tsx
Normal file
26
src/components/Blocks/Form/Gutter/index.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
left?: boolean
|
||||||
|
ref?: React.Ref<HTMLDivElement>
|
||||||
|
right?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Gutter: React.FC<Props & { ref?: React.Ref<HTMLDivElement> }> = (props) => {
|
||||||
|
const { children, className, left = true, right = true, ref } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={[left && classes.gutterLeft, right && classes.gutterRight, className]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
72
src/components/Blocks/Form/Link/index.tsx
Normal file
72
src/components/Blocks/Form/Link/index.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import type { Page } from "@/payload-types";
|
||||||
|
|
||||||
|
import { Button } from "../Button";
|
||||||
|
|
||||||
|
export type CMSLinkType = {
|
||||||
|
appearance?: "default" | "primary" | "secondary";
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
label?: string;
|
||||||
|
newTab?: boolean | null;
|
||||||
|
reference?: {
|
||||||
|
relationTo: "pages";
|
||||||
|
value: number | Page | string;
|
||||||
|
} | null;
|
||||||
|
type?: "custom" | "reference" | null;
|
||||||
|
url?: null | string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CMSLink: React.FC<CMSLinkType> = ({
|
||||||
|
type,
|
||||||
|
appearance,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
label,
|
||||||
|
newTab,
|
||||||
|
reference,
|
||||||
|
url,
|
||||||
|
}) => {
|
||||||
|
const href =
|
||||||
|
type === "reference" && typeof reference?.value === "object" && reference.value.slug
|
||||||
|
? `${reference?.relationTo !== "pages" ? `/${reference?.relationTo}` : ""}/${reference.value.slug}`
|
||||||
|
: url;
|
||||||
|
|
||||||
|
if (!href) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appearance) {
|
||||||
|
const newTabProps = newTab ? { rel: "noopener noreferrer", target: "_blank" } : {};
|
||||||
|
|
||||||
|
if (type === "custom") {
|
||||||
|
return (
|
||||||
|
<a href={url || ""} {...newTabProps} className={className}>
|
||||||
|
{label && label}
|
||||||
|
{children ? <>{children}</> : null}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (href) {
|
||||||
|
return (
|
||||||
|
<Link href={href} {...newTabProps} className={className} prefetch={false}>
|
||||||
|
{label && label}
|
||||||
|
{children ? <>{children}</> : null}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonProps = {
|
||||||
|
appearance,
|
||||||
|
href,
|
||||||
|
label,
|
||||||
|
newTab,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Button className={className} {...buttonProps} el="link" />;
|
||||||
|
};
|
9
src/components/Blocks/Form/Message/index.module.scss
Normal file
9
src/components/Blocks/Form/Message/index.module.scss
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@use "../../css/queries.scss" as *;
|
||||||
|
|
||||||
|
.message {
|
||||||
|
margin: var(--base) 0 var(--base) 0;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
margin: calc(var(--base) * 0.5) 0 calc(var(--base) * 0.5) 0;
|
||||||
|
}
|
||||||
|
}
|
15
src/components/Blocks/Form/Message/index.tsx
Normal file
15
src/components/Blocks/Form/Message/index.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import type { MessageField } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import RichText from "../RichText";
|
||||||
|
import { Width } from "../Width";
|
||||||
|
import classes from "./index.module.scss";
|
||||||
|
|
||||||
|
export const Message: React.FC<MessageField> = ({ message }) => {
|
||||||
|
return (
|
||||||
|
<Width width={100}>
|
||||||
|
<RichText className={classes.message} content={message} />
|
||||||
|
</Width>
|
||||||
|
);
|
||||||
|
};
|
15
src/components/Blocks/Form/Number/index.module.scss
Normal file
15
src/components/Blocks/Form/Number/index.module.scss
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@use '../shared.scss';
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: var(--base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
@include shared.formInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
31
src/components/Blocks/Form/Number/index.tsx
Normal file
31
src/components/Blocks/Form/Number/index.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import type { TextField } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
import type { FieldErrorsImpl, FieldValues, UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Error } from "../Error";
|
||||||
|
import { Width } from "../Width";
|
||||||
|
import classes from "./index.module.scss";
|
||||||
|
|
||||||
|
export const Number: React.FC<
|
||||||
|
{
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
|
register: UseFormRegister<any & FieldValues>;
|
||||||
|
} & TextField
|
||||||
|
> = ({ name, errors, label, register, required: requiredFromProps, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className={classes.wrap}>
|
||||||
|
<label className={classes.label} htmlFor={name}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
<input className={classes.input} id={name} type="number" {...register(name, { required: requiredFromProps })} />
|
||||||
|
{requiredFromProps && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
);
|
||||||
|
};
|
24
src/components/Blocks/Form/RichText/index.tsx
Normal file
24
src/components/Blocks/Form/RichText/index.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { serializeLexical } from './serialize'
|
||||||
|
|
||||||
|
const RichText: React.FC<{ className?: string; content: any; enableGutter?: boolean }> = ({
|
||||||
|
className,
|
||||||
|
content,
|
||||||
|
}) => {
|
||||||
|
if (!content) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={[className].filter(Boolean).join(' ')}>
|
||||||
|
{content &&
|
||||||
|
!Array.isArray(content) &&
|
||||||
|
typeof content === 'object' &&
|
||||||
|
'root' in content &&
|
||||||
|
serializeLexical({ nodes: content?.root?.children })}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RichText
|
126
src/components/Blocks/Form/RichText/nodeFormat.tsx
Normal file
126
src/components/Blocks/Form/RichText/nodeFormat.tsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
//This copy-and-pasted from lexical here here: https://github.com/facebook/lexical/blob/c2ceee223f46543d12c574e62155e619f9a18a5d/packages/lexical/src/LexicalConstants.ts
|
||||||
|
|
||||||
|
import type { ElementFormatType, TextFormatType } from '@payloadcms/richtext-lexical/lexical'
|
||||||
|
import type { TextDetailType, TextModeType } from 'lexical/nodes/LexicalTextNode'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DOM
|
||||||
|
export const DOM_ELEMENT_TYPE = 1
|
||||||
|
export const DOM_TEXT_TYPE = 3
|
||||||
|
|
||||||
|
// Reconciling
|
||||||
|
export const NO_DIRTY_NODES = 0
|
||||||
|
export const HAS_DIRTY_NODES = 1
|
||||||
|
export const FULL_RECONCILE = 2
|
||||||
|
|
||||||
|
// Text node modes
|
||||||
|
export const IS_NORMAL = 0
|
||||||
|
export const IS_TOKEN = 1
|
||||||
|
export const IS_SEGMENTED = 2
|
||||||
|
// IS_INERT = 3
|
||||||
|
|
||||||
|
// Text node formatting
|
||||||
|
export const IS_BOLD = 1
|
||||||
|
export const IS_ITALIC = 1 << 1
|
||||||
|
export const IS_STRIKETHROUGH = 1 << 2
|
||||||
|
export const IS_UNDERLINE = 1 << 3
|
||||||
|
export const IS_CODE = 1 << 4
|
||||||
|
export const IS_SUBSCRIPT = 1 << 5
|
||||||
|
export const IS_SUPERSCRIPT = 1 << 6
|
||||||
|
export const IS_HIGHLIGHT = 1 << 7
|
||||||
|
|
||||||
|
export const IS_ALL_FORMATTING =
|
||||||
|
IS_BOLD |
|
||||||
|
IS_ITALIC |
|
||||||
|
IS_STRIKETHROUGH |
|
||||||
|
IS_UNDERLINE |
|
||||||
|
IS_CODE |
|
||||||
|
IS_SUBSCRIPT |
|
||||||
|
IS_SUPERSCRIPT |
|
||||||
|
IS_HIGHLIGHT
|
||||||
|
|
||||||
|
// Text node details
|
||||||
|
export const IS_DIRECTIONLESS = 1
|
||||||
|
export const IS_UNMERGEABLE = 1 << 1
|
||||||
|
|
||||||
|
// Element node formatting
|
||||||
|
export const IS_ALIGN_LEFT = 1
|
||||||
|
export const IS_ALIGN_CENTER = 2
|
||||||
|
export const IS_ALIGN_RIGHT = 3
|
||||||
|
export const IS_ALIGN_JUSTIFY = 4
|
||||||
|
export const IS_ALIGN_START = 5
|
||||||
|
export const IS_ALIGN_END = 6
|
||||||
|
|
||||||
|
// Reconciliation
|
||||||
|
export const NON_BREAKING_SPACE = '\u00A0'
|
||||||
|
const ZERO_WIDTH_SPACE = '\u200b'
|
||||||
|
|
||||||
|
export const DOUBLE_LINE_BREAK = '\n\n'
|
||||||
|
|
||||||
|
// For FF, we need to use a non-breaking space, or it gets composition
|
||||||
|
// in a stuck state.
|
||||||
|
|
||||||
|
const RTL = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'
|
||||||
|
const LTR =
|
||||||
|
'A-Za-z\u00C0-\u00D6\u00D8-\u00F6' +
|
||||||
|
'\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u200E\u2C00-\uFB1C' +
|
||||||
|
'\uFE00-\uFE6F\uFEFD-\uFFFF'
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export const RTL_REGEX = new RegExp('^[^' + LTR + ']*[' + RTL + ']')
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export const LTR_REGEX = new RegExp('^[^' + RTL + ']*[' + LTR + ']')
|
||||||
|
|
||||||
|
export const TEXT_TYPE_TO_FORMAT: Record<string | TextFormatType, number> = {
|
||||||
|
bold: IS_BOLD,
|
||||||
|
code: IS_CODE,
|
||||||
|
highlight: IS_HIGHLIGHT,
|
||||||
|
italic: IS_ITALIC,
|
||||||
|
strikethrough: IS_STRIKETHROUGH,
|
||||||
|
subscript: IS_SUBSCRIPT,
|
||||||
|
superscript: IS_SUPERSCRIPT,
|
||||||
|
underline: IS_UNDERLINE,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DETAIL_TYPE_TO_DETAIL: Record<string | TextDetailType, number> = {
|
||||||
|
directionless: IS_DIRECTIONLESS,
|
||||||
|
unmergeable: IS_UNMERGEABLE,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ELEMENT_TYPE_TO_FORMAT: Record<Exclude<ElementFormatType, ''>, number> = {
|
||||||
|
center: IS_ALIGN_CENTER,
|
||||||
|
end: IS_ALIGN_END,
|
||||||
|
justify: IS_ALIGN_JUSTIFY,
|
||||||
|
left: IS_ALIGN_LEFT,
|
||||||
|
right: IS_ALIGN_RIGHT,
|
||||||
|
start: IS_ALIGN_START,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ELEMENT_FORMAT_TO_TYPE: Record<number, ElementFormatType> = {
|
||||||
|
[IS_ALIGN_CENTER]: 'center',
|
||||||
|
[IS_ALIGN_END]: 'end',
|
||||||
|
[IS_ALIGN_JUSTIFY]: 'justify',
|
||||||
|
[IS_ALIGN_LEFT]: 'left',
|
||||||
|
[IS_ALIGN_RIGHT]: 'right',
|
||||||
|
[IS_ALIGN_START]: 'start',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TEXT_MODE_TO_TYPE: Record<TextModeType, 0 | 1 | 2> = {
|
||||||
|
normal: IS_NORMAL,
|
||||||
|
segmented: IS_SEGMENTED,
|
||||||
|
token: IS_TOKEN,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TEXT_TYPE_TO_MODE: Record<number, TextModeType> = {
|
||||||
|
[IS_NORMAL]: 'normal',
|
||||||
|
[IS_SEGMENTED]: 'segmented',
|
||||||
|
[IS_TOKEN]: 'token',
|
||||||
|
}
|
174
src/components/Blocks/Form/RichText/serialize.tsx
Normal file
174
src/components/Blocks/Form/RichText/serialize.tsx
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import type { LinkFields, SerializedLinkNode } from "@payloadcms/richtext-lexical";
|
||||||
|
import type {
|
||||||
|
SerializedElementNode,
|
||||||
|
SerializedLexicalNode,
|
||||||
|
SerializedTextNode,
|
||||||
|
} from "@payloadcms/richtext-lexical/lexical";
|
||||||
|
import type { SerializedListItemNode, SerializedListNode } from "@payloadcms/richtext-lexical/lexical/list";
|
||||||
|
import type { SerializedHeadingNode } from "@payloadcms/richtext-lexical/lexical/rich-text";
|
||||||
|
import type { JSX } from "react";
|
||||||
|
|
||||||
|
import React, { Fragment } from "react";
|
||||||
|
|
||||||
|
import { CMSLink } from "../Link";
|
||||||
|
import {
|
||||||
|
IS_BOLD,
|
||||||
|
IS_CODE,
|
||||||
|
IS_ITALIC,
|
||||||
|
IS_STRIKETHROUGH,
|
||||||
|
IS_SUBSCRIPT,
|
||||||
|
IS_SUPERSCRIPT,
|
||||||
|
IS_UNDERLINE,
|
||||||
|
} from "./nodeFormat";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
nodes: SerializedLexicalNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeLexical({ nodes }: Props): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{nodes?.map((_node, index): JSX.Element | null => {
|
||||||
|
if (_node.type === "text") {
|
||||||
|
const node = _node as SerializedTextNode;
|
||||||
|
let text = <React.Fragment key={index}>{node.text}</React.Fragment>;
|
||||||
|
if (node.format & IS_BOLD) {
|
||||||
|
text = <strong key={index}>{text}</strong>;
|
||||||
|
}
|
||||||
|
if (node.format & IS_ITALIC) {
|
||||||
|
text = <em key={index}>{text}</em>;
|
||||||
|
}
|
||||||
|
if (node.format & IS_STRIKETHROUGH) {
|
||||||
|
text = (
|
||||||
|
<span key={index} style={{ textDecoration: "line-through" }}>
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (node.format & IS_UNDERLINE) {
|
||||||
|
text = (
|
||||||
|
<span key={index} style={{ textDecoration: "underline" }}>
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (node.format & IS_CODE) {
|
||||||
|
text = <code key={index}>{node.text}</code>;
|
||||||
|
}
|
||||||
|
if (node.format & IS_SUBSCRIPT) {
|
||||||
|
text = <sub key={index}>{text}</sub>;
|
||||||
|
}
|
||||||
|
if (node.format & IS_SUPERSCRIPT) {
|
||||||
|
text = <sup key={index}>{text}</sup>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_node == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Hacky fix for
|
||||||
|
// https://github.com/facebook/lexical/blob/d10c4e6e55261b2fdd7d1845aed46151d0f06a8c/packages/lexical-list/src/LexicalListItemNode.ts#L133
|
||||||
|
// which does not return checked: false (only true - i.e. there is no prop for false)
|
||||||
|
const serializedChildrenFn = (node: SerializedElementNode): JSX.Element | null => {
|
||||||
|
if (node.children == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
if (node?.type === "list" && (node as SerializedListNode)?.listType === "check") {
|
||||||
|
for (const item of node.children) {
|
||||||
|
if ("checked" in item) {
|
||||||
|
if (!item?.checked) {
|
||||||
|
item.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serializeLexical({ nodes: node.children });
|
||||||
|
} else {
|
||||||
|
return serializeLexical({ nodes: node.children });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const serializedChildren = "children" in _node ? serializedChildrenFn(_node as SerializedElementNode) : "";
|
||||||
|
|
||||||
|
switch (_node.type) {
|
||||||
|
case "heading": {
|
||||||
|
const node = _node as SerializedHeadingNode;
|
||||||
|
|
||||||
|
type Heading = Extract<keyof JSX.IntrinsicElements, "h1" | "h2" | "h3" | "h4" | "h5">;
|
||||||
|
const Tag = node?.tag as Heading;
|
||||||
|
return <Tag key={index}>{serializedChildren}</Tag>;
|
||||||
|
}
|
||||||
|
case "linebreak": {
|
||||||
|
return <br key={index} />;
|
||||||
|
}
|
||||||
|
case "link": {
|
||||||
|
const node = _node as SerializedLinkNode;
|
||||||
|
|
||||||
|
const fields: LinkFields = node.fields;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CMSLink
|
||||||
|
key={index}
|
||||||
|
newTab={Boolean(fields?.newTab)}
|
||||||
|
reference={fields.doc as any}
|
||||||
|
type={fields.linkType === "internal" ? "reference" : "custom"}
|
||||||
|
url={fields.url}
|
||||||
|
>
|
||||||
|
{serializedChildren}
|
||||||
|
</CMSLink>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "list": {
|
||||||
|
const node = _node as SerializedListNode;
|
||||||
|
|
||||||
|
type List = Extract<keyof JSX.IntrinsicElements, "ol" | "ul">;
|
||||||
|
const Tag = node?.tag as List;
|
||||||
|
return (
|
||||||
|
<Tag className="list" key={index}>
|
||||||
|
{serializedChildren}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case "listitem": {
|
||||||
|
const node = _node as SerializedListItemNode;
|
||||||
|
|
||||||
|
if (node?.checked != null) {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
aria-checked={node.checked ? "true" : "false"}
|
||||||
|
className={` ${node.checked ? "" : ""}`}
|
||||||
|
key={index}
|
||||||
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role
|
||||||
|
role="checkbox"
|
||||||
|
tabIndex={-1}
|
||||||
|
value={node?.value}
|
||||||
|
>
|
||||||
|
{serializedChildren}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<li key={index} value={node?.value}>
|
||||||
|
{serializedChildren}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "paragraph": {
|
||||||
|
return <p key={index}>{serializedChildren}</p>;
|
||||||
|
}
|
||||||
|
case "quote": {
|
||||||
|
return <blockquote key={index}>{serializedChildren}</blockquote>;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
45
src/components/Blocks/Form/Select/index.tsx
Normal file
45
src/components/Blocks/Form/Select/index.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import type { SelectField } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
import type { Control, FieldErrorsImpl, FieldValues } from "react-hook-form";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Controller } from "react-hook-form";
|
||||||
|
import ReactSelect from "react-select";
|
||||||
|
|
||||||
|
import { Error } from "../Error";
|
||||||
|
import { Width } from "../Width";
|
||||||
|
|
||||||
|
export const Select: React.FC<
|
||||||
|
{
|
||||||
|
control: Control<FieldValues, any>;
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
|
} & SelectField
|
||||||
|
> = ({ name, control, errors, label, options, required, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className="form-group pr-4">
|
||||||
|
<label htmlFor={name}>{label}</label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name={name}
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<ReactSelect
|
||||||
|
classNamePrefix="rs"
|
||||||
|
inputId={name}
|
||||||
|
instanceId={name}
|
||||||
|
onChange={(val) => onChange(val ? val.value : "")}
|
||||||
|
options={options}
|
||||||
|
value={options.find((s) => s.value === value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
rules={{ required }}
|
||||||
|
/>
|
||||||
|
{required && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
);
|
||||||
|
};
|
123
src/components/Blocks/Form/State/index.module.scss
Normal file
123
src/components/Blocks/Form/State/index.module.scss
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
@use '../shared.scss';
|
||||||
|
|
||||||
|
.select {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: var(--base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reactSelect {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
div.rs__control {
|
||||||
|
@include shared.formInput;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__input-container {
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__value-container {
|
||||||
|
padding: 0;
|
||||||
|
> * {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__single-value {
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__indicators {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--base) * 0.9);
|
||||||
|
right: calc(var(--base) * 0.9);
|
||||||
|
.arrow {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__indicator {
|
||||||
|
padding: 0px 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
svg path {
|
||||||
|
fill: var(--color-dark-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
svg path {
|
||||||
|
fill: var(--color-dark-gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__indicator-separator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__menu {
|
||||||
|
color: var(--color-black);
|
||||||
|
background-color: var(--color-white);
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: 0 4px 11px hsl(0deg 0% 0% / 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__menu-list {
|
||||||
|
padding: calc(var(--base) / 4) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__group-heading {
|
||||||
|
margin-bottom: calc(var(--base) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__option {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: calc(var(--base) / 2) var(--base);
|
||||||
|
|
||||||
|
&--is-focused {
|
||||||
|
background-color: var(--color-light-gray);
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--is-selected {
|
||||||
|
background-color: var(--color-light-gray);
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__multi-value {
|
||||||
|
padding: 0;
|
||||||
|
background: var(--color-light-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__multi-value__label {
|
||||||
|
max-width: 150px;
|
||||||
|
color: var(--color-black);
|
||||||
|
padding: calc(var(--base) / 8) calc(var(--base) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__multi-value__remove {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-black);
|
||||||
|
background: var(--color-light-gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rs__clear-indicator {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/components/Blocks/Form/State/index.tsx
Normal file
50
src/components/Blocks/Form/State/index.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import type { StateField } from '@payloadcms/plugin-form-builder/types'
|
||||||
|
import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { Controller } from 'react-hook-form'
|
||||||
|
import ReactSelect from 'react-select'
|
||||||
|
|
||||||
|
import { Error } from '../Error'
|
||||||
|
import { Width } from '../Width'
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
import { stateOptions } from './options'
|
||||||
|
|
||||||
|
export const State: React.FC<
|
||||||
|
{
|
||||||
|
control: Control<FieldValues, any>
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any
|
||||||
|
}>
|
||||||
|
>
|
||||||
|
} & StateField
|
||||||
|
> = ({ name, control, errors, label, required, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className={classes.select}>
|
||||||
|
<label className={classes.label} htmlFor={name}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name={name}
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<ReactSelect
|
||||||
|
className={classes.reactSelect}
|
||||||
|
classNamePrefix="rs"
|
||||||
|
id={name}
|
||||||
|
instanceId={name}
|
||||||
|
onChange={(val) => onChange(val ? val.value : '')}
|
||||||
|
options={stateOptions}
|
||||||
|
value={stateOptions.find((t) => t.value === value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
rules={{ required }}
|
||||||
|
/>
|
||||||
|
{required && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
)
|
||||||
|
}
|
52
src/components/Blocks/Form/State/options.ts
Normal file
52
src/components/Blocks/Form/State/options.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
export const stateOptions = [
|
||||||
|
{ label: 'Alabama', value: 'AL' },
|
||||||
|
{ label: 'Alaska', value: 'AK' },
|
||||||
|
{ label: 'Arizona', value: 'AZ' },
|
||||||
|
{ label: 'Arkansas', value: 'AR' },
|
||||||
|
{ label: 'California', value: 'CA' },
|
||||||
|
{ label: 'Colorado', value: 'CO' },
|
||||||
|
{ label: 'Connecticut', value: 'CT' },
|
||||||
|
{ label: 'Delaware', value: 'DE' },
|
||||||
|
{ label: 'Florida', value: 'FL' },
|
||||||
|
{ label: 'Georgia', value: 'GA' },
|
||||||
|
{ label: 'Hawaii', value: 'HI' },
|
||||||
|
{ label: 'Idaho', value: 'ID' },
|
||||||
|
{ label: 'Illinois', value: 'IL' },
|
||||||
|
{ label: 'Indiana', value: 'IN' },
|
||||||
|
{ label: 'Iowa', value: 'IA' },
|
||||||
|
{ label: 'Kansas', value: 'KS' },
|
||||||
|
{ label: 'Kentucky', value: 'KY' },
|
||||||
|
{ label: 'Louisiana', value: 'LA' },
|
||||||
|
{ label: 'Maine', value: 'ME' },
|
||||||
|
{ label: 'Maryland', value: 'MD' },
|
||||||
|
{ label: 'Massachusetts', value: 'MA' },
|
||||||
|
{ label: 'Michigan', value: 'MI' },
|
||||||
|
{ label: 'Minnesota', value: 'MN' },
|
||||||
|
{ label: 'Mississippi', value: 'MS' },
|
||||||
|
{ label: 'Missouri', value: 'MO' },
|
||||||
|
{ label: 'Montana', value: 'MT' },
|
||||||
|
{ label: 'Nebraska', value: 'NE' },
|
||||||
|
{ label: 'Nevada', value: 'NV' },
|
||||||
|
{ label: 'New Hampshire', value: 'NH' },
|
||||||
|
{ label: 'New Jersey', value: 'NJ' },
|
||||||
|
{ label: 'New Mexico', value: 'NM' },
|
||||||
|
{ label: 'New York', value: 'NY' },
|
||||||
|
{ label: 'North Carolina', value: 'NC' },
|
||||||
|
{ label: 'North Dakota', value: 'ND' },
|
||||||
|
{ label: 'Ohio', value: 'OH' },
|
||||||
|
{ label: 'Oklahoma', value: 'OK' },
|
||||||
|
{ label: 'Oregon', value: 'OR' },
|
||||||
|
{ label: 'Pennsylvania', value: 'PA' },
|
||||||
|
{ label: 'Rhode Island', value: 'RI' },
|
||||||
|
{ label: 'South Carolina', value: 'SC' },
|
||||||
|
{ label: 'South Dakota', value: 'SD' },
|
||||||
|
{ label: 'Tennessee', value: 'TN' },
|
||||||
|
{ label: 'Texas', value: 'TX' },
|
||||||
|
{ label: 'Utah', value: 'UT' },
|
||||||
|
{ label: 'Vermont', value: 'VT' },
|
||||||
|
{ label: 'Virginia', value: 'VA' },
|
||||||
|
{ label: 'Washington', value: 'WA' },
|
||||||
|
{ label: 'West Virginia', value: 'WV' },
|
||||||
|
{ label: 'Wisconsin', value: 'WI' },
|
||||||
|
{ label: 'Wyoming', value: 'WY' },
|
||||||
|
]
|
33
src/components/Blocks/Form/Text/index.tsx
Normal file
33
src/components/Blocks/Form/Text/index.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import type { TextField } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
import type { FieldErrorsImpl, FieldValues, UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Error } from "../Error";
|
||||||
|
import { Width } from "../Width";
|
||||||
|
|
||||||
|
export const Text: React.FC<
|
||||||
|
{
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
|
register: UseFormRegister<any & FieldValues>;
|
||||||
|
} & TextField
|
||||||
|
> = ({ name, errors, label, register, required: requiredFromProps, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className="form-group pr-4">
|
||||||
|
<label htmlFor={name}>{label}</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id={name}
|
||||||
|
className="input-lg input-circle form-control"
|
||||||
|
{...register(name, { required: requiredFromProps })}
|
||||||
|
/>
|
||||||
|
{requiredFromProps && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
);
|
||||||
|
};
|
34
src/components/Blocks/Form/Textarea/index.tsx
Normal file
34
src/components/Blocks/Form/Textarea/index.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import type { TextField } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
import type { FieldErrorsImpl, FieldValues, UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Error } from "../Error";
|
||||||
|
import { Width } from "../Width";
|
||||||
|
|
||||||
|
export const Textarea: React.FC<
|
||||||
|
{
|
||||||
|
errors: Partial<
|
||||||
|
FieldErrorsImpl<{
|
||||||
|
[x: string]: any;
|
||||||
|
}>
|
||||||
|
>;
|
||||||
|
register: UseFormRegister<any & FieldValues>;
|
||||||
|
rows?: number;
|
||||||
|
} & TextField
|
||||||
|
> = ({ name, errors, label, register, required: requiredFromProps, rows = 3, width }) => {
|
||||||
|
return (
|
||||||
|
<Width width={width}>
|
||||||
|
<div className="form-group pr-4">
|
||||||
|
<label htmlFor={name}>{label}</label>
|
||||||
|
<textarea
|
||||||
|
className="input-lg input-circle form-control"
|
||||||
|
id={name}
|
||||||
|
rows={rows}
|
||||||
|
{...register(name, { required: requiredFromProps })}
|
||||||
|
/>
|
||||||
|
{requiredFromProps && errors[name] && <Error />}
|
||||||
|
</div>
|
||||||
|
</Width>
|
||||||
|
);
|
||||||
|
};
|
23
src/components/Blocks/Form/VerticalPadding/index.module.scss
Normal file
23
src/components/Blocks/Form/VerticalPadding/index.module.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.top-large {
|
||||||
|
padding-top: var(--block-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-medium {
|
||||||
|
padding-top: calc(var(--block-padding) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-small {
|
||||||
|
padding-top: calc(var(--block-padding) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-large {
|
||||||
|
padding-bottom: var(--block-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-medium {
|
||||||
|
padding-bottom: calc(var(--block-padding) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-small {
|
||||||
|
padding-bottom: calc(var(--block-padding) / 3);
|
||||||
|
}
|
29
src/components/Blocks/Form/VerticalPadding/index.tsx
Normal file
29
src/components/Blocks/Form/VerticalPadding/index.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
export type VerticalPaddingOptions = 'large' | 'medium' | 'none' | 'small'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
bottom?: VerticalPaddingOptions
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
top?: VerticalPaddingOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VerticalPadding: React.FC<Props> = ({
|
||||||
|
bottom = 'medium',
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
top = 'medium',
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={[className, classes[`top-${top}`], classes[`bottom-${bottom}`]]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
10
src/components/Blocks/Form/Width/index.module.scss
Normal file
10
src/components/Blocks/Form/Width/index.module.scss
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
@use "../../css/queries.scss" as *;
|
||||||
|
|
||||||
|
.width {
|
||||||
|
padding-left: calc(var(--base) * 0.5);
|
||||||
|
padding-right: calc(var(--base) * 0.5);
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
14
src/components/Blocks/Form/Width/index.tsx
Normal file
14
src/components/Blocks/Form/Width/index.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import classes from './index.module.scss'
|
||||||
|
|
||||||
|
export const Width: React.FC<{
|
||||||
|
children: React.ReactNode
|
||||||
|
width?: number
|
||||||
|
}> = ({ children, width }) => {
|
||||||
|
return (
|
||||||
|
<div className={classes.width} style={{ width: width ? `${width}%` : undefined }}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
43
src/components/Blocks/Form/buildInitialFormState.tsx
Normal file
43
src/components/Blocks/Form/buildInitialFormState.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import type { FormFieldBlock } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
|
||||||
|
export const buildInitialFormState = (fields: FormFieldBlock[]) => {
|
||||||
|
return fields.reduce((initialSchema, field) => {
|
||||||
|
if (field.blockType === "checkbox") {
|
||||||
|
return {
|
||||||
|
...initialSchema,
|
||||||
|
[field.name]: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (field.blockType === "country") {
|
||||||
|
return {
|
||||||
|
...initialSchema,
|
||||||
|
[field.name]: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (field.blockType === "email") {
|
||||||
|
return {
|
||||||
|
...initialSchema,
|
||||||
|
[field.name]: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (field.blockType === "text") {
|
||||||
|
return {
|
||||||
|
...initialSchema,
|
||||||
|
[field.name]: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (field.blockType === "select") {
|
||||||
|
return {
|
||||||
|
...initialSchema,
|
||||||
|
[field.name]: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (field.blockType === "state") {
|
||||||
|
return {
|
||||||
|
...initialSchema,
|
||||||
|
[field.name]: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, {});
|
||||||
|
};
|
21
src/components/Blocks/Form/fields.tsx
Normal file
21
src/components/Blocks/Form/fields.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Checkbox } from './Checkbox'
|
||||||
|
import { Country } from './Country'
|
||||||
|
import { Email } from './Email'
|
||||||
|
import { Message } from './Message'
|
||||||
|
import { Number } from './Number'
|
||||||
|
import { Select } from './Select'
|
||||||
|
import { State } from './State'
|
||||||
|
import { Text } from './Text'
|
||||||
|
import { Textarea } from './Textarea'
|
||||||
|
|
||||||
|
export const fields = {
|
||||||
|
checkbox: Checkbox,
|
||||||
|
country: Country,
|
||||||
|
email: Email,
|
||||||
|
message: Message,
|
||||||
|
number: Number,
|
||||||
|
select: Select,
|
||||||
|
state: State,
|
||||||
|
text: Text,
|
||||||
|
textarea: Textarea,
|
||||||
|
}
|
69
src/components/Blocks/Form/index.module.scss
Normal file
69
src/components/Blocks/Form/index.module.scss
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
@use "../css/queries.scss" as *;
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasSubmitted {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 80vh;
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro {
|
||||||
|
margin-bottom: var(--base);
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
margin-bottom: var(--base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmationMessage {
|
||||||
|
max-width: 800px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fieldWrap {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-left: calc(var(--base) * -0.5);
|
||||||
|
margin-right: calc(var(--base) * -0.5);
|
||||||
|
width: calc(100% + #{var(--base)});
|
||||||
|
|
||||||
|
> * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.Select {
|
||||||
|
& label {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--base) * -0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarContent {
|
||||||
|
@include mid-break {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobileSidebar {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: calc(var(--base) * 2);
|
||||||
|
}
|
||||||
|
}
|
169
src/components/Blocks/Form/index.tsx
Normal file
169
src/components/Blocks/Form/index.tsx
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import type { Form as FormType } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import React, { useCallback, useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { Form } from "@/payload-types";
|
||||||
|
import { Button } from "./Button";
|
||||||
|
import RichText from "./RichText";
|
||||||
|
import { buildInitialFormState } from "./buildInitialFormState";
|
||||||
|
import { fields } from "./fields";
|
||||||
|
|
||||||
|
export type Value = unknown;
|
||||||
|
|
||||||
|
export interface Property {
|
||||||
|
[key: string]: Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Data {
|
||||||
|
[key: string]: Property | Property[] | Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormBlockType = {
|
||||||
|
blockName?: string;
|
||||||
|
blockType?: "formBlock";
|
||||||
|
enableIntro: boolean;
|
||||||
|
form: FormType | Form;
|
||||||
|
introContent?: {
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FormBlock: React.FC<
|
||||||
|
FormBlockType & {
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
> = (props) => {
|
||||||
|
const {
|
||||||
|
enableIntro,
|
||||||
|
form: formFromProps,
|
||||||
|
form: { id: formID, confirmationMessage, confirmationType, redirect, submitButtonLabel } = {},
|
||||||
|
introContent,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const formMethods = useForm({
|
||||||
|
defaultValues: buildInitialFormState(formFromProps.fields),
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
formState: { errors },
|
||||||
|
getValues,
|
||||||
|
handleSubmit,
|
||||||
|
register,
|
||||||
|
setValue,
|
||||||
|
} = formMethods;
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [hasSubmitted, setHasSubmitted] = useState<boolean>();
|
||||||
|
const [error, setError] = useState<{ message: string; status?: string } | undefined>();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const onSubmit = useCallback(
|
||||||
|
(data: Data) => {
|
||||||
|
let loadingTimerID: ReturnType<typeof setTimeout>;
|
||||||
|
const submitForm = async () => {
|
||||||
|
setError(undefined);
|
||||||
|
|
||||||
|
const dataToSend = Object.entries(data).map(([name, value]) => ({
|
||||||
|
field: name,
|
||||||
|
value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// delay loading indicator by 1s
|
||||||
|
loadingTimerID = setTimeout(() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const req = await fetch(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/form-submissions`, {
|
||||||
|
body: JSON.stringify({
|
||||||
|
form: formID,
|
||||||
|
submissionData: dataToSend,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await req.json();
|
||||||
|
|
||||||
|
clearTimeout(loadingTimerID);
|
||||||
|
|
||||||
|
if (req.status >= 400) {
|
||||||
|
setIsLoading(false);
|
||||||
|
|
||||||
|
setError({
|
||||||
|
message: res.errors?.[0]?.message || "Internal Server Error",
|
||||||
|
status: res.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
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.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void submitForm();
|
||||||
|
},
|
||||||
|
[router, formID, redirect, confirmationType]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{enableIntro && introContent && !hasSubmitted && <RichText content={introContent} />}
|
||||||
|
{!isLoading && hasSubmitted && confirmationType === "message" && <RichText content={confirmationMessage} />}
|
||||||
|
{isLoading && !hasSubmitted && <p>Loading, please wait...</p>}
|
||||||
|
{error && <div className="text-red-500">{`${error.status || "500"}: ${error.message || ""}`}</div>}
|
||||||
|
{!hasSubmitted && (
|
||||||
|
<form id={formID} onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="form">
|
||||||
|
{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}
|
||||||
|
control={control}
|
||||||
|
errors={errors}
|
||||||
|
register={register}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<Button appearance="primary" el="button" form={formID} label={submitButtonLabel} />
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
42
src/components/Blocks/Form/shared.scss
Normal file
42
src/components/Blocks/Form/shared.scss
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
@use "../css/common.scss" as *;
|
||||||
|
|
||||||
|
@mixin formInput() {
|
||||||
|
all: unset;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: var(--font-body);
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--color-black);
|
||||||
|
background: var(--color-white);
|
||||||
|
color: var(--color-black);
|
||||||
|
font-size: 1rem;
|
||||||
|
height: calc(var(--base) * 2.5);
|
||||||
|
line-height: var(--base);
|
||||||
|
padding: calc(var(--base) * 0.75);
|
||||||
|
|
||||||
|
&::-moz-placeholder,
|
||||||
|
&::-webkit-input-placeholder {
|
||||||
|
color: var(--color-mid-gray);
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-mid-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
border-color: var(--color-gray);
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: var(--color-light-gray);
|
||||||
|
color: var(--color-gray);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-light-gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
129
src/components/Blocks/css/app.scss
Normal file
129
src/components/Blocks/css/app.scss
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
@use './queries.scss' as *;
|
||||||
|
@use './colors.scss' as *;
|
||||||
|
@use './type.scss' as *;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--breakpoint-xs-width: #{$breakpoint-xs-width};
|
||||||
|
--breakpoint-s-width: #{$breakpoint-s-width};
|
||||||
|
--breakpoint-m-width: #{$breakpoint-m-width};
|
||||||
|
--breakpoint-l-width: #{$breakpoint-l-width};
|
||||||
|
--scrollbar-width: 17px;
|
||||||
|
|
||||||
|
--base: 24px;
|
||||||
|
--font-body: system-ui;
|
||||||
|
--font-mono: 'Roboto Mono', monospace;
|
||||||
|
|
||||||
|
--gutter-h: 180px;
|
||||||
|
--block-padding: 120px;
|
||||||
|
|
||||||
|
--header-z-index: 100;
|
||||||
|
--modal-z-index: 90;
|
||||||
|
|
||||||
|
@include large-break {
|
||||||
|
--gutter-h: 144px;
|
||||||
|
--block-padding: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
--gutter-h: 24px;
|
||||||
|
--block-padding: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// GLOBAL STYLES
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
@extend %body;
|
||||||
|
background: var(--color-white);
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
color: var(--color-black);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: var(--color-green);
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-selection {
|
||||||
|
background: var(--color-green);
|
||||||
|
color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@extend %h1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@extend %h2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
@extend %h3;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
@extend %h4;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
@extend %h5;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
@extend %h6;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: var(--base) 0;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
margin: calc(var(--base) * 0.75) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
padding-left: var(--base);
|
||||||
|
margin: 0 0 var(--base);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: currentColor;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
opacity: 0.8;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.7;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
10
src/components/Blocks/css/colors.scss
Normal file
10
src/components/Blocks/css/colors.scss
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
:root {
|
||||||
|
--color-red: rgb(255, 0, 0);
|
||||||
|
--color-green: rgb(178, 255, 214);
|
||||||
|
--color-white: rgb(255, 255, 255);
|
||||||
|
--color-dark-gray: rgb(51, 52, 52);
|
||||||
|
--color-mid-gray: rgb(196, 196, 196);
|
||||||
|
--color-gray: rgb(212, 212, 212);
|
||||||
|
--color-light-gray: rgb(244, 244, 244);
|
||||||
|
--color-black: rgb(0, 0, 0);
|
||||||
|
}
|
2
src/components/Blocks/css/common.scss
Normal file
2
src/components/Blocks/css/common.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@forward './queries.scss';
|
||||||
|
@forward './type.scss';
|
32
src/components/Blocks/css/queries.scss
Normal file
32
src/components/Blocks/css/queries.scss
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
$breakpoint-xs-width: 400px;
|
||||||
|
$breakpoint-s-width: 768px;
|
||||||
|
$breakpoint-m-width: 1024px;
|
||||||
|
$breakpoint-l-width: 1440px;
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// MEDIA QUERIES
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
@mixin extra-small-break {
|
||||||
|
@media (max-width: #{$breakpoint-xs-width}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin small-break {
|
||||||
|
@media (max-width: #{$breakpoint-s-width}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin mid-break {
|
||||||
|
@media (max-width: #{$breakpoint-m-width}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin large-break {
|
||||||
|
@media (max-width: #{$breakpoint-l-width}) {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
167
src/components/Blocks/css/type.scss
Normal file
167
src/components/Blocks/css/type.scss
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
@use 'queries' as *;
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// HEADINGS
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
%h1,
|
||||||
|
%h2,
|
||||||
|
%h3,
|
||||||
|
%h4,
|
||||||
|
%h5,
|
||||||
|
%h6 {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
%h1 {
|
||||||
|
margin: 50px 0;
|
||||||
|
font-size: 84px;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
margin: 24px 0;
|
||||||
|
font-size: 36px;
|
||||||
|
line-height: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%h2 {
|
||||||
|
margin: 32px 0;
|
||||||
|
font-size: 56px;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
margin: 36px 0;
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
margin: 24px 0;
|
||||||
|
font-size: 28px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%h3 {
|
||||||
|
margin: 28px 0;
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 56px;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
margin: 24px 0;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%h4 {
|
||||||
|
margin: 24px 0;
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: 48px;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 33px;
|
||||||
|
line-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%h5 {
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 42px;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 26px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
margin: 16px 0;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%h6 {
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 28px;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
margin: 16px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// TYPE STYLES
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
%body {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 32px;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%large-body {
|
||||||
|
font-size: 25px;
|
||||||
|
line-height: 32px;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
font-size: 17px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%label {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
@include mid-break {
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 2.75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include small-break {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
letter-spacing: 2.625px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,15 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { contactItems } from "@/data/contact";
|
import { contactItems } from "@/data/contact";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { FormBlock } from "../Blocks/Form";
|
||||||
|
import { Form as FormType } from "@payloadcms/plugin-form-builder/types";
|
||||||
|
import { Form } from "@/payload-types";
|
||||||
|
|
||||||
export default function Contact() {
|
export interface ContactProps {
|
||||||
|
form?: FormType | Form;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Contact({ form }: ContactProps) {
|
||||||
return (
|
return (
|
||||||
<div className="container position-relative">
|
<div className="container position-relative">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@ -51,12 +58,12 @@ export default function Contact() {
|
|||||||
<div className="col-lg-8 col-xl-7 offset-xl-1 wow fadeInUp">
|
<div className="col-lg-8 col-xl-7 offset-xl-1 wow fadeInUp">
|
||||||
<div className="row g-0">
|
<div className="row g-0">
|
||||||
{/* Google Map Column */}
|
{/* Google Map Column */}
|
||||||
<div className="col-md-5 d-flex align-items-stretch pt-40 pt-sm-0 pb-40 pb-sm-0 mb-sm-30">
|
<div className="col-12 d-flex align-items-stretch pt-40 pt-sm-0 pb-40 pb-sm-0 mb-sm-30">
|
||||||
<div className="map-boxed-1 d-flex align-items-stretch">
|
<div className="map-boxed-1 d-flex align-items-stretch h-[400px]">
|
||||||
<iframe
|
<iframe
|
||||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3399.869553403109!2d-110.24257920000001!3d31.555194399999998!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x86d7292ebdc13925%3A0x43d6cab7d0f93f14!2s5151%20AZ-90%2C%20Sierra%20Vista%2C%20AZ%2085635%2C%20USA!5e0!3m2!1sen!2sid!4v1738604989180!5m2!1sen!2sid"
|
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3399.869553403109!2d-110.24257920000001!3d31.555194399999998!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x86d7292ebdc13925%3A0x43d6cab7d0f93f14!2s5151%20AZ-90%2C%20Sierra%20Vista%2C%20AZ%2085635%2C%20USA!5e0!3m2!1sen!2sid!4v1738604989180!5m2!1sen!2sid"
|
||||||
width={600}
|
width={600}
|
||||||
height={450}
|
height={600}
|
||||||
style={{ border: 0 }}
|
style={{ border: 0 }}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
referrerPolicy="no-referrer-when-downgrade"
|
referrerPolicy="no-referrer-when-downgrade"
|
||||||
@ -64,86 +71,19 @@ export default function Contact() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* End Google Map Column */}
|
{/* End Google Map Column */}
|
||||||
{/* Contact Form Column */}
|
|
||||||
<div className="col-md-7">
|
|
||||||
<div className="box-shadow bg-white round p-4 p-sm-5 position-relative z-index-1">
|
|
||||||
<h4 className="h3 mb-40 mb-sm-30">Get in Touch</h4>
|
|
||||||
{/* Contact Form */}
|
|
||||||
<form onSubmit={(e) => e.preventDefault()} className="form contact-form" id="contact_form">
|
|
||||||
{/* Name */}
|
|
||||||
<div className="form-group">
|
|
||||||
<label htmlFor="name" className="visually-hidden">
|
|
||||||
Name
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
id="name"
|
|
||||||
className="input-lg input-circle form-control"
|
|
||||||
placeholder="Name"
|
|
||||||
pattern=".{3,100}"
|
|
||||||
required
|
|
||||||
aria-required="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/* End Name */}
|
|
||||||
{/* Email */}
|
|
||||||
<div className="form-group">
|
|
||||||
<label htmlFor="email" className="visually-hidden">
|
|
||||||
Email
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
name="email"
|
|
||||||
id="email"
|
|
||||||
className="input-lg input-circle form-control"
|
|
||||||
placeholder="Email"
|
|
||||||
pattern=".{5,100}"
|
|
||||||
required
|
|
||||||
aria-required="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/* End Email */}
|
|
||||||
{/* Message */}
|
|
||||||
<div className="form-group">
|
|
||||||
<label htmlFor="message" className="visually-hidden">
|
|
||||||
Message
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
name="message"
|
|
||||||
id="message"
|
|
||||||
className="input-lg input-circle form-control"
|
|
||||||
style={{ height: 130 }}
|
|
||||||
placeholder="Message"
|
|
||||||
defaultValue={""}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/* Send Button */}
|
|
||||||
<button
|
|
||||||
className="submit_btn btn btn-mod btn-color btn-large btn-full btn-circle btn-hover-anim"
|
|
||||||
id="submit_btn"
|
|
||||||
aria-controls="result"
|
|
||||||
>
|
|
||||||
<span>Send Message</span>
|
|
||||||
</button>
|
|
||||||
{/* End Send Button */}
|
|
||||||
{/* Inform Tip */}
|
|
||||||
<div className="form-tip w-100 pt-30 mt-sm-20">
|
|
||||||
<i className="icon-info size-16" />
|
|
||||||
All the fields are required. By sending the form you agree to the{" "}
|
|
||||||
<a href="#">Terms & Conditions</a> and <a href="#">Privacy Policy</a>.
|
|
||||||
</div>
|
|
||||||
{/* End Inform Tip */}
|
|
||||||
<div id="result" role="region" aria-live="polite" aria-atomic="true" />
|
|
||||||
</form>
|
|
||||||
{/* End Contact Form */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* End Contact Form Column */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* End Right Column */}
|
{/* End Right Column */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!!form && (
|
||||||
|
<div className="box-shadow bg-white round p-4 p-sm-5 position-relative z-index-1">
|
||||||
|
<h4 className="h3 mb-40 mb-sm-30">{form?.title ?? ""}</h4>
|
||||||
|
<div>
|
||||||
|
<FormBlock enableIntro={true} form={form} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ export interface Config {
|
|||||||
users: User;
|
users: User;
|
||||||
media: Media;
|
media: Media;
|
||||||
blogs: Blog;
|
blogs: Blog;
|
||||||
|
interests: Interest;
|
||||||
|
forms: Form;
|
||||||
|
'form-submissions': FormSubmission;
|
||||||
'payload-locked-documents': PayloadLockedDocument;
|
'payload-locked-documents': PayloadLockedDocument;
|
||||||
'payload-preferences': PayloadPreference;
|
'payload-preferences': PayloadPreference;
|
||||||
'payload-migrations': PayloadMigration;
|
'payload-migrations': PayloadMigration;
|
||||||
@ -23,6 +26,9 @@ export interface Config {
|
|||||||
users: UsersSelect<false> | UsersSelect<true>;
|
users: UsersSelect<false> | UsersSelect<true>;
|
||||||
media: MediaSelect<false> | MediaSelect<true>;
|
media: MediaSelect<false> | MediaSelect<true>;
|
||||||
blogs: BlogsSelect<false> | BlogsSelect<true>;
|
blogs: BlogsSelect<false> | BlogsSelect<true>;
|
||||||
|
interests: InterestsSelect<false> | InterestsSelect<true>;
|
||||||
|
forms: FormsSelect<false> | FormsSelect<true>;
|
||||||
|
'form-submissions': FormSubmissionsSelect<false> | FormSubmissionsSelect<true>;
|
||||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||||
@ -30,8 +36,12 @@ export interface Config {
|
|||||||
db: {
|
db: {
|
||||||
defaultIDType: number;
|
defaultIDType: number;
|
||||||
};
|
};
|
||||||
globals: {};
|
globals: {
|
||||||
globalsSelect: {};
|
'consultation-datetime': ConsultationDatetime;
|
||||||
|
};
|
||||||
|
globalsSelect: {
|
||||||
|
'consultation-datetime': ConsultationDatetimeSelect<false> | ConsultationDatetimeSelect<true>;
|
||||||
|
};
|
||||||
locale: null;
|
locale: null;
|
||||||
user: User & {
|
user: User & {
|
||||||
collection: 'users';
|
collection: 'users';
|
||||||
@ -123,6 +133,188 @@ export interface Blog {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This is the procedure interests that can be selected when request a consultation
|
||||||
|
*
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "interests".
|
||||||
|
*/
|
||||||
|
export interface Interest {
|
||||||
|
id: number;
|
||||||
|
interest: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "forms".
|
||||||
|
*/
|
||||||
|
export interface Form {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
fields?:
|
||||||
|
| (
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
defaultValue?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'checkbox';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'country';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'email';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
defaultValue?: number | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'number';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
defaultValue?: string | null;
|
||||||
|
options?:
|
||||||
|
| {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
required?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'select';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'state';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
defaultValue?: string | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'text';
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
label?: string | null;
|
||||||
|
width?: number | null;
|
||||||
|
defaultValue?: string | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'textarea';
|
||||||
|
}
|
||||||
|
)[]
|
||||||
|
| null;
|
||||||
|
submitButtonLabel?: string | null;
|
||||||
|
/**
|
||||||
|
* Choose whether to display an on-page message or redirect to a different page after they submit the form.
|
||||||
|
*/
|
||||||
|
confirmationType?: ('message' | 'redirect') | null;
|
||||||
|
confirmationMessage?: {
|
||||||
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: string;
|
||||||
|
version: number;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
|
redirect?: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 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}}. You can use a wildcard {{*}} to output all data and {{*:table}} to format it as an HTML table in the email.
|
||||||
|
*/
|
||||||
|
emails?:
|
||||||
|
| {
|
||||||
|
emailTo?: string | null;
|
||||||
|
cc?: string | null;
|
||||||
|
bcc?: string | null;
|
||||||
|
replyTo?: string | null;
|
||||||
|
emailFrom?: string | null;
|
||||||
|
subject: string;
|
||||||
|
/**
|
||||||
|
* Enter the message that should be sent in this email.
|
||||||
|
*/
|
||||||
|
message?: {
|
||||||
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: string;
|
||||||
|
version: number;
|
||||||
|
[k: string]: unknown;
|
||||||
|
}[];
|
||||||
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "form-submissions".
|
||||||
|
*/
|
||||||
|
export interface FormSubmission {
|
||||||
|
id: number;
|
||||||
|
form: number | Form;
|
||||||
|
submissionData?:
|
||||||
|
| {
|
||||||
|
field: string;
|
||||||
|
value: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-locked-documents".
|
* via the `definition` "payload-locked-documents".
|
||||||
@ -141,6 +333,18 @@ export interface PayloadLockedDocument {
|
|||||||
| ({
|
| ({
|
||||||
relationTo: 'blogs';
|
relationTo: 'blogs';
|
||||||
value: number | Blog;
|
value: number | Blog;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'interests';
|
||||||
|
value: number | Interest;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'forms';
|
||||||
|
value: number | Form;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'form-submissions';
|
||||||
|
value: number | FormSubmission;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
@ -230,6 +434,156 @@ export interface BlogsSelect<T extends boolean = true> {
|
|||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "interests_select".
|
||||||
|
*/
|
||||||
|
export interface InterestsSelect<T extends boolean = true> {
|
||||||
|
interest?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "forms_select".
|
||||||
|
*/
|
||||||
|
export interface FormsSelect<T extends boolean = true> {
|
||||||
|
title?: T;
|
||||||
|
fields?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
checkbox?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
required?: T;
|
||||||
|
defaultValue?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
country?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
required?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
email?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
required?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
number?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
defaultValue?: T;
|
||||||
|
required?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
select?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
defaultValue?: T;
|
||||||
|
options?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
label?: T;
|
||||||
|
value?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
required?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
state?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
required?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
text?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
defaultValue?: T;
|
||||||
|
required?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
textarea?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
label?: T;
|
||||||
|
width?: T;
|
||||||
|
defaultValue?: T;
|
||||||
|
required?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
submitButtonLabel?: T;
|
||||||
|
confirmationType?: T;
|
||||||
|
confirmationMessage?: T;
|
||||||
|
redirect?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
url?: T;
|
||||||
|
};
|
||||||
|
emails?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
emailTo?: T;
|
||||||
|
cc?: T;
|
||||||
|
bcc?: T;
|
||||||
|
replyTo?: T;
|
||||||
|
emailFrom?: T;
|
||||||
|
subject?: T;
|
||||||
|
message?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "form-submissions_select".
|
||||||
|
*/
|
||||||
|
export interface FormSubmissionsSelect<T extends boolean = true> {
|
||||||
|
form?: T;
|
||||||
|
submissionData?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
field?: T;
|
||||||
|
value?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-locked-documents_select".
|
* via the `definition` "payload-locked-documents_select".
|
||||||
@ -262,6 +616,60 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
|||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Date and time available for consultation
|
||||||
|
*
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "consultation-datetime".
|
||||||
|
*/
|
||||||
|
export interface ConsultationDatetime {
|
||||||
|
id: number;
|
||||||
|
day?: {
|
||||||
|
monday?: boolean | null;
|
||||||
|
tuesday?: boolean | null;
|
||||||
|
wednesday?: boolean | null;
|
||||||
|
thursday?: boolean | null;
|
||||||
|
friday?: boolean | null;
|
||||||
|
saturday?: boolean | null;
|
||||||
|
};
|
||||||
|
time?:
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
start: string;
|
||||||
|
end: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt?: string | null;
|
||||||
|
createdAt?: string | null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "consultation-datetime_select".
|
||||||
|
*/
|
||||||
|
export interface ConsultationDatetimeSelect<T extends boolean = true> {
|
||||||
|
day?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
monday?: T;
|
||||||
|
tuesday?: T;
|
||||||
|
wednesday?: T;
|
||||||
|
thursday?: T;
|
||||||
|
friday?: T;
|
||||||
|
saturday?: T;
|
||||||
|
};
|
||||||
|
time?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
name?: T;
|
||||||
|
start?: T;
|
||||||
|
end?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
globalType?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "auth".
|
* via the `definition` "auth".
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { postgresAdapter } from "@payloadcms/db-postgres";
|
import { postgresAdapter } from "@payloadcms/db-postgres";
|
||||||
import { payloadCloudPlugin } from "@payloadcms/payload-cloud";
|
import { payloadCloudPlugin } from "@payloadcms/payload-cloud";
|
||||||
import { s3Storage } from "@payloadcms/storage-s3";
|
import { s3Storage } from "@payloadcms/storage-s3";
|
||||||
|
import { formBuilderPlugin } from "@payloadcms/plugin-form-builder";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { buildConfig } from "payload";
|
import { buildConfig } from "payload";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
@ -12,6 +13,15 @@ import { Media } from "@/collections/Media";
|
|||||||
import { Users } from "@/collections/Users";
|
import { Users } from "@/collections/Users";
|
||||||
import { Interests } from "@/collections/consultations/Interests";
|
import { Interests } from "@/collections/consultations/Interests";
|
||||||
import { ConsultationDateTime } from "@/collections/consultations/consultation-datetime";
|
import { ConsultationDateTime } from "@/collections/consultations/consultation-datetime";
|
||||||
|
import {
|
||||||
|
BoldFeature,
|
||||||
|
FixedToolbarFeature,
|
||||||
|
HeadingFeature,
|
||||||
|
InlineToolbarFeature,
|
||||||
|
ItalicFeature,
|
||||||
|
lexicalEditor,
|
||||||
|
LinkFeature,
|
||||||
|
} from "@payloadcms/richtext-lexical";
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url);
|
const filename = fileURLToPath(import.meta.url);
|
||||||
const dirname = path.dirname(filename);
|
const dirname = path.dirname(filename);
|
||||||
@ -48,6 +58,18 @@ export default buildConfig({
|
|||||||
connectionString: process.env.DATABASE_URI || "",
|
connectionString: process.env.DATABASE_URI || "",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
editor: lexicalEditor({
|
||||||
|
features: () => {
|
||||||
|
return [
|
||||||
|
BoldFeature(),
|
||||||
|
ItalicFeature(),
|
||||||
|
LinkFeature({ enabledCollections: ["blogs"] }),
|
||||||
|
HeadingFeature({ enabledHeadingSizes: ["h1", "h2", "h3"] }),
|
||||||
|
FixedToolbarFeature(),
|
||||||
|
InlineToolbarFeature(),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}),
|
||||||
sharp,
|
sharp,
|
||||||
plugins: [
|
plugins: [
|
||||||
payloadCloudPlugin(),
|
payloadCloudPlugin(),
|
||||||
@ -69,5 +91,23 @@ export default buildConfig({
|
|||||||
endpoint: process.env.S3_ENDPOINT,
|
endpoint: process.env.S3_ENDPOINT,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
// form builder
|
||||||
|
formBuilderPlugin({
|
||||||
|
fields: {
|
||||||
|
text: true,
|
||||||
|
textarea: true,
|
||||||
|
select: true,
|
||||||
|
email: true,
|
||||||
|
state: true,
|
||||||
|
country: true,
|
||||||
|
checkbox: true,
|
||||||
|
number: true,
|
||||||
|
message: false,
|
||||||
|
payment: false,
|
||||||
|
},
|
||||||
|
formOverrides: {
|
||||||
|
fields: undefined,
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
12
src/services/payload/form.ts
Normal file
12
src/services/payload/form.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import payloadConfig from "@/payload.config";
|
||||||
|
import { getPayload } from "payload";
|
||||||
|
|
||||||
|
export async function fetchForm(formID: number) {
|
||||||
|
const payload = await getPayload({ config: payloadConfig });
|
||||||
|
const formRes = await payload.findByID({
|
||||||
|
collection: "forms",
|
||||||
|
id: formID,
|
||||||
|
});
|
||||||
|
|
||||||
|
return formRes;
|
||||||
|
}
|
0
src/utils/general.ts
Normal file
0
src/utils/general.ts
Normal file
5
src/utils/toKebabCase.ts
Normal file
5
src/utils/toKebabCase.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const toKebabCase = (string) =>
|
||||||
|
string
|
||||||
|
?.replace(/([a-z])([A-Z])/g, '$1-$2')
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.toLowerCase()
|
Loading…
x
Reference in New Issue
Block a user