Skip to content

Commit

Permalink
Merge pull request #256 from TripInfoWeb/dev_diary
Browse files Browse the repository at this point in the history
Refactor: 여행일기 등록 시 useForm 사용
  • Loading branch information
HyunJinNo authored Sep 3, 2024
2 parents 3aa65ed + c33afb3 commit 70cdc84
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 121 deletions.
2 changes: 1 addition & 1 deletion src/components/diary/list/DiaryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const DiaryList = async ({ page }: Props) => {
</div>
<DiaryPaginationContainer
currentPage={page}
totalPages={data.totalPages}
totalPages={data.page.totalPages}
/>
</div>
);
Expand Down
100 changes: 74 additions & 26 deletions src/components/diary/write/DiaryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import DateRangeModalContainer from "@/containers/diary/write/DateRangeModalCont
import { useDiaryEditorStoreType } from "@/store/diaryEditorStore";
import dynamic from "next/dynamic";
import QuillEditorSkeleton from "@/components/skeleton/common/QuillEditorSkeleton";
import { FormEvent } from "react";
import { useFormContext } from "react-hook-form";
import { parse } from "node-html-parser";

const QuillEditorContainer = dynamic(
() => import("@/containers/diary/write/QuillEditorContainer"),
Expand Down Expand Up @@ -41,6 +42,8 @@ const DiaryEditor = ({
setCurrentDay,
onSubmit,
}: Props) => {
const formContext = useFormContext();

return (
<div className="flex w-full flex-col">
{dateRangeModal && (
Expand All @@ -54,35 +57,40 @@ const DiaryEditor = ({
새로운 <span className="text-main">경험을 기록</span>하고 나만의
추억카드를 만들어보세요!
</p>
<div className="mt-[4.6875rem] flex h-[3.3125rem] flex-row items-center gap-[0.625rem]">
<div className="relative mt-[4.6875rem] flex h-[3.3125rem] flex-row items-center gap-[0.625rem]">
<h2 className="w-[2.625rem] text-lg font-semibold text-black dark:text-slate-200">
제목<span className="text-main">*</span>
</h2>
<input
className="h-full flex-grow rounded-full border-[0.0625rem] border-gray3 bg-transparent pl-5 text-sm outline-none hover:border-main"
className={`${formContext.formState.errors.title ? "border-red-500" : "border-gray3 hover:border-main"} h-full flex-grow rounded-full border-[0.0625rem] bg-transparent pl-5 text-sm outline-none`}
type="text"
name="title"
placeholder="제목을 입력하세요."
value={diaryEditorStore.title}
{...formContext.register("title")}
maxLength={50}
onChange={(e) =>
diaryEditorStore.setDiaryEditor({ title: e.target.value })
}
onChange={(e) => {
formContext.setValue("title", e.target.value);
formContext.trigger("title");
}}
/>
{formContext.formState.errors.title && (
<p className="absolute -bottom-6 left-16 mt-1 text-xs text-red-500">
{formContext.formState.errors.title.message as String}
</p>
)}
</div>
<div className="mt-10 flex flex-row items-center gap-40 max-[1024px]:flex-col max-[1024px]:items-start max-[1024px]:gap-10">
<div className="flex h-[3.3125rem] flex-row items-center gap-[0.625rem] max-[1024px]:w-full">
<div className="relative flex h-[3.3125rem] flex-row items-center gap-[0.625rem] max-[1024px]:w-full">
<h2 className="w-[2.625rem] text-lg font-semibold text-black dark:text-slate-200">
날짜<span className="text-main">*</span>
</h2>
<button
className={`${diaryEditorStore.startDate ? "text-black" : "text-gray2"} h-[3.3125rem] w-[21.75rem] flex-grow rounded-full border-[0.0625rem] border-gray3 bg-transparent pl-5 text-start text-sm hover:border-main`}
className={`${formContext.getValues("startDate") ? "text-black" : "text-gray2"} ${formContext.formState.errors.startDate ? "border-red-500" : "border-gray3 hover:border-main"} h-[3.3125rem] w-[21.75rem] flex-grow rounded-full border-[0.0625rem] bg-transparent pl-5 text-start text-sm`}
type="button"
onClick={() => showDateRangeModal()}
>
{diaryEditorStore.startDate !== null &&
diaryEditorStore.endDate !== null ? (
<p>{`${diaryEditorStore.startDate.toLocaleDateString("ko-KR")} ~ ${diaryEditorStore.endDate?.toLocaleDateString("ko-KR")}`}</p>
{formContext.getValues("startDate") !== null &&
formContext.getValues("endDate") !== null ? (
<p>{`${formContext.getValues("startDate").toLocaleDateString("ko-KR")} ~ ${formContext.getValues("endDate").toLocaleDateString("ko-KR")}`}</p>
) : (
<div className="flex flex-row items-center gap-2">
{"YYYY.MM.DD"}
Expand All @@ -95,21 +103,35 @@ const DiaryEditor = ({
</div>
)}
</button>
{formContext.formState.errors.startDate && (
<p className="absolute -bottom-6 left-16 mt-1 text-xs text-red-500">
{formContext.formState.errors.startDate.message as String}
</p>
)}
</div>
{diaryEditorStore.days > 0 && (
<div className="flex h-[3.3125rem] flex-grow flex-row items-center gap-[0.625rem] max-[1024px]:w-full">
<div className="relative flex h-[3.3125rem] flex-grow flex-row items-center gap-[0.625rem] max-[1024px]:w-full">
<h2 className="w-[2.625rem] text-lg font-semibold text-black dark:text-slate-200">
지역<span className="text-main">*</span>
</h2>
<button
className={`${diaryEditorStore.address[diaryEditorStore.currentDay - 1] === "" ? "text-gray2" : "text-black"} h-full flex-grow rounded-full border-[0.0625rem] border-gray3 bg-transparent pl-5 text-start text-sm outline-none hover:border-main`}
className={`${formContext.getValues("address")[diaryEditorStore.currentDay - 1] === "" ? "text-gray2" : "text-black"} ${formContext.formState.errors.address ? "border-red-500" : "border-gray3 hover:border-main"} h-full flex-grow rounded-full border-[0.0625rem] bg-transparent pl-5 text-start text-sm outline-none`}
type="button"
onClick={() => showAddressModal()}
>
{diaryEditorStore.address[diaryEditorStore.currentDay - 1] === ""
{formContext.getValues("address")[
diaryEditorStore.currentDay - 1
] === ""
? "지역명을 입력하세요."
: diaryEditorStore.address[diaryEditorStore.currentDay - 1]}
: formContext.getValues("address")[
diaryEditorStore.currentDay - 1
]}
</button>
{formContext.formState.errors.address && (
<p className="absolute -bottom-6 left-16 mt-1 text-xs text-red-500">
주소를 입력해 주세요.
</p>
)}
</div>
)}
</div>
Expand Down Expand Up @@ -144,21 +166,24 @@ const DiaryEditor = ({
</div>
)}
{diaryEditorStore.days > 0 && (
<div className="mt-6 flex flex-col gap-5 rounded-2xl border-[0.0625rem] border-gray3 pb-[0.875rem] pt-6">
<div
className={`${formContext.formState.errors.moodLevels ? "border-red-500" : "border-gray3"} relative mt-6 flex flex-col gap-5 rounded-2xl border-[0.0625rem] pb-[0.875rem] pt-6`}
>
<h2 className="pl-6 text-lg font-semibold text-black dark:text-slate-200">
{`하루 기분은 어땠나요? (Day ${diaryEditorStore.currentDay})`}
</h2>
<div className="flex flex-wrap items-center">
{["최고", "좋아", "무난", "슬퍼", "화나"].map((value, index) => (
<button
key={index + 1}
className={`${diaryEditorStore.moodLevels[diaryEditorStore.currentDay - 1] === index + 1 ? "bg-[#F2FAF7] text-main" : "text-gray1"} flex h-[5.75rem] w-[6.5rem] flex-col items-center justify-between py-[0.5625rem] text-[0.9375rem] hover:bg-[#F2FAF7] hover:text-main dark:text-slate-400`}
onClick={() =>
diaryEditorStore.changeMoodLevel(
diaryEditorStore.currentDay - 1,
index + 1,
)
}
className={`${formContext.getValues("moodLevels")[diaryEditorStore.currentDay - 1] === index + 1 ? "bg-[#F2FAF7] text-main" : "text-gray1"} flex h-[5.75rem] w-[6.5rem] flex-col items-center justify-between py-[0.5625rem] text-[0.9375rem] hover:bg-[#F2FAF7] hover:text-main dark:text-slate-400`}
onClick={() => {
const moodLevels: number[] =
formContext.getValues("moodLevels");
moodLevels[diaryEditorStore.currentDay - 1] = index + 1;
formContext.setValue("moodLevels", moodLevels);
formContext.trigger("moodLevels");
}}
>
<div className="relative h-10 w-8">
<Image
Expand All @@ -172,13 +197,36 @@ const DiaryEditor = ({
</button>
))}
</div>
{formContext.formState.errors.moodLevels && (
<p className="absolute -bottom-6 left-4 mt-1 text-xs text-red-500">
모든 날짜의 기분 정보를 입력해 주세요.
</p>
)}
</div>
)}
{diaryEditorStore.days > 0 && <QuillEditorContainer />}
<button
className={`${diaryEditorStore.days > 0 ? "bg-main hover:scale-105" : "cursor-not-allowed bg-gray1"} mb-[5.3125rem] mt-10 flex h-[2.625rem] w-[9.625rem] items-center justify-center self-end rounded-full text-[0.9375rem] text-white`}
type="submit"
onClick={() => onSubmit()}
onClick={() => {
const imageUrl =
parse(formContext.getValues("contents")[0])
.querySelector("img")
?.getAttribute("src") ?? "";

if (imageUrl === "") {
alert("Day1에 최소 1장의 이미지를 등록해 주세요.");
return;
}

formContext.setValue("image", imageUrl);

if (!formContext.formState.isValid) {
formContext.trigger();
} else {
onSubmit();
}
}}
disabled={diaryEditorStore.days === 0 || loading}
>
{loading ? (
Expand Down
2 changes: 1 addition & 1 deletion src/components/diary/write/QuillEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const QuillEditor = ({
onChange,
}: Props) => {
return (
<div className="flex flex-col">
<div className="relative flex flex-col">
<HashSpinner loading={loading} />
<div className="-mb-2 mt-8 flex flex-row items-center gap-2 text-sm font-medium text-gray1">
<FaRegImage />
Expand Down
13 changes: 10 additions & 3 deletions src/containers/diary/write/AddressModalContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import AddressModal from "@/components/diary/write/AddressModal";
import useDiaryEditorStore from "@/store/diaryEditorStore";
import { useEffect, useState } from "react";
import { useFormContext } from "react-hook-form";
import { useDebouncedCallback } from "use-debounce";

interface Props {
Expand All @@ -11,6 +12,7 @@ interface Props {

const AddressModalContainer = ({ closeModal }: Props) => {
const diaryEditorStore = useDiaryEditorStore();
const formContext = useFormContext();

// 주소-좌표 변환 객체
const [geocoder, setGeocoder] = useState<any>();
Expand All @@ -34,14 +36,19 @@ const AddressModalContainer = ({ closeModal }: Props) => {

const onResetAddress = () => {
const index = diaryEditorStore.currentDay - 1;
diaryEditorStore.changeAddress(index, "");
const addressList: string[] = formContext.getValues("address");
addressList[index] = "";
formContext.setValue("address", addressList);
formContext.trigger();
closeModal();
};

const onChangeAddress = (placeInfo: { address_name: string }) => {
const index = diaryEditorStore.currentDay - 1;
const addressArr = placeInfo.address_name.trim().split(" ");
diaryEditorStore.changeAddress(index, `${addressArr[0]} ${addressArr[1]}`);
const addressList: string[] = formContext.getValues("address");
addressList[index] = placeInfo.address_name;
formContext.setValue("address", addressList);
formContext.trigger();
closeModal();
};

Expand Down
13 changes: 8 additions & 5 deletions src/containers/diary/write/DateRangeModalContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import DateRangeModal from "@/components/diary/write/DateRangeModal";
import useDiaryEditorStore from "@/store/diaryEditorStore";
import { useEffect, useState } from "react";
import { useFormContext } from "react-hook-form";

interface Props {
closeModal: () => void;
Expand All @@ -18,6 +19,7 @@ const DateRangeModalContainer = ({ closeModal }: Props) => {
key: "selection",
},
]);
const formContext = useFormContext();

const onChangeDateRange = () => {
const days = Math.floor(
Expand All @@ -26,14 +28,15 @@ const DateRangeModalContainer = ({ closeModal }: Props) => {
1,
);

formContext.setValue("startDate", state[0].startDate);
formContext.setValue("endDate", state[0].endDate);
formContext.setValue("address", Array<string>(days).fill(""));
formContext.setValue("moodLevels", Array<number>(days).fill(0));
formContext.setValue("contents", Array<string>(days).fill(""));

diaryEditorStore.setDiaryEditor({
startDate: state[0].startDate,
endDate: state[0].endDate,
days: days,
currentDay: 1,
address: Array<string>(days).fill(""),
moodLevels: Array<number>(days).fill(0),
contents: Array<string>(days).fill(""),
});
closeModal();
};
Expand Down
87 changes: 43 additions & 44 deletions src/containers/diary/write/DiaryEditorContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import useAuthStore from "@/store/authStore";
import useDiaryEditorStore from "@/store/diaryEditorStore";
import { CreateDiaryRequestDto } from "@/types/DiaryDto";
import { useRouter } from "next/navigation";
import { FormEvent, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import sanitizeHtml from "sanitize-html";
import { parse } from "node-html-parser";
import { FormProvider, useForm, useFormContext } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const DiaryEditorContainer = () => {
const router = useRouter();
Expand All @@ -20,41 +22,36 @@ const DiaryEditorContainer = () => {
const [addressModal, setAddressModal] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(false);

const onSubmit = async () => {
// Validate from fields using Zod
const validatedFields = DiaryCreateFormSchema.safeParse({
const methods = useForm({
resolver: zodResolver(DiaryCreateFormSchema),
defaultValues: {
userId: authStore.id,
title: diaryEditorStore.title,
startDate: diaryEditorStore.startDate,
endDate: diaryEditorStore.endDate,
address: diaryEditorStore.address,
image:
parse(diaryEditorStore.contents[0])
.querySelector("img")
?.getAttribute("src") ?? "",
moodLevels: diaryEditorStore.moodLevels,
contents: diaryEditorStore.contents.map((content) =>
sanitizeHtml(content, sanitizeOption),
),
});
title: "",
startDate: new Date(),
endDate: new Date(),
address: [""],
image: "",
moodLevels: [],
contents: [],
},
mode: "onChange",
});

// If validation fails, return errors early. Otherwise, continue.
if (!validatedFields.success) {
alert(validatedFields.error.issues[0].message);
return;
}
const onSubmit = async () => {
const { title, image, startDate, endDate, contents, moodLevels, address } =
methods.getValues();

const data: CreateDiaryRequestDto = {
title: validatedFields.data.title,
titleImage: validatedFields.data.image,
startDatetime: validatedFields.data.startDate,
endDatetime: validatedFields.data.endDate,
title: title,
titleImage: image,
startDatetime: startDate,
endDatetime: endDate,
diaryDayRequests: Array.from(
{ length: diaryEditorStore.days },
(_, index) => ({
content: validatedFields.data.contents[index],
feelingStatus: FEELING_STATUS[validatedFields.data.moodLevels[index]],
place: validatedFields.data.address[index],
content: contents[index],
feelingStatus: FEELING_STATUS[moodLevels[index]],
place: address[index],
}),
),
};
Expand Down Expand Up @@ -90,21 +87,23 @@ const DiaryEditorContainer = () => {
}, []);

return (
<DiaryEditor
text="등록"
diaryEditorStore={diaryEditorStore}
dateRangeModal={dateRangeModal}
addressModal={addressModal}
loading={loading}
showDateRangeModal={() => setDateRangeModal(true)}
closeDateRangeModal={() => setDateRangeModal(false)}
showAddressModal={() => setAddressModal(true)}
closeAddressModal={() => setAddressModal(false)}
setCurrentDay={(day: number) =>
diaryEditorStore.setDiaryEditor({ currentDay: day })
}
onSubmit={onSubmit}
/>
<FormProvider {...methods}>
<DiaryEditor
text="등록"
diaryEditorStore={diaryEditorStore}
dateRangeModal={dateRangeModal}
addressModal={addressModal}
loading={loading}
showDateRangeModal={() => setDateRangeModal(true)}
closeDateRangeModal={() => setDateRangeModal(false)}
showAddressModal={() => setAddressModal(true)}
closeAddressModal={() => setAddressModal(false)}
setCurrentDay={(day: number) =>
diaryEditorStore.setDiaryEditor({ currentDay: day })
}
onSubmit={onSubmit}
/>
</FormProvider>
);
};

Expand Down
Loading

0 comments on commit 70cdc84

Please sign in to comment.