Skip to content

Commit

Permalink
Merge pull request #155 from SocialGouv/fix/offer-use-form
Browse files Browse the repository at this point in the history
feat: payload form builder integration
  • Loading branch information
ClementNumericite authored Nov 28, 2024
2 parents 2153528 + 76ee623 commit d853e84
Show file tree
Hide file tree
Showing 19 changed files with 850 additions and 68 deletions.
3 changes: 1 addition & 2 deletions webapp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

### Bug Fixes

- wallet history button disappear when wallet is empty ([f0477c4](https://github.com/SocialGouv/carte-jeune-engage/commit/f0477c44279b5ddb5d85deff09d5eb095cf97574))
- router push dashboard before ([4651d31](https://github.com/SocialGouv/carte-jeune-engage/commit/4651d31d7a442a549565c2756f3d07bcd5169892))

* wallet history button disappear when wallet is empty ([f0477c4](https://github.com/SocialGouv/carte-jeune-engage/commit/f0477c44279b5ddb5d85deff09d5eb095cf97574))

## [0.70.25](https://github.com/SocialGouv/carte-jeune-engage/compare/v0.70.24...v0.70.25) (2024-11-22)

### Bug Fixes
Expand Down
2 changes: 2 additions & 0 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"start": "next start",
"seed:dev": "PAYLOAD_DROP_DATABASE=true tsx ./src/payload/seed/index.ts",
"seed:prod": "tsx ./src/payload/seed/index.ts",
"seed:forms": "tsx ./src/payload/seed/forms/index.ts",
"cron-job": "tsx ./src/notifications/index.ts",
"payload": "PAYLOAD_CONFIG_PATH=./src/payload/payload.config.ts payload",
"format": "prettier --write .",
Expand All @@ -29,6 +30,7 @@
"@payloadcms/db-postgres": "^0.7.0",
"@payloadcms/next-payload": "^0.1.11",
"@payloadcms/plugin-cloud-storage": "^1.1.1",
"@payloadcms/plugin-form-builder": "1.2.1",
"@payloadcms/richtext-slate": "^1.3.1",
"@sentry/nextjs": "^7.100.1",
"@serwist/next": "^9.0.2",
Expand Down
8 changes: 5 additions & 3 deletions webapp/src/components/forms/FormInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ const FormInput = ({
: {}
}
>
<FormLabel fontWeight="medium" color="disabled" fontSize={12} mb={1}>
{label}
</FormLabel>
{label && (
<FormLabel fontWeight="medium" color="disabled" fontSize={12} mb={1}>
{label}
</FormLabel>
)}
<Input
id={name}
as={kind === "textarea" ? Textarea : Input}
Expand Down
179 changes: 179 additions & 0 deletions webapp/src/components/forms/payload/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import React, { useState, useCallback } from "react";
import { buildInitialFormState } from "./buildInitialFormState";
import { fields } from "./fields";
import { Form as FormType } from "@payloadcms/plugin-form-builder/dist/types";
import { useForm } from "react-hook-form";
import { useRouter } from "next/router";
import {
Box,
ButtonGroup,
Flex,
Icon,
IconButton,
Text,
} from "@chakra-ui/react";
import { api } from "~/utils/api";
import { HiArrowLeft, HiArrowRight } from "react-icons/hi2";

export type Value = unknown;

export interface Property {
[key: string]: Value;
}

export interface Data {
[key: string]: Value | Property | Property[];
}

export type FormBlockType = {
blockName?: string;
blockType?: "formBlock";
enableIntro: Boolean;
form: FormType;
};

export const FormBlock: React.FC<
FormBlockType & {
id?: string;
afterOnSubmit: () => void;
}
> = (props) => {
const {
form: formFromProps,
form: { id: formID, confirmationType, redirect } = {},
afterOnSubmit,
} = props;

const formMethods = useForm({
defaultValues: buildInitialFormState(formFromProps.fields),
});
const {
register,
handleSubmit,
formState: { errors },
control,
} = formMethods;

const { mutateAsync } = api.form.submitForm.useMutation();

const [currentStep, setCurrentStep] = useState(0);

const [isLoading, setIsLoading] = useState(false);
const [hasSubmitted, setHasSubmitted] = useState<boolean>();
const [error, setError] = useState<
{ status?: string; message: string } | undefined
>();
const router = useRouter();

const handleNextStep = () => {
if (currentStep === formFromProps.fields.length - 1) {
onSubmit(formMethods.getValues());
} else {
setCurrentStep((prev) => prev + 1);
}
};

// const handlePrevStep = () => {
// setCurrentStep((prev) => prev - 1);
// };

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,
})) as { field: string; value: string }[];

loadingTimerID = setTimeout(() => {
setIsLoading(true);
}, 1000);

try {
await mutateAsync({
formId: formID as unknown as number,
submissionData: dataToSend,
});

clearTimeout(loadingTimerID);

setIsLoading(false);
setHasSubmitted(true);

afterOnSubmit();
} catch (err) {
console.warn(err);
setIsLoading(false);
setError({
message: "Something went wrong.",
});
}
};

submitForm();
},
[router, formID, redirect, confirmationType]
);

return (
<div>
{error && <div>{`${error.status || "500"}: ${error.message || ""}`}</div>}
{!hasSubmitted && (
<Box as="form" id={formID} onSubmit={handleSubmit(onSubmit)} w="full">
<div>
{formFromProps &&
formFromProps.fields &&
(() => {
const field = formFromProps.fields[currentStep];
const Field: React.FC<any> =
fields?.[field.blockType as keyof typeof fields];
return (
<Flex flexDir="column" gap={6}>
{"label" in field && (
<Text fontSize={24} fontWeight={800} textAlign="center">
{field.label}
</Text>
)}
<React.Fragment key={field.blockName}>
<Field
form={formFromProps}
field={{ ...field, label: undefined }}
{...formMethods}
register={register}
errors={errors}
control={control}
/>
</React.Fragment>
</Flex>
);
})()}
</div>
<ButtonGroup justifyContent="space-between" w="full" mt={6}>
{/* {currentStep > 0 && (
<IconButton
colorScheme="blackBtn"
aria-label="Next"
icon={<Icon as={HiArrowLeft} w={5} h={5} />}
onClick={handlePrevStep}
px={6}
/>
)} */}
<IconButton
colorScheme="blackBtn"
aria-label="Next"
isLoading={isLoading || hasSubmitted}
icon={<Icon as={HiArrowRight} w={5} h={5} />}
onClick={handleNextStep}
ml="auto"
px={6}
/>
</ButtonGroup>
</Box>
)}
</div>
);
};
77 changes: 77 additions & 0 deletions webapp/src/components/forms/payload/Ladder/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from "react";
import { TextField } from "@payloadcms/plugin-form-builder/dist/types";
import {
UseFormRegister,
FieldValues,
FieldErrorsImpl,
Controller,
} from "react-hook-form";
import {
Flex,
FormControl,
FormLabel,
IconButton,
Text,
} from "@chakra-ui/react";

export const Ladder: React.FC<{
register: UseFormRegister<FieldValues & any>;
control: any;
errors: Partial<
FieldErrorsImpl<{
[x: string]: any;
}>
>;
field: TextField & {
min: number;
max: number;
textLegend: { label: string }[];
};
}> = ({ field: currentField, control }) => {
const ladderArr = Array.from(
{ length: currentField.max - currentField.min + 1 },
(_, index) => (index + currentField.min).toString()
);

return (
<FormControl>
<Controller
control={control}
name={currentField.name}
render={({ field: { onChange, value } }) => {
return (
<Flex flexDir="column" gap={2} alignItems="center">
<Flex alignItems="center" gap={0.5} w="full">
{ladderArr.map((item) => (
<IconButton
flex={1}
key={item}
aria-label="Test"
icon={<>{item}</>}
h={10}
borderRadius="base"
fontSize={16}
fontWeight={800}
minWidth="auto"
color={value === item ? "white" : "black"}
colorScheme={
value === item ? "primaryShades" : "bgGrayShades"
}
onClick={() => onChange(item)}
/>
))}
</Flex>
<Flex w="full" alignItems="center" justifyContent="space-between">
{currentField.textLegend.map((item) => (
<Text fontSize={14} fontWeight={500}>
{item.label}
</Text>
))}
</Flex>
</Flex>
);
}}
/>
</FormControl>
);
};
28 changes: 28 additions & 0 deletions webapp/src/components/forms/payload/Textarea/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import { TextField } from "@payloadcms/plugin-form-builder/dist/types";
import {
UseFormRegister,
FieldValues,
FieldErrorsImpl,
FieldError,
} from "react-hook-form";
import FormInput from "../../FormInput";

export const Textarea: React.FC<{
register: UseFormRegister<FieldValues & any>;
errors: Partial<
FieldErrorsImpl<{
[x: string]: any;
}>
>;
field: TextField;
}> = ({ register, field, errors }) => {
return (
<FormInput
key={field.name}
register={register}
field={{ ...field, kind: field.blockType as any }}
fieldError={errors[field.name] as FieldError}
/>
);
};
43 changes: 43 additions & 0 deletions webapp/src/components/forms/payload/buildInitialFormState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { FormFieldBlock } from "@payloadcms/plugin-form-builder/dist/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]: "",
};
}
return initialSchema;
}, {});
};
7 changes: 7 additions & 0 deletions webapp/src/components/forms/payload/fields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Ladder } from "./Ladder";
import { Textarea } from "./Textarea";

export const fields = {
country: Ladder,
textarea: Textarea,
};
2 changes: 1 addition & 1 deletion webapp/src/components/modals/ConfirmModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {

type ConfirmModalProps = {
title: string;
description?: string;
description?: string | React.ReactNode;
labels?: {
primary?: string;
secondary?: string;
Expand Down
Loading

0 comments on commit d853e84

Please sign in to comment.