diff --git a/src/components/diary/write/DiaryEditor.tsx b/src/components/diary/write/DiaryEditor.tsx index 2408c5be..07de20a6 100644 --- a/src/components/diary/write/DiaryEditor.tsx +++ b/src/components/diary/write/DiaryEditor.tsx @@ -62,7 +62,7 @@ const DiaryEditor = ({ 제목* { + const formContext = useFormContext(); + return (
{locationModal && } {categoryModal && ( )} -

{`정보 ${pathname}하기`}

@@ -54,43 +56,54 @@ const InformationEditor = ({ 혼자 여행할 때 유용한 정보를 다른 솔리들과 공유해보세요!

-
+

제목*

editorStore.setEditor({ title: e.target.value })} - required={true} + onChange={(e) => { + formContext.setValue("informationTitle", e.target.value); + formContext.trigger("informationTitle"); + }} /> + {formContext.formState.errors.informationTitle && ( +

+ {formContext.formState.errors.informationTitle.message as String} +

+ )}
-
+

장소*

+ {formContext.formState.errors.placeName && ( +

+ {formContext.formState.errors.placeName.message as String} +

+ )}
- editorStore.setEditor({ content: value, contentLength: length }) - } + content={formContext.getValues("informationContent")} + onChange={(value: string, length: number) => { + formContext.setValue("informationContent", value); + formContext.setValue("contentLength", length); + formContext.trigger("informationContent"); + }} />

- {editorStore.contentLength}/500 + {formContext.getValues("contentLength")}/500

diff --git a/src/containers/informations/write/CategoryModalContainer.tsx b/src/containers/informations/write/CategoryModalContainer.tsx index 5a01caf1..48c6a5e3 100644 --- a/src/containers/informations/write/CategoryModalContainer.tsx +++ b/src/containers/informations/write/CategoryModalContainer.tsx @@ -4,6 +4,7 @@ import CategoryModal from "@/components/informations/write/CategoryModal"; import useEditorStore from "@/store/editorStore"; import { CategoryResponseDto } from "@/types/CategoryDto"; import { useEffect, useState } from "react"; +import { useFormContext } from "react-hook-form"; interface Props { closeModal: () => void; @@ -12,26 +13,25 @@ interface Props { const CategoryModalContainer = ({ closeModal }: Props) => { const [parentCategory, setParentCategory] = useState(0); const [categories, setCategories] = useState(); - const { categoryId, setEditor } = useEditorStore(); + const formContext = useFormContext(); const setParentCategoryId = (parentCategoryId: number) => { setParentCategory(parentCategoryId); - setEditor({ - categoryId: 0, - categoryName: "", - }); + formContext.setValue("categoryId", 0); + formContext.setValue("categoryName", ""); + formContext.watch(); }; const setCategory = (categoryId: number, categoryName: string) => { - setEditor({ - categoryId: categoryId, - categoryName: categoryName, - }); + formContext.setValue("categoryId", categoryId); + formContext.setValue("categoryName", categoryName); + formContext.trigger("categoryId"); }; const onCancel = () => { setParentCategory(0); - setEditor({ categoryId: 0 }); + formContext.setValue("categoryId", 0); + formContext.trigger("categoryId"); closeModal(); }; @@ -58,7 +58,7 @@ const CategoryModalContainer = ({ closeModal }: Props) => { { @@ -23,6 +25,31 @@ const InformationEditorContainer = () => { const router = useRouter(); const [loading, setLoading] = useState(false); + const methods = useForm({ + resolver: zodResolver(InformationCreateFormSchema), + defaultValues: { + userId: id, + informationTitle: "", + informationAddress: "", + province: "", + city: "", + placeId: "", + placeXAxis: "", + placeYAxis: "", + placeName: "", + categoryId: 0, + categoryName: "", + thumbnailImageUrl: "", + contentImagesUrl: [""], + mainImageIndex: 0, + informationContent: "", + contentLength: 0, + hashtags: [], + tips: [""], + }, + mode: "onChange", + }); + // 장소 선택 모달창이 보이는지 여부 const [locationModal, setLocationModal] = useState(false); @@ -30,7 +57,14 @@ const InformationEditorContainer = () => { const [categoryModal, setCategoryModal] = useState(false); const showLocationModal = () => { - editorStore.resetPlaceInfo(); + methods.setValue("province", ""); + methods.setValue("city", ""); + methods.setValue("informationAddress", ""); + methods.setValue("placeId", ""); + methods.setValue("placeXAxis", ""); + methods.setValue("placeYAxis", ""); + methods.setValue("placeName", ""); + methods.watch(); setLocationModal(true); }; @@ -39,7 +73,7 @@ const InformationEditorContainer = () => { }; const showCategoryModal = () => { - editorStore.setEditor({ categoryId: 0 }); + methods.setValue("categoryId", 0); setCategoryModal(true); }; @@ -60,76 +94,78 @@ const InformationEditorContainer = () => { }; const onSubmit = async () => { - // Validate from fields using Zod - const validatedFields = InformationCreateFormSchema.safeParse({ - userId: id, - informationTitle: editorStore.title, - informationAddress: editorStore.address, - province: editorStore.province, - city: editorStore.city, - placeId: editorStore.placeId, - placeXAxis: editorStore.placeXAxis, - placeYAxis: editorStore.placeYAxis, - placeName: editorStore.placeName, - categoryId: editorStore.categoryId, - thumbnailImageUrl: editorStore.images[editorStore.mainImageIndex], - contentImagesUrl: editorStore.images.filter( - (url, index) => index !== editorStore.mainImageIndex && url !== "", - ), - informationContent: sanitizeHtml(editorStore.content, sanitizeOption), - hashtags: editorStore.hashtags, - tips: editorStore.tips, - }); - - // If validation fails, return errors early. Otherwise, continue. - if (!validatedFields.success) { - console.log(validatedFields.error.issues); - alert(validatedFields.error.issues[0].message); - return; - } - - const data: CreateInformationRequestDto = { - informationTitle: validatedFields.data.informationTitle, - informationAddress: validatedFields.data.informationAddress, - informationContent: validatedFields.data.informationContent, - informationTips: validatedFields.data.tips.join(";"), - placeRegisterRequest: { - searchId: validatedFields.data.placeId, - name: validatedFields.data.placeName, - xAxis: validatedFields.data.placeXAxis, - yAxis: validatedFields.data.placeYAxis, - address: validatedFields.data.informationAddress, - }, - categoryId: validatedFields.data.categoryId, - zoneCategoryNameParent: validatedFields.data.province, - zoneCategoryNameChild: validatedFields.data.city, - thumbNailImageUrl: validatedFields.data.thumbnailImageUrl, - contentImagesUrl: validatedFields.data.contentImagesUrl, - tagRegisterRequests: validatedFields.data.hashtags.map((tag) => ({ - name: tag, - })), - }; - - setLoading(true); - - const response = await fetch("/api/informations", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - cache: "no-store", - }); - - if (!response.ok) { - alert("정보 등록에 실패하였습니다."); - setLoading(false); - throw new Error(response.statusText); - } - - const result: InformationRegisterResponseDto = await response.json(); - router.push(`/informations/${result.id}`); - router.refresh(); + alert("테스트"); + + // // Validate from fields using Zod + // const validatedFields = InformationCreateFormSchema.safeParse({ + // userId: id, + // informationTitle: editorStore.title, + // informationAddress: editorStore.address, + // province: editorStore.province, + // city: editorStore.city, + // placeId: editorStore.placeId, + // placeXAxis: editorStore.placeXAxis, + // placeYAxis: editorStore.placeYAxis, + // placeName: editorStore.placeName, + // categoryId: editorStore.categoryId, + // thumbnailImageUrl: editorStore.images[editorStore.mainImageIndex], + // contentImagesUrl: editorStore.images.filter( + // (url, index) => index !== editorStore.mainImageIndex && url !== "", + // ), + // informationContent: sanitizeHtml(editorStore.content, sanitizeOption), + // hashtags: editorStore.hashtags, + // tips: editorStore.tips, + // }); + + // // If validation fails, return errors early. Otherwise, continue. + // if (!validatedFields.success) { + // console.log(validatedFields.error.issues); + // alert(validatedFields.error.issues[0].message); + // return; + // } + + // const data: CreateInformationRequestDto = { + // informationTitle: validatedFields.data.informationTitle, + // informationAddress: validatedFields.data.informationAddress, + // informationContent: validatedFields.data.informationContent, + // informationTips: validatedFields.data.tips.join(";"), + // placeRegisterRequest: { + // searchId: validatedFields.data.placeId, + // name: validatedFields.data.placeName, + // xAxis: validatedFields.data.placeXAxis, + // yAxis: validatedFields.data.placeYAxis, + // address: validatedFields.data.informationAddress, + // }, + // categoryId: validatedFields.data.categoryId, + // zoneCategoryNameParent: validatedFields.data.province, + // zoneCategoryNameChild: validatedFields.data.city, + // thumbNailImageUrl: validatedFields.data.thumbnailImageUrl, + // contentImagesUrl: validatedFields.data.contentImagesUrl, + // tagRegisterRequests: validatedFields.data.hashtags.map((tag) => ({ + // name: tag, + // })), + // }; + + // setLoading(true); + + // const response = await fetch("/api/informations", { + // method: "POST", + // headers: { + // "Content-Type": "application/json", + // }, + // body: JSON.stringify(data), + // cache: "no-store", + // }); + + // if (!response.ok) { + // alert("정보 등록에 실패하였습니다."); + // setLoading(false); + // throw new Error(response.statusText); + // } + + // const result: InformationRegisterResponseDto = await response.json(); + // router.push(`/informations/${result.id}`); + // router.refresh(); }; // 로그인을 하지 않은 사용자의 경우 로그인 페이지로 리다이렉트. @@ -148,21 +184,23 @@ const InformationEditorContainer = () => { }, [initialize]); return ( - + + + ); }; diff --git a/src/containers/informations/write/PlaceModalContainer.tsx b/src/containers/informations/write/PlaceModalContainer.tsx index 2d5432ef..8a8c04f1 100644 --- a/src/containers/informations/write/PlaceModalContainer.tsx +++ b/src/containers/informations/write/PlaceModalContainer.tsx @@ -3,6 +3,7 @@ import PlaceModal from "@/components/informations/write/PlaceModal"; import useEditorStore from "@/store/editorStore"; import { useEffect, useState } from "react"; +import { useFormContext } from "react-hook-form"; import { useDebouncedCallback } from "use-debounce"; interface Props { @@ -10,8 +11,9 @@ interface Props { } const PlaceModalContainer = ({ closeModal }: Props) => { - const { address, placeName, placeId, setEditor, resetPlaceInfo } = - useEditorStore(); + const formContext = useFormContext(); + // const { address, placeName, placeId, setEditor, resetPlaceInfo } = + // useEditorStore(); const [isCustom, setIsCustom] = useState(false); // 장소 검색 객체 (place search) @@ -61,7 +63,14 @@ const PlaceModalContainer = ({ closeModal }: Props) => { }, 300); const onResetPlace = () => { - resetPlaceInfo(); + formContext.setValue("province", ""); + formContext.setValue("city", ""); + formContext.setValue("informationAddress", ""); + formContext.setValue("placeId", ""); + formContext.setValue("placeXAxis", ""); + formContext.setValue("placeYAxis", ""); + formContext.setValue("placeName", ""); + formContext.trigger("placeName"); closeModal(); }; @@ -73,15 +82,14 @@ const PlaceModalContainer = ({ closeModal }: Props) => { y: string; }) => { const temp = placeInfo.address_name.split(" "); - setEditor({ - province: temp[0].slice(0, 2) ?? "", - city: temp[1] ?? "", - address: placeInfo.address_name, - placeId: placeInfo.id, - placeXAxis: placeInfo.x, - placeYAxis: placeInfo.y, - placeName: placeInfo.place_name, - }); + formContext.setValue("province", temp[0].slice(0, 2) ?? ""); + formContext.setValue("city", temp[1] ?? ""); + formContext.setValue("informationAddress", placeInfo.address_name); + formContext.setValue("placeId", placeInfo.id); + formContext.setValue("placeXAxis", placeInfo.x); + formContext.setValue("placeYAxis", placeInfo.y); + formContext.setValue("placeName", placeInfo.place_name); + formContext.watch(); closeModal(); }; @@ -91,18 +99,18 @@ const PlaceModalContainer = ({ closeModal }: Props) => { y: string; }) => { const temp = addressInfo.address_name.split(" "); - setEditor({ - province: temp[0].slice(0, 2) ?? "", - city: temp[1] ?? "", - address: addressInfo.address_name, - placeXAxis: addressInfo.x, - placeYAxis: addressInfo.y, - placeId: "0", - }); + formContext.setValue("province", temp[0].slice(0, 2) ?? ""); + formContext.setValue("city", temp[1] ?? ""); + formContext.setValue("informationAddress", addressInfo.address_name); + formContext.setValue("placeXAxis", addressInfo.x); + formContext.setValue("placeYAxis", addressInfo.y); + formContext.setValue("placeId", "0"); + formContext.watch(); }; const onChangeCustomPlaceName = (placeName: string) => { - setEditor({ placeName: placeName }); + formContext.setValue("placeName", placeName); + formContext.trigger("placeName"); }; const onClick = (isCustom: boolean) => { @@ -128,8 +136,11 @@ const PlaceModalContainer = ({ closeModal }: Props) => { handleLocationSearch={handleLocationSearch} handleAddressSearch={handleAddressSearch} isCustom={isCustom} - canTypePlaceName={placeId === "0"} - canRegister={address !== "" && placeName !== ""} + canTypePlaceName={formContext.getValues("placeId") === "0"} + canRegister={ + formContext.getValues("informationAddress") !== "" && + formContext.getValues("placeName") !== "" + } onClick={onClick} onResetPlace={onResetPlace} onChangePlace={onChangePlace} diff --git a/src/lib/zod/schema/InformationCreateFormSchema.ts b/src/lib/zod/schema/InformationCreateFormSchema.ts index 4922e1c2..9e92a308 100644 --- a/src/lib/zod/schema/InformationCreateFormSchema.ts +++ b/src/lib/zod/schema/InformationCreateFormSchema.ts @@ -82,7 +82,6 @@ export const InformationCreateFormSchema = z.object({ required_error: "Content is required.", invalid_type_error: "Content must be a string.", }) - .min(1, { message: "내용을 입력해 주세요." }) .max(500, { message: "Must be 500 or fewer characters long" }), hashtags: z .string({