diff --git a/pages/create-schedule/index.tsx b/pages/create-schedule/index.tsx index b372e793..7b99d72b 100644 --- a/pages/create-schedule/index.tsx +++ b/pages/create-schedule/index.tsx @@ -1,12 +1,48 @@ import { PlanContent, PlanSideBar } from "@create-schedule/components"; +import axios from "axios"; +import { GetServerSideProps } from "next"; +import { CREATE_SCHEDULE_PATH } from "@shared/constants"; +import { TemplateSchedule, TemporarySchedule } from "@shared/types"; -const CreateSchedule = () => { +interface CreateScheduleProps { + temporary: TemporarySchedule[]; + templates: TemplateSchedule[]; +} + +const CreateSchedule = ({ temporary, templates }: CreateScheduleProps) => { return (
- +
); }; export default CreateSchedule; + +export const getServerSideProps: GetServerSideProps = async () => { + try { + const [temporary, templates] = await axios.all([ + axios.get( + `${process.env.NEXT_PUBLIC_BASE_URL}/${CREATE_SCHEDULE_PATH.temporary}`, + ), + axios.get( + `${process.env.NEXT_PUBLIC_BASE_URL}/${CREATE_SCHEDULE_PATH.templates}`, + ), + ]); + + return { + props: { + temporary: temporary.data as TemporarySchedule[], + templates: templates.data as TemplateSchedule[], + }, + }; + } catch (error) { + return { + props: { + temporary: [], + templates: [], + }, + }; + } +}; diff --git a/src/create-schedule/components/BasicInfo.tsx b/src/create-schedule/components/BasicInfo.tsx index 683c851c..a13b372f 100644 --- a/src/create-schedule/components/BasicInfo.tsx +++ b/src/create-schedule/components/BasicInfo.tsx @@ -1,31 +1,51 @@ -import { useState } from "react"; +import { BASIC_INFO_TAG } from "@create-schedule/constants"; +import { useEffect, useState } from "react"; import MenuContent from "./MenuContent"; import MenuContentContainer from "./MenuContentContainer"; import SideBarMenuBox from "./SideBarMenuBox"; -interface BasicInfoProps {} +interface BasicInfoProps { + currentProgress: number; + currentTab: string; + handleTab: (value: string) => void; +} -const BasicInfo = ({}: BasicInfoProps) => { - const boxTitle = "기본정보"; +const BasicInfo = ({ + currentProgress, + currentTab, + handleTab, +}: BasicInfoProps) => { const [isOpen, setIsOpen] = useState(false); const handleToggle = () => { setIsOpen((prev) => !prev); }; + useEffect(() => { + if (currentProgress === 2) { + setIsOpen(true); + } + }, [currentProgress]); + return ( <> {isOpen && ( - - - + {BASIC_INFO_TAG.map((title) => ( + + ))} )} diff --git a/src/create-schedule/components/Continue.tsx b/src/create-schedule/components/Continue.tsx index f428228b..a03dd525 100644 --- a/src/create-schedule/components/Continue.tsx +++ b/src/create-schedule/components/Continue.tsx @@ -1,16 +1,10 @@ import { useSetRecoilState } from "recoil"; -import { currentPageName, currentProgress } from "@shared/recoil"; +import { currentScheduleProgress } from "@shared/recoil"; import BlockShowing from "./BlockShowing"; import MakeScheduleButton from "./MakeScheduleButton"; const Continue = () => { - const setCurrentPage = useSetRecoilState(currentPageName); - const setCurrentProgress = useSetRecoilState(currentProgress); - - const handleCurrentPageElements = () => { - setCurrentPage("기본정보"); - setCurrentProgress(2); - }; + const setCurrentProgress = useSetRecoilState(currentScheduleProgress); return (
@@ -21,7 +15,7 @@ const Continue = () => { setCurrentProgress(2)} />
diff --git a/src/create-schedule/components/CurrentPage.tsx b/src/create-schedule/components/CurrentPage.tsx index d79a0fa1..c549dfd4 100644 --- a/src/create-schedule/components/CurrentPage.tsx +++ b/src/create-schedule/components/CurrentPage.tsx @@ -1,9 +1,28 @@ import Image from "next/image"; -import { useRecoilValue } from "recoil"; -import { currentPageName } from "@shared/recoil"; +import { useEffect, useState } from "react"; +import { CurrentPageType } from "@shared/types"; -const CurrentPage = () => { - const currentPage = useRecoilValue(currentPageName); +interface CurrentPageProps { + currentProgress: number; +} + +const CurrentPage = ({ currentProgress }: CurrentPageProps) => { + const [pageName, setPageName] = useState("작성 중인 일정"); + + useEffect(() => { + switch (currentProgress) { + case 1: + return setPageName("작성 중인 일정"); + case 2: + return setPageName("기본정보"); + case 3: + return setPageName("태그 및 일정 템플릿"); + case 4: + return setPageName("일정 채우기"); + case 5: + return setPageName("작성 마무리"); + } + }, [currentProgress]); return (
@@ -31,7 +50,7 @@ const CurrentPage = () => { height={12.5} />
- {currentPage} + {pageName}
); diff --git a/src/create-schedule/components/DateCityHandler.tsx b/src/create-schedule/components/DateCityHandler.tsx index da76cce5..d0d80ac5 100644 --- a/src/create-schedule/components/DateCityHandler.tsx +++ b/src/create-schedule/components/DateCityHandler.tsx @@ -1,7 +1,7 @@ import { useModal } from "@shared/hook"; interface DateCityHandlerProps { - callType: "date_start" | "date_end" | "city"; + callType: "date_start" | "date_end" | "city_first" | "city_second"; } const DateCityHandler = ({ callType }: DateCityHandlerProps) => { @@ -17,7 +17,9 @@ const DateCityHandler = ({ callType }: DateCityHandlerProps) => { ? callType === "date_start" ? "calendarSelector_start" : "calendarSelector_end" - : "thumbnailSelector" // TODO: citySelector + : callType === "city_first" + ? "citySelector_first" + : "citySelector_second" }`, }); }; diff --git a/src/create-schedule/components/DateCityInput.tsx b/src/create-schedule/components/DateCityInput.tsx index aa6b92c0..2589a7e6 100644 --- a/src/create-schedule/components/DateCityInput.tsx +++ b/src/create-schedule/components/DateCityInput.tsx @@ -3,8 +3,8 @@ import { scheduleAnswers } from "@shared/recoil"; import DateCityHandler from "./DateCityHandler"; interface DateCityInputProps { - callType: "date_start" | "date_end" | "city"; - answerType?: "startedAt" | "endedAt"; + callType: "date_start" | "date_end" | "city_first" | "city_second"; + answerType?: "startAt" | "endAt"; placeholder: string; } @@ -26,7 +26,9 @@ const DateCityInput = ({ value={ answerType && callType.includes("date") ? answer[answerType] - : answer.city + : callType === "city_first" + ? answer.location.split(" ")[0] + : answer.location.split(" ")[1] ?? "" } />
diff --git a/src/create-schedule/components/FillPlan.tsx b/src/create-schedule/components/FillPlan.tsx index ea579d36..3db70033 100644 --- a/src/create-schedule/components/FillPlan.tsx +++ b/src/create-schedule/components/FillPlan.tsx @@ -1,8 +1,22 @@ +import { useSetRecoilState } from "recoil"; +import { currentScheduleProgress } from "@shared/recoil"; import SideBarMenuBox from "./SideBarMenuBox"; -const FillPlan = () => { +interface FillPlanProps { + handleTab: (value: string) => void; +} + +const FillPlan = ({ handleTab }: FillPlanProps) => { + const setCurrentProgress = useSetRecoilState(currentScheduleProgress); + return ( -
+
{ + handleTab("일정 채우기"); + setCurrentProgress(4); + }} + >
); diff --git a/src/create-schedule/components/FinishWriting.tsx b/src/create-schedule/components/FinishWriting.tsx index e928e653..5525a0db 100644 --- a/src/create-schedule/components/FinishWriting.tsx +++ b/src/create-schedule/components/FinishWriting.tsx @@ -1,28 +1,51 @@ -import { useState } from "react"; +import { FINISH_WRITING_TAG } from "@create-schedule/constants"; +import { useEffect, useState } from "react"; import MenuContent from "./MenuContent"; import MenuContentContainer from "./MenuContentContainer"; import SideBarMenuBox from "./SideBarMenuBox"; -const FinishWriting = () => { - const boxTitle = "작성 마무리"; +interface FinishWritingProps { + currentProgress: number; + currentTab: string; + handleTab: (value: string) => void; +} + +const FinishWriting = ({ + currentProgress, + currentTab, + handleTab, +}: FinishWritingProps) => { const [isOpen, setIsOpen] = useState(false); const handleToggle = () => { setIsOpen((prev) => !prev); }; + useEffect(() => { + if (currentProgress === 5) { + setIsOpen(true); + } + }, [currentProgress]); + return ( <> {isOpen && ( - - + {FINISH_WRITING_TAG.map((title) => ( + + ))} )} diff --git a/src/create-schedule/components/LocationModal.tsx b/src/create-schedule/components/LocationModal.tsx new file mode 100644 index 00000000..351de989 --- /dev/null +++ b/src/create-schedule/components/LocationModal.tsx @@ -0,0 +1,79 @@ +import { useState } from "react"; +import { LOCATION } from "@shared/constants"; +import { LocationType } from "@shared/types"; + +interface LocationModalProps { + callType: "first" | "second"; + handleLocation: (value: string) => void; + firstLocation?: string; +} + +const LocationModal = ({ + callType, + handleLocation, + firstLocation, +}: LocationModalProps) => { + const [clickedCity, setClickedCity] = useState(""); + + const onClick = (index: number) => { + if (callType === "first") { + setClickedCity(Object.keys(LOCATION)[index]); + } else if (firstLocation) { + setClickedCity(LOCATION[firstLocation as keyof LocationType][index]); + } + }; + + return ( + <> +
+ {callType === "first" + ? Object.keys(LOCATION).map((location, index) => ( +
+
onClick(index)} + > + {location} +
+
+ )) + : firstLocation && + LOCATION[firstLocation as keyof LocationType].map( + (location, index) => ( +
+
onClick(index)} + > + {location} +
+
+ ), + )} +
+
+ +
+ + ); +}; + +export default LocationModal; diff --git a/src/create-schedule/components/MenuContent.tsx b/src/create-schedule/components/MenuContent.tsx index a3d6b9ee..19ac186b 100644 --- a/src/create-schedule/components/MenuContent.tsx +++ b/src/create-schedule/components/MenuContent.tsx @@ -1,24 +1,32 @@ import { useSetRecoilState } from "recoil"; -import { currentPageName } from "@shared/recoil"; -import { CurrentPageType } from "@shared/types"; +import { currentScheduleProgress } from "@shared/recoil"; interface MenuContentProps { title: string; - boxTitle: CurrentPageType; - isClicked?: boolean; + targetProgress: number; + currentTab: string; + handleTab: (value: string) => void; } -const MenuContent = ({ title, boxTitle, isClicked }: MenuContentProps) => { - const setCurrentPage = useSetRecoilState(currentPageName); +const MenuContent = ({ + title, + targetProgress, + currentTab, + handleTab, +}: MenuContentProps) => { + const setCurrentProgress = useSetRecoilState(currentScheduleProgress); return (
setCurrentPage(boxTitle)} + onClick={() => { + handleTab(title); + setCurrentProgress(targetProgress); + }} > - {title} + {title}
); }; diff --git a/src/create-schedule/components/PageContent.tsx b/src/create-schedule/components/PageContent.tsx index 7e6226b6..4dea7c8c 100644 --- a/src/create-schedule/components/PageContent.tsx +++ b/src/create-schedule/components/PageContent.tsx @@ -1,26 +1,33 @@ import { SUBTITLE, TITLE } from "@create-schedule/constants"; -import { useRecoilValue } from "recoil"; -import { currentProgress } from "@shared/recoil"; +import { TemplateSchedule, TemporarySchedule } from "@shared/types"; import PlanDefaultInfo from "./PlanDefaultInfo"; import Remains from "./Remains"; import ScheduleTagTemplate from "./ScheduleTagTemplate"; import Title from "./Title"; -const PageContent = () => { - const current = useRecoilValue(currentProgress); +interface PageContentProps { + currentProgress: number; + temporary: TemporarySchedule[]; + templates: TemplateSchedule[]; +} +const PageContent = ({ + currentProgress, + temporary, + templates, +}: PageContentProps) => { return ( <> - {current === 1 && ( + {currentProgress === 1 && ( <> - <Remains /> + <Remains temporary={temporary} /> </> )} - {current === 2 && ( + {currentProgress === 2 && ( <div className="w-[628px]"> <Title title={TITLE.nthPlan("명란마요", 5)} @@ -29,10 +36,10 @@ const PageContent = () => { <PlanDefaultInfo /> </div> )} - {current === 3 && ( + {currentProgress === 3 && ( <div className="w-[628px]"> <Title title={TITLE.tag} subTitle={SUBTITLE.withyou} /> - <ScheduleTagTemplate /> + <ScheduleTagTemplate templates={templates} /> </div> )} </> diff --git a/src/create-schedule/components/PlanContent.tsx b/src/create-schedule/components/PlanContent.tsx index ddd806ed..071babcd 100644 --- a/src/create-schedule/components/PlanContent.tsx +++ b/src/create-schedule/components/PlanContent.tsx @@ -1,11 +1,34 @@ +import { useEffect } from "react"; +import { useRecoilState } from "recoil"; +import { currentScheduleProgress } from "@shared/recoil"; +import { TemplateSchedule, TemporarySchedule } from "@shared/types"; import CurrentPage from "./CurrentPage"; import PageContent from "./PageContent"; -const PlanContent = () => { +interface PlanContentProps { + temporary: TemporarySchedule[]; + templates: TemplateSchedule[]; +} + +const PlanContent = ({ temporary, templates }: PlanContentProps) => { + const [currentProgress, setCurrentProgress] = useRecoilState( + currentScheduleProgress, + ); + + useEffect(() => { + if (temporary.length === 0 && currentProgress !== 2) { + setCurrentProgress(2); + } + }, [temporary]); + return ( <div className="absolute inline-block w-planContent h-planScrollbar px-[66px] py-[36px] overflow-FAQ"> - <CurrentPage /> - <PageContent /> + <CurrentPage currentProgress={currentProgress} /> + <PageContent + currentProgress={currentProgress} + temporary={temporary} + templates={templates} + /> </div> ); }; diff --git a/src/create-schedule/components/PlanSideBar.tsx b/src/create-schedule/components/PlanSideBar.tsx index 235d45bd..2cce2176 100644 --- a/src/create-schedule/components/PlanSideBar.tsx +++ b/src/create-schedule/components/PlanSideBar.tsx @@ -1,3 +1,11 @@ +import { + BASIC_INFO_TAG, + FINISH_WRITING_TAG, + TAG_N_TEMPLATE_TAG, +} from "@create-schedule/constants"; +import { useEffect, useState } from "react"; +import { useRecoilValue } from "recoil"; +import { currentScheduleProgress } from "@shared/recoil"; import BasicInfo from "./BasicInfo"; import FillPlan from "./FillPlan"; import FinishWriting from "./FinishWriting"; @@ -5,13 +13,48 @@ import SideBarIntro from "./SideBarIntro"; import TagNTemplate from "./TagNTemplate"; const PlanSideBar = () => { + const currentProgress = useRecoilValue(currentScheduleProgress); + const [currentTab, setCurrentTab] = useState("일정 제목 입력"); + + const handleTab = (value: string) => { + setCurrentTab(value); + }; + + useEffect(() => { + switch (currentProgress) { + case 2: + if (BASIC_INFO_TAG.includes(currentTab)) return; + return setCurrentTab("일정 제목 입력"); + case 3: + if (TAG_N_TEMPLATE_TAG.includes(currentTab)) return; + return setCurrentTab("태그"); + case 5: + if (FINISH_WRITING_TAG.includes(currentTab)) return; + return setCurrentTab("공개범위 설정"); + default: + return setCurrentTab(""); + } + }, [currentProgress, currentTab]); + return ( <div className="inline-block w-[241px] h-full border-r-2 border-[#F1F1F1]"> <SideBarIntro /> - <BasicInfo /> - <TagNTemplate /> - <FillPlan /> - <FinishWriting /> + <BasicInfo + currentProgress={currentProgress} + currentTab={currentTab} + handleTab={handleTab} + /> + <TagNTemplate + currentProgress={currentProgress} + currentTab={currentTab} + handleTab={handleTab} + /> + <FillPlan handleTab={handleTab} /> + <FinishWriting + currentProgress={currentProgress} + currentTab={currentTab} + handleTab={handleTab} + /> </div> ); }; diff --git a/src/create-schedule/components/PlanTitleInput.tsx b/src/create-schedule/components/PlanTitleInput.tsx index c1d294d7..b3a29f51 100644 --- a/src/create-schedule/components/PlanTitleInput.tsx +++ b/src/create-schedule/components/PlanTitleInput.tsx @@ -12,13 +12,14 @@ const PlanTitleInput = () => { }; return ( - <div className="mb-[30px]"> + <div id="일정 제목 입력" className="mb-[30px]"> <ScheduleTitle title={SCHEDULE_TITLE.planTitle} /> <input className="w-[628px] h-[55px] border border-[#E0E0E0] rounded-[5px] px-[26px]" type="text" placeholder="일정 제목을 입력해주세요." maxLength={40} + value={title} onChange={({ target: { value } }) => handleTitle(value)} /> <RemainChar title={title} /> diff --git a/src/create-schedule/components/RemainContent.tsx b/src/create-schedule/components/RemainContent.tsx index fdeec68b..68ddcc6e 100644 --- a/src/create-schedule/components/RemainContent.tsx +++ b/src/create-schedule/components/RemainContent.tsx @@ -1,32 +1,13 @@ import React from "react"; import { ScheduleCard } from "@shared/components"; +import { TemporarySchedule } from "@shared/types"; -const RemainContent = () => { - const content = [ - { - id: 1, - imageSrc: "image", - title: "베이커리 빵지순례", - position: "서울시 연남동", - createdAt: "23.10.23", - }, - { - id: 2, - imageSrc: "image", - title: "베이커리 빵지순례", - position: "서울시 연남동", - createdAt: "23.10.24", - }, - { - id: 3, - imageSrc: "image", - title: "베이커리 빵지순례", - position: "서울시 연남동", - createdAt: "23.10.25", - }, - ]; +interface RemainContentProps { + temporary: TemporarySchedule[]; +} - return <ScheduleCard content={content} callType="remain" />; +const RemainContent = ({ temporary }: RemainContentProps) => { + return <ScheduleCard content={temporary} callType="temporary" />; }; export default RemainContent; diff --git a/src/create-schedule/components/Remains.tsx b/src/create-schedule/components/Remains.tsx index 4eedcc42..da1c6840 100644 --- a/src/create-schedule/components/Remains.tsx +++ b/src/create-schedule/components/Remains.tsx @@ -1,13 +1,17 @@ -import Continue from "./Continue"; +import { TemporarySchedule } from "@shared/types"; import RemainContent from "./RemainContent"; -const Remains = () => { +interface RemainsProps { + temporary: TemporarySchedule[]; +} + +const Remains = ({ temporary }: RemainsProps) => { return ( <div> <div className="text-[#333333] font-medium leading-[23px] mb-[15px]"> 작성중이던 일정이 있습니다 </div> - <RemainContent /> + <RemainContent temporary={temporary} /> </div> ); }; diff --git a/src/create-schedule/components/ScheduleNextButton.tsx b/src/create-schedule/components/ScheduleNextButton.tsx index d512869c..2bece306 100644 --- a/src/create-schedule/components/ScheduleNextButton.tsx +++ b/src/create-schedule/components/ScheduleNextButton.tsx @@ -1,6 +1,8 @@ +import { postBasicInfo, setButtonDisabled } from "@create-schedule/util"; +import { useMemo } from "react"; import { useRecoilValue, useSetRecoilState } from "recoil"; import { useModal } from "@shared/hook"; -import { currentProgress, scheduleAnswers } from "@shared/recoil"; +import { currentScheduleProgress, scheduleAnswers } from "@shared/recoil"; interface ScheduleNextButtonProps { value: string; @@ -10,11 +12,12 @@ interface ScheduleNextButtonProps { const ScheduleNextButton = ({ value, callType }: ScheduleNextButtonProps) => { const { openAlert } = useModal(); const answer = useRecoilValue(scheduleAnswers); - const setCurrentProgress = useSetRecoilState(currentProgress); + const setCurrentProgress = useSetRecoilState(currentScheduleProgress); + const disabled = useMemo(() => setButtonDisabled(callType, answer), [answer]); const handleInputCheck = () => { - const startDate = parseInt(answer.startedAt.replace(/\./g, "")); - const endDate = parseInt(answer.endedAt.replace(/\./g, "")); + const startDate = parseInt(answer.startAt.replace(/\./g, "")); + const endDate = parseInt(answer.endAt.replace(/\./g, "")); if (endDate - startDate < 0) { openAlert({ @@ -28,9 +31,19 @@ const ScheduleNextButton = ({ value, callType }: ScheduleNextButtonProps) => { return true; }; - const handleCurrentProgress = () => { + const handleCurrentProgress = async () => { if (callType === "basicInfo" && handleInputCheck()) { - setCurrentProgress((prev) => prev + 1); + try { + const res = await postBasicInfo(answer); + + if (res) { + sessionStorage.setItem("postId", res.data.id); + setCurrentProgress((prev) => prev + 1); + } + } catch (error) { + console.log(error); + return; + } } else if (callType === "template") { setCurrentProgress((prev) => prev + 1); } @@ -39,13 +52,20 @@ const ScheduleNextButton = ({ value, callType }: ScheduleNextButtonProps) => { return ( <> <button - className="w-[423px] h-[48px] text-[14px] text-white font-bold -tracking-[0.03em] bg-[#F864A1] rounded-[5px] mb-[9px]" + disabled={disabled} + className={`${ + disabled + ? "text-[#B1B1B1] bg-[#E9ECEF] cursor-not-allowed " + : "text-white bg-[#F864A1] " + }w-[423px] h-[48px] text-[14px] font-bold -tracking-[0.03em] rounded-[5px] mb-[9px]`} onClick={handleCurrentProgress} > {value} </button> <div className="w-fit text-[14px] text-[#F864A1] -tracking-[0.03em] mx-auto"> - 언제든 수정할 수 있습니다 + {callType === "basicInfo" && disabled + ? "기본정보는 반드시 입력해주세요" + : "언제든 수정할 수 있습니다"} </div> </> ); diff --git a/src/create-schedule/components/ScheduleTagInput.tsx b/src/create-schedule/components/ScheduleTagInput.tsx index c697bc04..c95b0a8a 100644 --- a/src/create-schedule/components/ScheduleTagInput.tsx +++ b/src/create-schedule/components/ScheduleTagInput.tsx @@ -8,7 +8,10 @@ const ScheduleTagInput = () => { const { tag } = useRecoilValue(scheduleAnswers); return ( - <div className="w-[628px] border border-[#E0E0E0] rounded-[5px] px-[8px] mb-[20px] focus-within:border-[#4285F4]"> + <div + id="태그" + className="w-[628px] border border-[#E0E0E0] rounded-[5px] px-[8px] mb-[20px] focus-within:border-[#4285F4]" + > <div className="w-full h-fit"> <TagInput tag={tag} /> <Tag tag={tag} hasRemoveIcon /> diff --git a/src/create-schedule/components/ScheduleTagTemplate.tsx b/src/create-schedule/components/ScheduleTagTemplate.tsx index f0847de7..ce58eab9 100644 --- a/src/create-schedule/components/ScheduleTagTemplate.tsx +++ b/src/create-schedule/components/ScheduleTagTemplate.tsx @@ -1,12 +1,16 @@ import { SCHEDULE_TITLE } from "@create-schedule/constants"; +import { TemplateSchedule } from "@shared/types"; import ScheduleNextButton from "./ScheduleNextButton"; import ScheduleTagInput from "./ScheduleTagInput"; import ScheduleTitle from "./ScheduleTitle"; import TagRecommend from "./TagRecommend"; -import TemplateButton from "./TemplateButton"; import TemplateRecommend from "./TemplateRecommend"; -const ScheduleTagTemplate = () => { +interface ScheduleTagTemplateProps { + templates: TemplateSchedule[]; +} + +const ScheduleTagTemplate = ({ templates }: ScheduleTagTemplateProps) => { return ( <> <ScheduleTitle title={SCHEDULE_TITLE.tag} /> @@ -14,7 +18,7 @@ const ScheduleTagTemplate = () => { <div className="mb-[38px]"> <TagRecommend /> </div> - <TemplateRecommend /> + <TemplateRecommend templates={templates} /> <div className="w-full text-center mt-[60px]"> <ScheduleNextButton value="다음으로 넘어갈까요?" callType="template" /> </div> diff --git a/src/create-schedule/components/SelectCity.tsx b/src/create-schedule/components/SelectCity.tsx index c4f33543..2a7bf038 100644 --- a/src/create-schedule/components/SelectCity.tsx +++ b/src/create-schedule/components/SelectCity.tsx @@ -4,15 +4,15 @@ import ScheduleTitle from "./ScheduleTitle"; const SelectCity = () => { return ( - <div className="mb-[60px]"> + <div id="장소 선택" className="mb-[60px]"> <ScheduleTitle hasSubTitle - title={SCHEDULE_TITLE.city} - subTitle={SCHEDULE_SUBTITLE.city} + title={SCHEDULE_TITLE.location} + subTitle={SCHEDULE_SUBTITLE.location} /> - <DateCityInput callType="city" placeholder="대한민국" /> + <DateCityInput callType="city_first" placeholder="대한민국" /> <span className="inline-block relative invisible w-[13px] h-[2px] border border-[#333333] bottom-[4px] mx-[5px]" /> - <DateCityInput callType="city" placeholder="시군구" /> + <DateCityInput callType="city_second" placeholder="시군구" /> </div> ); }; diff --git a/src/create-schedule/components/SelectDate.tsx b/src/create-schedule/components/SelectDate.tsx index ff6e34bf..08c70c7e 100644 --- a/src/create-schedule/components/SelectDate.tsx +++ b/src/create-schedule/components/SelectDate.tsx @@ -4,7 +4,7 @@ import ScheduleTitle from "./ScheduleTitle"; const SelectDate = () => { return ( - <div className="mb-[38px]"> + <div id="시작일 종료일 설정" className="mb-[38px]"> <ScheduleTitle hasSubTitle title={SCHEDULE_TITLE.date} @@ -12,13 +12,13 @@ const SelectDate = () => { /> <DateCityInput callType="date_start" - answerType="startedAt" + answerType="startAt" placeholder="일정 시작일(오늘 날짜 기준)" /> <span className="inline-block relative w-[13px] h-[2px] border border-[#333333] bottom-[4px] mx-[5px]" /> <DateCityInput callType="date_end" - answerType="endedAt" + answerType="endAt" placeholder="일정 종료일" /> </div> diff --git a/src/create-schedule/components/SelectMainImage.tsx b/src/create-schedule/components/SelectMainImage.tsx index 2c014267..a02da45a 100644 --- a/src/create-schedule/components/SelectMainImage.tsx +++ b/src/create-schedule/components/SelectMainImage.tsx @@ -1,38 +1,58 @@ +import axios from "axios"; import { ThumbnailSelectorProps } from "modalContent/ThumbnailSelector"; import Image from "next/image"; -import { useSetRecoilState } from "recoil"; +import { useRecoilState } from "recoil"; import { useModal } from "@shared/hook"; import { scheduleAnswers } from "@shared/recoil"; const SelectMainImage = () => { - const setImageSrc = useSetRecoilState(scheduleAnswers); + const [{ thumbnail }, setThumbnail] = useRecoilState(scheduleAnswers); const { openModal } = useModal(); + const getImage = async (id: string) => { + try { + const image = await axios.get(`https://api.pexels.com/v1/photos/${id}`, { + headers: { + "Content-Type": "application/json", + Authorization: process.env.NEXT_PUBLIC_IMAGE_API_KEY, + }, + }); + setThumbnail((prev) => ({ ...prev, thumbnail: image.data.src.large })); + } catch (error) { + // TODO: Error handling + console.log(error); + } + }; + const handleModal = () => { openModal<ThumbnailSelectorProps>({ contentId: "thumbnailSelector", isHeaderCloseBtn: true, okCallback: (id: string) => { - setImageSrc((prev) => ({ ...prev, imageSrc: id })); + getImage(id); }, }); }; return ( <div + id="대표 이미지 설정" className="w-[222px] h-[126px] border border-[#E0E0E0] rounded-[5px] mr-[8px] cursor-pointer" onClick={handleModal} > - <div className="py-[32.5px] text-center"> + <div className={`${thumbnail ? "" : "py-[32.5px] text-center"}`}> <Image - src="/images/samples/camera.svg" - alt="camera" - width={30} - height={30} + className="rounded-[4px]" + src={`${thumbnail ? thumbnail : "/images/samples/camera.svg"}`} + alt="thumbnail" + width={thumbnail ? 221 : 30} + height={thumbnail ? 125 : 30} /> - <div className="text-[10px] text-[#757575] -tracking-[0.01em]"> - 이미지 선택하기 - </div> + {!thumbnail && ( + <div className="text-[10px] text-[#757575] -tracking-[0.01em]"> + 이미지 선택하기 + </div> + )} </div> </div> ); diff --git a/src/create-schedule/components/SideBarMenuBox.tsx b/src/create-schedule/components/SideBarMenuBox.tsx index 75f5a43d..991b3218 100644 --- a/src/create-schedule/components/SideBarMenuBox.tsx +++ b/src/create-schedule/components/SideBarMenuBox.tsx @@ -14,16 +14,15 @@ const SideBarMenuBox = ({ handleToggle, }: SideBarMenuBoxProps) => { return ( - <div className="w-full h-[63px] -tracking-[0.5px] px-[27px] py-[20px]"> + <div + className="w-full h-[63px] -tracking-[0.5px] px-[27px] py-[20px] cursor-pointer" + onClick={handleToggle} + > <p className="inline-block text-[14px] text-[#F864A1] font-bold leading-[23px]"> {title} </p> {isAccordion && ( - // TODO: rotate image, onclick - <span - className={`${isOpen ? "" : "rotate-180 "}float-right cursor-pointer`} - onClick={handleToggle} - > + <span className={`${isOpen ? "" : "rotate-180 "}float-right`}> <Image src="/images/samples/accordionMenu.svg" alt="fold" diff --git a/src/create-schedule/components/TagNTemplate.tsx b/src/create-schedule/components/TagNTemplate.tsx index 7ab1eea1..3c3a1b55 100644 --- a/src/create-schedule/components/TagNTemplate.tsx +++ b/src/create-schedule/components/TagNTemplate.tsx @@ -1,28 +1,51 @@ -import { useState } from "react"; +import { TAG_N_TEMPLATE_TAG } from "@create-schedule/constants"; +import { useEffect, useState } from "react"; import MenuContent from "./MenuContent"; import MenuContentContainer from "./MenuContentContainer"; import SideBarMenuBox from "./SideBarMenuBox"; -const TagNTemplate = () => { - const boxTitle = "태그 및 일정 템플릿"; +interface TagNTemplateProps { + currentProgress: number; + currentTab: string; + handleTab: (value: string) => void; +} + +const TagNTemplate = ({ + currentProgress, + currentTab, + handleTab, +}: TagNTemplateProps) => { const [isOpen, setIsOpen] = useState(false); const handleToggle = () => { setIsOpen((prev) => !prev); }; + useEffect(() => { + if (currentProgress === 3) { + setIsOpen(true); + } + }, [currentProgress]); + return ( <> <SideBarMenuBox - title={boxTitle} + title="태그 및 일정 템플릿" isOpen={isOpen} isAccordion handleToggle={handleToggle} /> {isOpen && ( <MenuContentContainer> - <MenuContent title="제목 및 썸네일" boxTitle={boxTitle} /> - <MenuContent title="템플릿 선택" boxTitle={boxTitle} /> + {TAG_N_TEMPLATE_TAG.map((title) => ( + <MenuContent + key={title} + title={title} + targetProgress={3} + currentTab={currentTab} + handleTab={handleTab} + /> + ))} </MenuContentContainer> )} </> diff --git a/src/create-schedule/components/Template.tsx b/src/create-schedule/components/Template.tsx index 54bed351..b0a08c53 100644 --- a/src/create-schedule/components/Template.tsx +++ b/src/create-schedule/components/Template.tsx @@ -1,8 +1,12 @@ -import { templateContent } from "@create-schedule/constants"; import { ScheduleCard } from "@shared/components"; +import { TemplateSchedule } from "@shared/types"; -const Template = () => { - return <ScheduleCard content={templateContent} callType="template" />; +interface TemplateProps { + templates: TemplateSchedule[]; +} + +const Template = ({ templates }: TemplateProps) => { + return <ScheduleCard content={templates} callType="template" />; }; export default Template; diff --git a/src/create-schedule/components/TemplateButton.tsx b/src/create-schedule/components/TemplateButton.tsx index 9b593c7c..0445faf4 100644 --- a/src/create-schedule/components/TemplateButton.tsx +++ b/src/create-schedule/components/TemplateButton.tsx @@ -1,5 +1,5 @@ import { useSetRecoilState } from "recoil"; -import { currentProgress } from "@shared/recoil"; +import { currentScheduleProgress } from "@shared/recoil"; import MakeScheduleButton from "./MakeScheduleButton"; interface TemplateButtonProps { @@ -7,7 +7,7 @@ interface TemplateButtonProps { } const TemplateButton = ({ clickedContent }: TemplateButtonProps) => { - const setCurrentProgress = useSetRecoilState(currentProgress); + const setCurrentProgress = useSetRecoilState(currentScheduleProgress); const handleCurrentProgress = () => { setCurrentProgress((prev) => prev + 1); diff --git a/src/create-schedule/components/TemplateRecommend.tsx b/src/create-schedule/components/TemplateRecommend.tsx index f11c1d45..dacd6c5b 100644 --- a/src/create-schedule/components/TemplateRecommend.tsx +++ b/src/create-schedule/components/TemplateRecommend.tsx @@ -1,16 +1,21 @@ import { SCHEDULE_TITLE } from "@create-schedule/constants"; +import { TemplateSchedule } from "@shared/types"; import ScheduleTitle from "./ScheduleTitle"; import Template from "./Template"; -const TemplateRecommend = () => { +interface TemplateRecommendProps { + templates: TemplateSchedule[]; +} + +const TemplateRecommend = ({ templates }: TemplateRecommendProps) => { return ( - <> + <div id="일정 템플릿"> <ScheduleTitle title={SCHEDULE_TITLE.templateRecommend("명란마요")} textSize="text-[16px]" /> - <Template /> - </> + <Template templates={templates} /> + </div> ); }; diff --git a/src/create-schedule/components/index.ts b/src/create-schedule/components/index.ts index 34b8e9f8..ada70af6 100644 --- a/src/create-schedule/components/index.ts +++ b/src/create-schedule/components/index.ts @@ -7,6 +7,7 @@ export { default as DateCityHandler } from "./DateCityHandler"; export { default as DateCityInput } from "./DateCityInput"; export { default as FillPlan } from "./FillPlan"; export { default as FinishWriting } from "./FinishWriting"; +export { default as LocationModal } from "./LocationModal"; export { default as MakeScheduleButton } from "./MakeScheduleButton"; export { default as MenuContent } from "./MenuContent"; export { default as MenuContentContainer } from "./MenuContentContainer"; diff --git a/src/create-schedule/constants/index.ts b/src/create-schedule/constants/index.ts index b9d133e1..d79b3cc6 100644 --- a/src/create-schedule/constants/index.ts +++ b/src/create-schedule/constants/index.ts @@ -1,7 +1,7 @@ import { PlanSubTitle, PlanTitle } from "@shared/types"; export const TITLE: PlanTitle = { - remains: "잠깐, 작성중이던 일정이 있어요", + temporary: "잠깐, 작성중이던 일정이 있어요", nthPlan: (nickname: string, number: number) => `${nickname} 님의 ${number}번째 일정`, tag: "태그 및 일정 템플릿", @@ -19,7 +19,7 @@ export const SCHEDULE_TITLE = { planTitle: "일정 제목", mainImage: "대표 이미지", date: "일정 시작일 ~ 종료일", - city: "장소 선택", + location: "장소 선택", tag: "카테고리 태그(#)", tagRecommend: "이런 태그는 어떤가요?", templateRecommend: (nickname: string) => @@ -30,36 +30,16 @@ export const SCHEDULE_SUBTITLE = { mainImage: "Chather 캐쳐 메인, 검색 결과, SNS 광고 등 여러 곳에서 노출할 대표 이미지를 등록해 주세요.", date: "준비기간 및 모집기간 등 전체 일정을 고려해 설정해 주세요.", - city: "일정을 수행할 위치를 선택해주세요.", + location: "일정을 수행할 위치를 선택해주세요.", }; -export const templateContent = [ - { - id: 1, - imageSrc: "image", - title: "베이커리 빵지순례", - position: "서울시 연남동", - requiredTime: "1일 이내", - }, - { - id: 2, - imageSrc: "image", - title: "카페에서 함께 공부해요", - position: "서울시 연남동", - requiredTime: "2~3일 일정", - }, - { - id: 3, - imageSrc: "image", - title: "베이커리 빵지순례", - position: "가로수길", - requiredTime: "1일 이내", - }, - { - id: 4, - imageSrc: "image", - title: "호캉스 일정", - position: "남해", - requiredTime: "2~3일 일정", - }, +export const BASIC_INFO_TAG = [ + "일정 제목 입력", + "대표 이미지 설정", + "시작일 종료일 설정", + "장소 선택", ]; + +export const TAG_N_TEMPLATE_TAG = ["태그", "일정 템플릿"]; + +export const FINISH_WRITING_TAG = ["공개범위 설정", "일정 소개"]; diff --git a/src/create-schedule/util/index.ts b/src/create-schedule/util/index.ts new file mode 100644 index 00000000..3db001f1 --- /dev/null +++ b/src/create-schedule/util/index.ts @@ -0,0 +1,51 @@ +import axios from "axios"; +import { CREATE_SCHEDULE_PATH } from "@shared/constants"; +import { ScheduleAnswerType } from "@shared/types"; + +export const postBasicInfo = async (answer: ScheduleAnswerType) => { + if ( + answer.title && + answer.thumbnail && + answer.startAt && + answer.endAt && + answer.location + ) + try { + const res = await axios.post( + `${process.env.NEXT_PUBLIC_BASE_URL}/${CREATE_SCHEDULE_PATH.temporary}`, + { + title: answer.title, + thumbnail: answer.thumbnail, + location: answer.location, + startAt: answer.startAt, + endAt: answer.endAt, + }, + { headers: { "Content-Type": "application/json" } }, + ); + + return res; + } catch (error) { + console.log(error); + } +}; + +export const setButtonDisabled = ( + callType: "basicInfo" | "template", + answer: ScheduleAnswerType, +) => { + if (callType === "basicInfo") { + if ( + answer.title && + answer.thumbnail && + answer.startAt && + answer.endAt && + answer.location + ) { + return false; + } + + return true; + } + + return false; +}; diff --git a/src/mock/browser.ts b/src/mock/browser.ts index f429ff42..5f072eb0 100644 --- a/src/mock/browser.ts +++ b/src/mock/browser.ts @@ -1,5 +1,10 @@ import { setupWorker } from "msw/browser"; import { handlers } from "./handlers"; import { myScheduleHandlers } from "./handlers/mySchedule"; +import { basicInfoHandlers } from "./handlers/postBasicInfo"; -export const worker = setupWorker(...handlers, ...myScheduleHandlers); +export const worker = setupWorker( + ...handlers, + ...myScheduleHandlers, + ...basicInfoHandlers, +); diff --git a/src/mock/data/basicInfo.ts b/src/mock/data/basicInfo.ts new file mode 100644 index 00000000..aa49177c --- /dev/null +++ b/src/mock/data/basicInfo.ts @@ -0,0 +1,3 @@ +export const BasicInfoData = { + id: 1, +}; diff --git a/src/mock/data/template.ts b/src/mock/data/template.ts new file mode 100644 index 00000000..2dded088 --- /dev/null +++ b/src/mock/data/template.ts @@ -0,0 +1,36 @@ +import { TemplateSchedule } from "@shared/types"; + +export const templateContent: TemplateSchedule[] = [ + { + id: 1, + thumbnailUrl: "image", + title: "베이커리 빵지순례", + location: "서울시 연남동", + days: "1일 이내", + schedules: [], + }, + { + id: 2, + thumbnailUrl: "image", + title: "카페에서 함께 공부해요", + location: "서울시 연남동", + days: "2~3일 일정", + schedules: [], + }, + { + id: 3, + thumbnailUrl: "image", + title: "베이커리 빵지순례", + location: "가로수길", + days: "1일 이내", + schedules: [], + }, + { + id: 4, + thumbnailUrl: "image", + title: "호캉스 일정", + location: "남해", + days: "2~3일 일정", + schedules: [], + }, +]; diff --git a/src/mock/data/temporary.ts b/src/mock/data/temporary.ts new file mode 100644 index 00000000..bcde07bc --- /dev/null +++ b/src/mock/data/temporary.ts @@ -0,0 +1,25 @@ +import { TemporarySchedule } from "@shared/types"; + +export const temporaryContent: TemporarySchedule[] = [ + { + id: 1, + thumbnailUrl: "image", + title: "베이커리 빵지순례", + location: "서울시 연남동", + createdAt: new Date("2023-10-23"), + }, + { + id: 2, + thumbnailUrl: "image", + title: "베이커리 빵지순례", + location: "서울시 연남동", + createdAt: new Date("2023-10-24"), + }, + { + id: 3, + thumbnailUrl: "image", + title: "베이커리 빵지순례", + location: "서울시 연남동", + createdAt: new Date("2023-10-25"), + }, +]; diff --git a/src/mock/handlers/getTemplates.ts b/src/mock/handlers/getTemplates.ts new file mode 100644 index 00000000..13a3e395 --- /dev/null +++ b/src/mock/handlers/getTemplates.ts @@ -0,0 +1,15 @@ +import { templateContent } from "mock/data/template"; +import { temporaryContent } from "mock/data/temporary"; +import { http } from "msw"; +import { CREATE_SCHEDULE_PATH } from "@shared/constants"; + +const BASE_URL = "http://localhost:3000/api"; + +export const templateHandlers = [ + http.get(`${BASE_URL}/${CREATE_SCHEDULE_PATH.temporary}`, () => { + return new Response(JSON.stringify(temporaryContent)); + }), + http.get(`${BASE_URL}/${CREATE_SCHEDULE_PATH.templates}`, () => { + return new Response(JSON.stringify(templateContent)); + }), +]; diff --git a/src/mock/handlers/postBasicInfo.ts b/src/mock/handlers/postBasicInfo.ts new file mode 100644 index 00000000..089acebd --- /dev/null +++ b/src/mock/handlers/postBasicInfo.ts @@ -0,0 +1,11 @@ +import { BasicInfoData } from "mock/data/basicInfo"; +import { http } from "msw"; +import { CREATE_SCHEDULE_PATH } from "@shared/constants"; + +const BASE_URL = "http://localhost:3000/api"; + +export const basicInfoHandlers = [ + http.post(`${BASE_URL}/${CREATE_SCHEDULE_PATH.temporary}`, ({ request }) => { + if (request.body) return new Response(JSON.stringify(BasicInfoData)); + }), +]; diff --git a/src/mock/server.ts b/src/mock/server.ts index 7b37f2ac..e90c897e 100644 --- a/src/mock/server.ts +++ b/src/mock/server.ts @@ -1,4 +1,5 @@ import { setupServer } from "msw/node"; import { handlers } from "./handlers"; +import { templateHandlers } from "./handlers/getTemplates"; -export const server = setupServer(...handlers); +export const server = setupServer(...handlers, ...templateHandlers); diff --git a/src/modalContent/CalendarSelector.tsx b/src/modalContent/CalendarSelector.tsx index c9153417..92556523 100644 --- a/src/modalContent/CalendarSelector.tsx +++ b/src/modalContent/CalendarSelector.tsx @@ -5,7 +5,7 @@ import { useModal } from "@shared/hook"; import { scheduleAnswers } from "@shared/recoil"; interface CalendarSelectorProps { - type: "startedAt" | "endedAt"; + type: "startAt" | "endAt"; } const CalendarSelector = ({ type }: CalendarSelectorProps) => { diff --git a/src/modalContent/CitySelector.tsx b/src/modalContent/CitySelector.tsx new file mode 100644 index 00000000..cb06dc2e --- /dev/null +++ b/src/modalContent/CitySelector.tsx @@ -0,0 +1,44 @@ +import { LocationModal } from "@create-schedule/components"; +import { useRecoilState } from "recoil"; +import { useModal } from "@shared/hook"; +import { scheduleAnswers } from "@shared/recoil"; + +interface CitySelectorProps { + callType: "first" | "second"; +} + +const CitySelector = ({ callType }: CitySelectorProps) => { + const { closeModal } = useModal(); + const [answer, setAnswer] = useRecoilState(scheduleAnswers); + + const handleLocation = (value: string) => { + if (callType === "first") { + setAnswer((prev) => ({ ...prev, location: value })); + closeModal(); + return; + } + + setAnswer((prev) => ({ + ...prev, + location: `${prev.location.split(" ")[0]} ${value}`, + })); + closeModal(); + return; + }; + + return ( + <> + {callType === "first" ? ( + <LocationModal callType={callType} handleLocation={handleLocation} /> + ) : ( + <LocationModal + callType={callType} + handleLocation={handleLocation} + firstLocation={answer.location.split(" ")[0]} + /> + )} + </> + ); +}; + +export default CitySelector; diff --git a/src/modalContent/index.tsx b/src/modalContent/index.tsx index 769a22e4..cfcda0d7 100644 --- a/src/modalContent/index.tsx +++ b/src/modalContent/index.tsx @@ -1,5 +1,6 @@ import { ModalContentId } from "@shared/recoil/modal"; import CalendarSelector from "./CalendarSelector"; +import CitySelector from "./CitySelector"; import RecruitManage, { RecruitManageProps } from "./RecruitManage"; import ThumbnailSelector, { ThumbnailSelectorProps } from "./ThumbnailSelector"; @@ -15,9 +16,13 @@ const ModalContent = <T,>(modalProps: ModalContentProps) => { case "RecruitManage": return <RecruitManage {...(modalProps as RecruitManageProps)} />; case "calendarSelector_start": - return <CalendarSelector type="startedAt" />; + return <CalendarSelector type="startAt" />; case "calendarSelector_end": - return <CalendarSelector type="endedAt" />; + return <CalendarSelector type="endAt" />; + case "citySelector_first": + return <CitySelector callType="first" />; + case "citySelector_second": + return <CitySelector callType="second" />; default: return <></>; } diff --git a/src/shared/components/ScheduleCard.tsx b/src/shared/components/ScheduleCard.tsx index 729c221f..65e9d1f3 100644 --- a/src/shared/components/ScheduleCard.tsx +++ b/src/shared/components/ScheduleCard.tsx @@ -1,19 +1,10 @@ import { Continue, TemplateButton } from "@create-schedule/components"; import { useState } from "react"; - -// TODO: 일정 타입 지정 -export interface Schedule { - id: number; - imageSrc: string; - title: string; - position: string; - createdAt?: string; - requiredTime?: string; -} +import { TemplateSchedule, TemporarySchedule } from "@shared/types"; interface ScheduleCardProps { - content: Schedule[]; - callType: "remain" | "template"; + content: TemporarySchedule[] | TemplateSchedule[]; + callType: "temporary" | "template"; } const ScheduleCard = ({ content, callType }: ScheduleCardProps) => { @@ -35,24 +26,26 @@ const ScheduleCard = ({ content, callType }: ScheduleCardProps) => { onClick={() => handleClick(index)} > <div className="w-[149px] h-[113px] bg-black rounded-t-[5px]"> - {_content.imageSrc} + {_content.thumbnailUrl} </div> <div className="relative h-[83px] px-[8px] py-[8px]"> <div className="text-[12px] text-[#333333] font-semibold -tracking-[0.02em]"> {_content.title} </div> <div className="text-[10px] text-[#959CA1] font-medium -tracking-[0.02em]"> - {_content.position} + {_content.location} </div> <div className={`${ - callType === "remain" ? "text-[#757575] " : "text-[#F864A1] " + callType === "temporary" + ? "text-[#757575] " + : "text-[#F864A1] " }absolute bottom-[8px] text-[10px] font-semibold -tracking-[0.02em] float-bottom`} > {`${ - callType === "remain" - ? `${_content.createdAt} 작성` - : `${_content.requiredTime}` + callType === "temporary" + ? `${(_content as TemporarySchedule).createdAt} 작성` // TODO: Date format handling + : `${(_content as TemplateSchedule).days}` }`} </div> </div> diff --git a/src/shared/constants/index.ts b/src/shared/constants/index.ts index d6241175..29c47322 100644 --- a/src/shared/constants/index.ts +++ b/src/shared/constants/index.ts @@ -8,3 +8,275 @@ export const KAKAO_LOGOUT_URL = "https://kapi.kakao.com/v1/user/logout"; export const NAVER_LOGOUT_URL = "https://nid.naver.com/nidlogin.logout"; export const DEFAULT_URL = "http://localhost:3000"; + +export const CREATE_SCHEDULE_PATH = { + base: "schedules", + temporary: "schedules/temporary", + tag: "schedules/tag", + templates: "schedules/templates", + items: "schedules/items", +}; + +export const LOCATION = { + 서울특별시: [ + "강남구", + "강동구", + "강북구", + "강서구", + "관악구", + "광진구", + "구로구", + "금천구", + "노원구", + "도봉구", + "동대문구", + "동작구", + "마포구", + "서대문구", + "서초구", + "성동구", + "성북구", + "송파구", + "양천구", + "영등포구", + "용산구", + "은평구", + "종로구", + "중구", + "중랑구", + ], + 경기도: [ + "수원시 장안구", + "수원시 권선구", + "수원시 팔달구", + "수원시 영통구", + "성남시 수정구", + "성남시 중원구", + "성남시 분당구", + "의정부시", + "안양시 만안구", + "안양시 동안구", + "부천시", + "광명시", + "평택시", + "동두천시", + "안산시 상록구", + "안산시 단원구", + "고양시 덕양구", + "고양시 일산동구", + "고양시 일산서구", + "과천시", + "구리시", + "남양주시", + "오산시", + "시흥시", + "군포시", + "의왕시", + "하남시", + "용인시 처인구", + "용인시 기흥구", + "용인시 수지구", + "파주시", + "이천시", + "안성시", + "김포시", + "화성시", + "광주시", + "양주시", + "포천시", + "여주시", + "연천군", + "가평군", + "양평군", + ], + 인천광역시: [ + "계양구", + "미추홀구", + "남동구", + "동구", + "부평구", + "서구", + "연수구", + "중구", + "강화군", + "옹진군", + ], + 강원특별자치도: [ + "춘천시", + "원주시", + "강릉시", + "동해시", + "태백시", + "속초시", + "삼척시", + "홍천군", + "횡성군", + "영월군", + "평창군", + "정선군", + "철원군", + "화천군", + "양구군", + "인제군", + "고성군", + "양양군", + ], + 충청북도: [ + "청주시 상당구", + "청주시 서원구", + "청주시 흥덕구", + "청주시 청원구", + "충주시", + "제천시", + "보은군", + "옥천군", + "영동군", + "증평군", + "진천군", + "괴산군", + "음성군", + "단양군", + ], + 충청남도: [ + "천안시 동남구", + "천안시 서북구", + "공주시", + "보령시", + "아산시", + "서산시", + "논산시", + "계룡시", + "당진시", + "금산군", + "부여군", + "서천군", + "청양군", + "홍성군", + "예산군", + "태안군", + ], + 대전광역시: ["대덕구", "동구", "서구", "유성구", "중구"], + 세종특별자치시: ["세종시"], + 전라북도: [ + "전주시 완산구", + "전주시 덕진구", + "군산시", + "익산시", + "정읍시", + "남원시", + "김제시", + "완주군", + "진안군", + "무주군", + "장수군", + "임실군", + "순창군", + "고창군", + "부안군", + ], + 전라남도: [ + "목포시", + "여수시", + "순천시", + "나주시", + "광양시", + "담양군", + "곡성군", + "구례군", + "고흥군", + "보성군", + "화순군", + "장흥군", + "강진군", + "해남군", + "영암군", + "무안군", + "함평군", + "영광군", + "장성군", + "완도군", + "진도군", + "신안군", + ], + 광주광역시: ["광산구", "남구", "동구", "북구", "서구"], + 경상북도: [ + "포항시 남구", + "포항시 북구", + "경주시", + "김천시", + "안동시", + "구미시", + "영주시", + "영천시", + "상주시", + "문경시", + "경산시", + "군위군", + "의성군", + "청송군", + "영양군", + "영덕군", + "청도군", + "고령군", + "성주군", + "칠곡군", + "예천군", + "봉화군", + "울진군", + "울릉군", + ], + 경상남도: [ + "창원시 의창구", + "창원시 성산구", + "창원시 마산합포구", + "창원시 마산회원구", + "창원시 진해구", + "진주시", + "통영시", + "사천시", + "김해시", + "밀양시", + "거제시", + "양산시", + "의령군", + "함안군", + "창녕군", + "고성군", + "남해군", + "하동군", + "산청군", + "함양군", + "거창군", + "합천군", + ], + 부산광역시: [ + "강서구", + "금정구", + "남구", + "동구", + "동래구", + "부산진구", + "북구", + "사상구", + "사하구", + "서구", + "수영구", + "연제구", + "영도구", + "중구", + "해운대구", + "기장군", + ], + 대구광역시: [ + "남구", + "달서구", + "동구", + "북구", + "서구", + "수성구", + "중구", + "달성군", + ], + 울산광역시: ["남구", "동구", "북구", "중구", "울주군"], + 제주특별자치도: ["서귀포시", "제주시"], +}; diff --git a/src/shared/recoil/createSchedule.tsx b/src/shared/recoil/createSchedule.tsx index 318a3b2c..f5318bb7 100644 --- a/src/shared/recoil/createSchedule.tsx +++ b/src/shared/recoil/createSchedule.tsx @@ -1,12 +1,7 @@ import { atom } from "recoil"; -import { CurrentPageType, ScheduleAnswerType } from "@shared/types"; +import { ScheduleAnswerType } from "@shared/types"; -export const currentPageName = atom<CurrentPageType>({ - key: "currentPage", - default: "작성 중인 일정", -}); - -export const currentProgress = atom<number>({ +export const currentScheduleProgress = atom<number>({ key: "currentProgress", default: 1, }); @@ -15,10 +10,10 @@ export const scheduleAnswers = atom<ScheduleAnswerType>({ key: "scheduleAnswers", default: { title: "", - imageSrc: "", - startedAt: "", - endedAt: "", - city: "", + thumbnail: "", + startAt: "", + endAt: "", + location: "", tag: [], }, }); diff --git a/src/shared/recoil/modal.tsx b/src/shared/recoil/modal.tsx index 892a6219..6388a36a 100644 --- a/src/shared/recoil/modal.tsx +++ b/src/shared/recoil/modal.tsx @@ -89,4 +89,6 @@ export type ModalContentId = | "thumbnailSelector" | "calendarSelector_start" | "calendarSelector_end" - | "RecruitManage"; + | "RecruitManage" + | "citySelector_first" + | "citySelector_second"; diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts index 26e747c7..552e9260 100644 --- a/src/shared/types/index.ts +++ b/src/shared/types/index.ts @@ -38,7 +38,7 @@ export type CurrentPageType = | "작성 마무리"; export interface PlanTitle { - remains: string; + temporary: string; nthPlan: (nickname: string, number: number) => string; tag: string; fill: string; @@ -53,12 +53,65 @@ export interface PlanSubTitle { export interface ScheduleAnswerType { title: string; - imageSrc: string; - startedAt: string; - endedAt: string; - city: string; + thumbnail: string; + startAt: string; + endAt: string; + location: string; tag: string[]; } -export type CalendarSelectorType = "startedAt" | "endedAt"; +export type CalendarSelectorType = "startAt" | "endAt"; + export type LoginType = "kakao" | "naver" | "catcher"; + +export interface ScheduleCardSection { + id: number; + thumbnailUrl: string; + title: string; + location: string; +} + +export interface ScheduleBasicInfo extends ScheduleCardSection { + startAt: Date; + endAt: Date; +} + +export interface TemporarySchedule extends ScheduleCardSection { + createdAt: Date; +} + +export interface TemplateSchedule extends ScheduleCardSection { + days: string; + schedules: TemplateDetail[]; +} + +export interface TemplateDetail { + id: number; + title: string; + category: string; + location: string; + description: string; + color: string; + startAt: Date; + endAt: Date; +} + +export interface LocationType { + 서울특별시: string[]; + 경기도: string[]; + 인천광역시: string[]; + 강원특별자치도: string[]; + 충청북도: string[]; + 충청남도: string[]; + 대전광역시: string[]; + 세종특별자치시: string[]; + 전라북도: string[]; + 전라남도: string[]; + 광주광역시: string[]; + 경상북도: string[]; + 경상남도: string[]; + 부산광역시: string[]; + 대구광역시: string[]; + 울산광역시: string[]; + 제주특별자치도: string[]; +}