Skip to content

Commit

Permalink
feat: implement form detail and edit
Browse files Browse the repository at this point in the history
  • Loading branch information
oka4shi committed Apr 21, 2024
1 parent ab95420 commit a2b3f34
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 217 deletions.
4 changes: 1 addition & 3 deletions schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1485,9 +1485,7 @@ paths:
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/FormAnswer'
$ref: '#/components/schemas/FormAnswer'
'401':
description: Unauthorized
content:
Expand Down
178 changes: 178 additions & 0 deletions src/app/forms/[form_id]/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { SubmitHandler, useForm } from "react-hook-form";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { css } from "@styled-system/css";
import toast from "react-hot-toast";

import { client } from "@/lib/openapi";
import { deleteAllUploadedFiles, postFiles } from "@/lib/postFile";

import { components } from "@/schema";
import { FormItems, FileErrorsType, FormFieldsType, FilesFormType } from "./FormItems";

import { buttonStyle } from "@/recipes/button";
import { sosFileType } from "@/lib/file";

interface Props {
form: components["schemas"]["Form"];
answerId: string;
answerItems: FormFieldsType | undefined;
editable: boolean;
}

export const Form = ({ form, answerId, answerItems, editable }: Props) => {
const router = useRouter();
const onSubmit: SubmitHandler<FormFieldsType> = async (data) => {
if (Array.from(fileErrors).some((v) => v[1])) {
toast.error(`正しいファイルをアップロードしてください`);
return;
}

if (!form) {
toast.error("申請の読み込みが終わってから送信してください");
return;
}

const fileIds = await postFiles("private", files);
if (!fileIds) {
toast.error("ファイルのアップロード中にエラーが発生しました");
return;
}

type formAnswerItems = components["schemas"]["CreateFormAnswer"]["items"];
const items: formAnswerItems = form.items.flatMap((item): formAnswerItems[number] | [] => {
const value = (() => {
switch (item.type) {
case "string":
return data[item.id] || null;
case "int":
const datum = data[item.id];
return datum ? parseInt(datum ?? "") : null;
case "file":
return fileIds[item.id] || null;
case "choose_many":
const options = JSON.parse(String(data[item.id] ?? "[]")) as string[];
return options.length ? options : null;
default:
return data[item.id] || null;
}
})();

return value
? {
item_id: item.id,
type: item.type,
value: value,
}
: [];
});

if (answerItems) {
client
.PUT("/form-answers/{form_answer_id}", {
params: {
path: {
form_answer_id: answerId,
},
},
body: {
form_id: form.id,
items: items,
},
})
.then(async ({ error }) => {
if (error) {
toast.error(`申請の修正に失敗しました`);
await deleteAllUploadedFiles(fileIds);
return;
}
toast.success("申請の修正に成功しました");
router.push("/forms");
})
.catch(async () => {
toast.error(`申請の修正内容の送信中にエラーが発生しました`);
await deleteAllUploadedFiles(fileIds);
});
} else {
client
.POST("/form-answers", {
body: {
form_id: form.id,
items: items,
},
})
.then(async ({ error }) => {
if (error) {
toast.error(`申請の送信に失敗しました`);
await deleteAllUploadedFiles(fileIds);
return;
}
toast.success("申請の送信に成功しました");
router.push("/forms");
})
.catch(async () => {
toast.error(`申請の送信中にエラーが発生しました`);
await deleteAllUploadedFiles(fileIds);
});
}
};

const fileFormErrors: FileErrorsType = new Map(
form?.items
.filter((item) => {
item.type === "file";
})
.map((item) => [item.id, null]),
);
const [fileErrors, setFileErrors] = useState(fileFormErrors);

const filesState: FilesFormType = new Map(
form?.items
.filter((item) => item.type === "file")
.map((item) => {
if (answerItems && answerItems[item.id]) {
const dataTransfer = new DataTransfer();
(answerItems[item.id] as unknown as string[]).forEach((fileId) => {
const file = new File([], fileId, { type: sosFileType });
dataTransfer.items.add(file);
});
return [item.id, dataTransfer.files];
} else {
return [item.id, null];
}
}),
);
const [files, setFiles] = useState(filesState);
const {
register,
getValues,
setValue,
handleSubmit,
formState: { errors },
} = useForm({ mode: "onBlur", defaultValues: answerItems });

return (
<form
onSubmit={handleSubmit(onSubmit)}
className={css({
display: "flex",
flexDirection: "column",
rowGap: 3,
})}>
<FormItems
items={form.items}
getValues={getValues}
setValue={setValue}
register={register}
disabled={!!answerItems && !editable}
errors={errors}
files={files}
setFiles={setFiles}
setFileErrors={setFileErrors}
/>
{(editable || !answerItems) && (
<button className={buttonStyle({ visual: "solid", color: "purple" })}>{!answerItems ? "送信" : "更新"}</button>
)}
</form>
);
};
15 changes: 13 additions & 2 deletions src/app/forms/[form_id]/FormItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type Props = {
getValues: UseFormGetValues<FormFieldsType>;
setValue: UseFormSetValue<FormFieldsType>;
register: UseFormRegister<FormFieldsType>;
disabled?: boolean;
errors: FieldErrors<FormFieldsType>;
files: FilesFormType;
setFiles: Dispatch<SetStateAction<FilesFormType>>;
Expand All @@ -42,6 +43,7 @@ export const FormItems: FC<Props> = ({
getValues,
setValue,
register,
disabled,
errors,
files,
setFiles,
Expand All @@ -66,6 +68,7 @@ export const FormItems: FC<Props> = ({
required: { value: item.required, message: "数字を入力してください" },
max: item.max ? { value: item.max, message: `${item.max}以下の数字を入力してください` } : undefined,
min: item.min ? { value: item.min, message: `${item.min}以上の数字を入力してください` } : undefined,
disabled,
})}
error={errors[item.id]?.message}
/>
Expand All @@ -86,6 +89,7 @@ export const FormItems: FC<Props> = ({
minLength: item.min_length
? { value: item.min_length, message: `${item.min_length}文字以上で入力してください` }
: undefined,
disabled,
})}
error={errors[item.id]?.message}
/>
Expand All @@ -100,6 +104,7 @@ export const FormItems: FC<Props> = ({
options={item.options ?? []}
register={register(item.id, {
required: { value: item.required, message: "項目を選択してください。" },
disabled,
})}
error={errors[item.id]?.message}
/>
Expand All @@ -112,7 +117,10 @@ export const FormItems: FC<Props> = ({
description={item.description}
required={item.required ?? true}
options={item.options ?? []}
register={register(item.id)}
register={register(item.id, {
disabled,
})}
disabled={disabled}
getValues={getValues}
setValue={setValue}
error={errors[item.id]?.message}
Expand All @@ -127,7 +135,10 @@ export const FormItems: FC<Props> = ({
required={item.required ?? true}
extensions={item.extensions ?? []}
limit={item.limit ?? null}
register={register(item.id)}
register={register(item.id, {
disabled,
})}
disabled={disabled}
files={files}
setFiles={setFiles}
setErrorState={setFileErrors}
Expand Down
Loading

0 comments on commit a2b3f34

Please sign in to comment.