diff --git a/package.json b/package.json index 71381bc..cb4e4e8 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-dom": "^19.0.0", "react-mobile-picker": "^1.1.2", "react-qr-code": "^2.0.15", + "swiper": "^11.2.8", "yup": "^1.6.1", "zustand": "^5.0.3" }, @@ -75,5 +76,6 @@ "extends": [ "plugin:storybook/recommended" ] - } + }, + "packageManager": "pnpm@9.7.0+sha512.dc09430156b427f5ecfc79888899e1c39d2d690f004be70e05230b72cb173d96839587545d09429b55ac3c429c801b4dc3c0e002f653830a420fa2dd4e3cf9cf" } diff --git a/src/app/api/goal/index.ts b/src/app/api/goal/index.ts index acbf1d2..5682915 100644 --- a/src/app/api/goal/index.ts +++ b/src/app/api/goal/index.ts @@ -1,5 +1,17 @@ import { authAPI } from "@/app/api/config" -export const getGoalInfo = (id: number) => { - return authAPI.get(`/goal/2025-01-25`) +export const getGoalMainInfo = (date: string) => { + return authAPI.get(`/api/goal?date=${date}`) +} + +export const createGoal = (request) => { + return authAPI.post(`/api/goal`, request) +} + +export const getGoalDetailInfo = (date: string) => { + return authAPI.get(`/api/goal?date=${date}`) +} + +export const postCertifyGoal = (goalId: number, request) => { + return authAPI.post(`/api/goal/${goalId}/certify`, request) } diff --git a/src/app/api/letter/index.ts b/src/app/api/letter/index.ts new file mode 100644 index 0000000..a3cb6bb --- /dev/null +++ b/src/app/api/letter/index.ts @@ -0,0 +1,9 @@ +import { authAPI } from "@/app/api/config" + +export const getGoalLetter = (weeklyGoalId: number) => { + return authAPI.get(`/api/letter/${weeklyGoalId}`) +} + +export const createGoalLetter = (weeklyGoalId: number, request: string) => { + return authAPI.post(`/api/letter/${weeklyGoalId}`, request) +} diff --git a/src/app/goal/_components/buttonwrapper.module.scss b/src/app/goal/_components/buttonwrapper.module.scss index af40d66..f94e5e6 100644 --- a/src/app/goal/_components/buttonwrapper.module.scss +++ b/src/app/goal/_components/buttonwrapper.module.scss @@ -1,9 +1,9 @@ .goal__btn { - padding: 0 30px; - background: $white; + background: #fff; position: fixed; - bottom: 0; - left: 0; width: 100%; - z-index: 100; + left: 0; + bottom: 0; + padding: 16px 32px; + border-top: 1px solid #D8DADB; } diff --git a/src/app/goal/_components/buttonwrapper.tsx b/src/app/goal/_components/buttonwrapper.tsx index d1ceeee..6757667 100644 --- a/src/app/goal/_components/buttonwrapper.tsx +++ b/src/app/goal/_components/buttonwrapper.tsx @@ -1,20 +1,39 @@ "use client" -import styles from "./buttonwrapper.module.scss"; -import Button from "@/components/common/button/button"; +import styles from "./buttonwrapper.module.scss" +import Button from "@/components/common/button/button" +import { useRouter } from "next/navigation" +import dayjs from "dayjs" -const ButtonWrapper = () => { +type Props = { + step: 1 | 2 | 3 + onNext: () => void + onMutate: () => void +} + +const ButtonWrapper = ({ step, onNext, onMutate }: Props) => { + const router = useRouter() + const today = dayjs().format("YYYY-MM-DD") + const handleClick = () => { + if (step < 2) { + onNext() + } else if (step === 2) { + onMutate() + onNext() + } else if (step === 3) { + router.push(`/goal/detail?date=${today}`) + } + } return (
) } -export default ButtonWrapper; \ No newline at end of file +export default ButtonWrapper diff --git a/src/app/goal/_components/complete.tsx b/src/app/goal/_components/complete.tsx index 53c6995..de78f73 100644 --- a/src/app/goal/_components/complete.tsx +++ b/src/app/goal/_components/complete.tsx @@ -3,7 +3,7 @@ import Link from "next/link" import styles from "./complete.module.scss" import GoalResult from "@/app/goal/_components/goalresult" -const Complete = () => { +const Complete = ({ data }) => { return (
@@ -12,11 +12,9 @@ const Complete = () => { 목표가
설정되었어요! -

2020년 5월 1일

- 목표 보드로 이동하기 -
-
- +

+ {data.startDate} ~ {data.endDate} +

) diff --git a/src/app/goal/_components/goalresult.module.scss b/src/app/goal/_components/goalresult.module.scss index 133adf3..d4f12cb 100644 --- a/src/app/goal/_components/goalresult.module.scss +++ b/src/app/goal/_components/goalresult.module.scss @@ -1,22 +1,24 @@ .goal__result { - width: 160px; - height: 163px; - border: 5px solid #F2F2F5; - border-radius: 50%; + padding-right: 16px; + p { - margin-top: 20px; + padding-top: 20px; text-align: center; font-weight: 700; + span { color: #8D8D8D; font-weight: 400; } } } + .goal__icon { width: 100%; - height: 100%; + aspect-ratio: 1 / 1; display: flex; align-items: center; justify-content: center; + border: 5px solid #F2F2F5; + border-radius: 50%; } \ No newline at end of file diff --git a/src/app/goal/_components/goalresult.tsx b/src/app/goal/_components/goalresult.tsx index 4439a7f..a65f733 100644 --- a/src/app/goal/_components/goalresult.tsx +++ b/src/app/goal/_components/goalresult.tsx @@ -1,13 +1,16 @@ import styles from "./goalresult.module.scss" -import DefaultIcon from "@/assets/icons/goals/icon-default.svg"; -const GoalResult = () => { + +const GoalResult = ({ data }) => { + const Icon = data?.icon return (
- +
-

01 하루 물 6컵

+

+ {String(data?.number || 1).padStart(2, "0")} {data?.name || "목표 추가"} +

) } -export default GoalResult \ No newline at end of file +export default GoalResult diff --git a/src/app/goal/_components/step1.module.scss b/src/app/goal/_components/step1.module.scss index f208b00..0f56179 100644 --- a/src/app/goal/_components/step1.module.scss +++ b/src/app/goal/_components/step1.module.scss @@ -10,6 +10,18 @@ padding-left: 31px; } } + + &__icon { + display: flex; + align-items: center; + justify-content: center; + text-align: center; + border: 3px solid #F2F2F5; + border-radius: 50%; + width: 64px; + height: 64px; + margin: 0 auto; + } } .goal__result { @@ -46,17 +58,25 @@ &-control { width: 100%; - overflow-x: auto; - display: flex; margin-top: 24px; - gap: 16px; - padding: 14px; + + > div { + text-align: center; + } } &-txt { + margin-top: 16px; display: flex; align-items: center; + justify-content: center; gap: 5px; font-size: 14px; + + p, input { + white-space: pre-wrap; + text-align: left; + max-width: 80px; + } } } diff --git a/src/app/goal/_components/step1.tsx b/src/app/goal/_components/step1.tsx index f77c842..7276870 100644 --- a/src/app/goal/_components/step1.tsx +++ b/src/app/goal/_components/step1.tsx @@ -1,90 +1,150 @@ -"use client"; -import classNames from "classnames/bind"; -import styles from "./step1.module.scss"; -const cx = classNames.bind(styles); +"use client" +import classNames from "classnames/bind" +import styles from "./step1.module.scss" -import StepBox from "@/app/goal/_components/stepBox"; -import Goal1 from "@/assets/icons/sticker/compliment-sticker-1.svg" -import AddButton from "@/components/common/addbutton/addbutton"; -import GoalResult from "@/app/goal/_components/goalresult"; +const cx = classNames.bind(styles) +import { Swiper, SwiperSlide } from "swiper/react" +import "swiper/css" +import "swiper/css/navigation" +import "swiper/css/pagination" +import StepBox from "@/app/goal/_components/stepBox" +import Goal1 from "@/assets/icons/goals/goal1.svg" +import Goal2 from "@/assets/icons/goals/goal2.svg" +import Goal3 from "@/assets/icons/goals/goal3.svg" +import Goal4 from "@/assets/icons/goals/goal4.svg" +import Goal5 from "@/assets/icons/goals/goal5.svg" +import Goal6 from "@/assets/icons/goals/goal6.svg" +import Goal7 from "@/assets/icons/goals/goal7.svg" +import Goal8 from "@/assets/icons/goals/goal8.svg" +import DefaultIcon from "@/assets/icons/goals/icon-default.svg" +import AddButton from "@/components/common/addbutton/addbutton" +import GoalResult from "@/app/goal/_components/goalresult" +import { useEffect, useRef, useState } from "react" + +const Step1 = ({ data, setData }) => { + const swiperRef = useRef() + const initialGoalList = [ + { id: 1, icon: Goal1, name: "하루 물 6컵" }, + { id: 2, icon: Goal2, name: "야채 먹기" }, + { id: 3, icon: Goal3, name: "삼시세끼 채소" }, + { id: 4, icon: Goal4, name: "가족과 운동" }, + { id: 5, icon: Goal5, name: "저녁 30분 운동" }, + { id: 6, icon: Goal6, name: "패스트 푸드 끊기" }, + { id: 7, icon: Goal7, name: "근력 운동" }, + { id: 8, icon: Goal8, name: "간식 안 먹기" }, + ] + const [goalList, setGoalList] = useState(initialGoalList) + const [selectedGoals, setSelectedGoals] = useState< + { icon: any; name: string; editable: boolean }[] + >([]) + const handleClickGoal = (id: number, icon: any, label: string) => { + if (selectedGoals.length >= 10) return alert("최대 10개의 목표까지 추가할 수 있어요") + + // 중복 방지 + const alreadyExists = selectedGoals.some((g) => g.name === label) + if (alreadyExists) return alert("이미 추가된 목표예요") + + setSelectedGoals((prev) => { + const updated = [...prev, { icon, name: label, editable: false }] + setTimeout(() => { + swiperRef.current?.slideTo(updated.length - 1) + }, 100) + return updated + }) + } + + const handleAddGoal = () => { + const newGoal = { + id: goalList.length + 1, + icon: DefaultIcon, + name: "목표입력", + editable: true, + } + + setGoalList((prev) => [...prev, newGoal]) + } + + useEffect(() => { + setData(selectedGoals) + }, [selectedGoals, setData]) -const Step1 = () => { return ( <>
- +

최대 10개까지의 목표를 추가할 수 있어요

- + (swiperRef.current = swiper)}> + {selectedGoals.map((goal, idx) => ( + + + + ))} + + + +

그로우핏 추천 목표 -

직접 설정하기

+

직접 설정하기

-
    -
  • - -
    - { - }}/> -

    하루 물 6컵

    -
    -
  • -
  • - -
    - { - }}/> -

    하루 물 6컵

    -
    -
  • -
  • - -
    - { - }}/> -

    하루 물 6컵

    -
    -
  • -
  • - -
    - { - }}/> -

    하루 물 6컵

    -
    -
  • -
  • - -
    - { - }}/> -

    하루 물 6컵

    -
    -
  • -
  • - -
    - { - }}/> -

    하루 물 6컵

    -
    -
  • -
+ + {goalList.map((goal, index) => { + const Icon = goal.icon + const isSelected = selectedGoals.some((g) => g.name === goal.name) + + return ( + +
+ +
+
+ handleClickGoal(goal.id, goal.icon, goal.name)} + /> + {goal.editable ? ( + { + const newName = e.target.value + setGoalList((prev) => { + const updated = [...prev] + updated[index].name = newName + return updated + }) + }} + placeholder="입력하세요" + maxLength={20} + /> + ) : ( +

{goal.name}

+ )} +
+
+ ) + })} +
- ); -}; + ) +} -export default Step1; \ No newline at end of file +export default Step1 diff --git a/src/app/goal/_components/step2.module.scss b/src/app/goal/_components/step2.module.scss index ec50856..3e89fef 100644 --- a/src/app/goal/_components/step2.module.scss +++ b/src/app/goal/_components/step2.module.scss @@ -2,6 +2,7 @@ &__content { padding: 0 16px; } + &__title { > p { color: #8D8D8D; @@ -13,6 +14,7 @@ .goal__period { margin-top: 50px; + h2 { font-size: 18px; text-align: center; @@ -22,21 +24,26 @@ .goal__times { margin-top: 89px; + > h3 { font-size: 16px; font-weight: 600; } + > div { margin-top: 16px; + span { font-size: 15px; } + input { border: 1px solid #D8DADB; border-radius: 6px; width: 107px; height: 42px; margin: 0 8px; + text-align: right; } } } \ No newline at end of file diff --git a/src/app/goal/_components/step2.tsx b/src/app/goal/_components/step2.tsx index b1a922e..28bd864 100644 --- a/src/app/goal/_components/step2.tsx +++ b/src/app/goal/_components/step2.tsx @@ -1,18 +1,33 @@ -"use client"; -import { useState } from "react"; -import classNames from "classnames/bind"; -import styles from "./step2.module.scss"; -import DefaultIcon from "@/assets/icons/goals/icon-default.svg" -const cx = classNames.bind(styles); -import BackHeader from "@/components/layout/header/BackHeader"; -import StepBox from "@/app/goal/_components/stepBox"; -import Goal1 from "@/assets/icons/sticker/compliment-sticker-1.svg" -import AddButton from "@/components/common/addbutton/addbutton"; -import CustomCalendar from "@/components/common/calendar/customcalendar"; -import { DateRange } from "react-day-picker"; +"use client" +import { useEffect, useState } from "react" +import classNames from "classnames/bind" +import styles from "./step2.module.scss" -const Step2 = () => { - const [selectedWeek, setSelectedWeek] = useState(); +const cx = classNames.bind(styles) +import StepBox from "@/app/goal/_components/stepBox" +import CustomCalendar from "@/components/common/calendar/customcalendar" +import { DateRange } from "react-day-picker" +import dayjs from "dayjs" + +const Step2 = ({ setData }) => { + const [selectedWeek, setSelectedWeek] = useState() + useEffect(() => { + if (selectedWeek?.from && selectedWeek?.to) { + setData((prev) => ({ + ...prev, + startDate: dayjs(selectedWeek.from).format("YYYY-MM-DD"), + endDate: dayjs(selectedWeek.to).format("YYYY-MM-DD"), + })) + } + }, [selectedWeek, setData]) + + const handleCertCountChange = (e) => { + const value = Math.min(Number(e.target.value), 5) + setData((prev) => ({ + ...prev, + certificationCount: value, + })) + } return ( <> @@ -26,24 +41,28 @@ const Step2 = () => {
- +

인증횟수(최대 5회 가능)

인증 - +
- ); -}; + ) +} -export default Step2; \ No newline at end of file +export default Step2 diff --git a/src/app/goal/create/page.tsx b/src/app/goal/create/page.tsx index 31c26fa..12a1a6c 100644 --- a/src/app/goal/create/page.tsx +++ b/src/app/goal/create/page.tsx @@ -1,19 +1,56 @@ -import styles from "./page.module.scss" +"use client" import Step1 from "@/app/goal/_components/step1" import Step2 from "@/app/goal/_components/step2" import ButtonWrapper from "@/app/goal/_components/buttonwrapper" import Complete from "@/app/goal/_components/complete" import BackHeader from "@/components/layout/header/BackHeader" +import { useState } from "react" +import { useGoalCreate } from "@/queries/goal/userGoalQuery" const Page = () => { + const [step, setStep] = useState<1 | 2 | 3>(1) // 단계: 1 → 2 → 3 + const [data, setData] = useState({ + startDate: "", + endDate: "", + certificationCount: 0, + name: "", + iconId: 0, + icon: "", + }) + + const transformData = (rawData) => { + const goals = Object.entries(rawData) + .filter(([key]) => !["startDate", "endDate", "certificationCount"].includes(key)) + .map(([_, value], index) => ({ + name: value.name, + iconId: index + 1, + })) + + return { + startDate: rawData.startDate, + endDate: rawData.endDate, + certificationCount: rawData.certificationCount, + goals, + } + } + + const requestData = transformData(data) + + const handleNext = () => { + setStep((prev) => (prev < 3 ? ((prev + 1) as 1 | 2 | 3) : prev)) + } + const { mutate } = useGoalCreate(requestData) + return (
- {/**/} - {/**/} - - + + {step === 1 && } + {step === 2 && } + {step === 3 && } + +
) } diff --git a/src/app/goal/detail/@modal/goal-modal/goalmodal.module.scss b/src/app/goal/detail/@modal/goal-modal/goalmodal.module.scss index acf0cd7..c9d3f27 100644 --- a/src/app/goal/detail/@modal/goal-modal/goalmodal.module.scss +++ b/src/app/goal/detail/@modal/goal-modal/goalmodal.module.scss @@ -1,24 +1,42 @@ +.calendar { + > div { + padding: 0 10px; + + > div:first-of-type { + display: none; + } + } +} + .upload { padding-top: 20px; border-top: 1px solid #D9D9D9; } + .uploadtitle { display: flex; align-items: center; justify-content: space-between; padding: 0 30px; + h3 { font-size: 20px; font-weight: 700; } + p { color: $gray-80; font-size: 18px; } } +.smallcalendar_calendar-header { + //display: none; +} + .uploadimage { padding: 20px 30px; + span { display: flex; align-items: center; @@ -29,6 +47,7 @@ color: #727793; border-radius: 6px; } + label { display: inline-block; width: 240px; @@ -36,6 +55,7 @@ background-size: cover; border-radius: 6px; } + input { display: none; } diff --git a/src/app/goal/detail/@modal/goal-modal/page.tsx b/src/app/goal/detail/@modal/goal-modal/page.tsx index fd00333..9d0a17f 100644 --- a/src/app/goal/detail/@modal/goal-modal/page.tsx +++ b/src/app/goal/detail/@modal/goal-modal/page.tsx @@ -1,63 +1,78 @@ "use client" -import BottomSheet from "@/components/common/buttomsheet/bottomsheet"; -import { useRouter } from "next/navigation"; -import SmallCalendar from "@/components/common/smallcalendar/smallcalendar"; +import BottomSheet from "@/components/common/buttomsheet/bottomsheet" +import { useRouter, useSearchParams } from "next/navigation" +import SmallCalendar from "@/components/common/smallcalendar/smallcalendar" import styles from "./goalmodal.module.scss" -import React, {useState} from "react"; -import Button from "@/components/common/button/button"; +import React, { useState } from "react" +import Button from "@/components/common/button/button" +import dayjs from "dayjs" +import { useGoalCertifyQuery } from "@/queries/goal/userGoalQuery" const GoalModal = () => { - const router = useRouter(); - const [imagePreview, setImagePreview] = useState(null); + const router = useRouter() + const today = dayjs().format("YYYY-MM-DD") + const searchParams = useSearchParams() + const goalId = searchParams.get("goalId") + const [clickedDate, setClickedDate] = useState(today) + const [imagePreview, setImagePreview] = useState(null) + const { mutate } = useGoalCertifyQuery(goalId) const handleImageChange = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; + const file = e.target.files?.[0] + if (!file) return - const reader = new FileReader(); + const reader = new FileReader() reader.onloadend = () => { - setImagePreview(reader.result as string); - }; - reader.readAsDataURL(file); - }; + setImagePreview(reader.result as string) + } + reader.readAsDataURL(file) + } const closeModal = () => { - router.back(); - }; + router.back() + } + + const onSubmit = () => { + if (!imagePreview) { + alert("이미지를 업로드해주세요.") + return + } + const formData = new FormData() + formData.append("image", imagePreview) + mutate(formData) + } return ( - +
+ +

20분동안 걷기

0/3회 인증

- +
-
) } -export default GoalModal; \ No newline at end of file +export default GoalModal diff --git a/src/app/goal/detail/@modal/letter-modal/lettermodal.module.scss b/src/app/goal/detail/@modal/letter-modal/lettermodal.module.scss index 226f680..4653379 100644 --- a/src/app/goal/detail/@modal/letter-modal/lettermodal.module.scss +++ b/src/app/goal/detail/@modal/letter-modal/lettermodal.module.scss @@ -45,16 +45,6 @@ } } -.bottom { - margin-top: 50px; - padding: 0 0 0 30px; - - h2 { - font-size: 20px; - font-weight: 700; - } -} - .goal-box { width: 100%; display: flex; diff --git a/src/app/goal/detail/@modal/letter-modal/page.tsx b/src/app/goal/detail/@modal/letter-modal/page.tsx index bcf4d75..144307f 100644 --- a/src/app/goal/detail/@modal/letter-modal/page.tsx +++ b/src/app/goal/detail/@modal/letter-modal/page.tsx @@ -1,52 +1,45 @@ "use client" -import React from "react"; -import { useRouter } from "next/navigation"; +import React, { useState } from "react" +import { useRouter, useSearchParams } from "next/navigation" import styles from "./lettermodal.module.scss" -import BottomSheet from "@/components/common/buttomsheet/bottomsheet"; +import BottomSheet from "@/components/common/buttomsheet/bottomsheet" import ProfileIcon from "@/assets/character/profile-default.svg" -import Button from "@/components/common/button/button"; +import Button from "@/components/common/button/button" +import { useGetGoalLetter } from "@/queries/letter/useLetterQuery" + const LetterModal = () => { - const router = useRouter(); + const router = useRouter() + const searchParams = useSearchParams() + const weeklyGoalId = searchParams.get("weeklyGoalId") + const { data } = useGetGoalLetter(Number(weeklyGoalId)) const closeModal = () => { - router.back(); - }; + router.back() + } return (
- +

To. 미니준

-

블라블라블라

+

블라블라블라블라블라

From. 엄마가

-
-

1월 24일 ~ 1월 31일 목표

-
    -
  • -

    20분동안걷기

    -
    -
  • -
  • -

    20분동안걷기

    -
    -
  • -
  • -

    20분동안걷기

    -
    -
  • -
-
-
) } -export default LetterModal; \ No newline at end of file +export default LetterModal diff --git a/src/app/goal/detail/page.tsx b/src/app/goal/detail/page.tsx index de4900b..9607eca 100644 --- a/src/app/goal/detail/page.tsx +++ b/src/app/goal/detail/page.tsx @@ -1,26 +1,49 @@ "use client" +import { useEffect, useState } from "react" +import { useRouter, useSearchParams } from "next/navigation" +import dayjs from "dayjs" import styles from "./detail.module.scss" - import Character1 from "@/assets/character/step1.svg" import SmallCalendar from "@/components/common/smallcalendar/smallcalendar" import StepBox from "@/components/common/stepbox/stepbox" -import { useRouter } from "next/navigation" import TopSheet from "@/components/common/topsheet/topsheet" import DefaultHeader from "@/components/layout/header/DefaultHeader" import Navigation from "@/components/layout/Navigation" +import { useGoalMainQuery } from "@/queries/goal/userGoalQuery" const Page = () => { const router = useRouter() - const openModal = () => { - router.push("/goal/detail/goal-modal") + const searchParams = useSearchParams() + const date = searchParams.get("date") // "2025-07-05" + const [clickedDate, setClickedDate] = useState(date) + const { data } = useGoalMainQuery(clickedDate) + const [weeklyGoalId, setWeeklyGoalId] = useState() + + console.log(weeklyGoalId) + + const formatStartDate = dayjs(data?.data?.startDate).format("M월 D일") + const formatEndDate = dayjs(data?.data?.endDate).format("M월 D일") + const openModal = (goalId: number) => { + router.push(`/goal/detail/goal-modal?goalId=${goalId}`) } + const openLetterModal = () => { + const goalId = data?.data?.weeklyGoalId + if (goalId) { + // router.push(`/goal/letter?weeklyGoalId=${weeklyGoalId}`) + router.push(`/goal/letter/arrived?weeklyGoalId=${weeklyGoalId}`) + } + } + + useEffect(() => { + setWeeklyGoalId(data?.data.weeklyGoalId) + }, [data]) return (
- +
@@ -28,14 +51,17 @@ const Page = () => {
-
- 1월 24일 ~ 1월 31일 목표 +
+ + {formatStartDate} ~ {formatEndDate} 목표 + 한 주 동안 레벨 업! 더 가볍고 건강하게!

0/5개 달성

+ {!data?.data?.isLetterSent &&

편지 작성

}
- +
diff --git a/src/app/goal/letter/arrived/letter.module.scss b/src/app/goal/letter/arrived/letter.module.scss new file mode 100644 index 0000000..3658bfe --- /dev/null +++ b/src/app/goal/letter/arrived/letter.module.scss @@ -0,0 +1,102 @@ +.letter { + width: 100%; + height: 100%; + background: #fff; +} + +.letter__mission { + margin-top: 60px; + padding-top: 30px; + text-align: center; + + p { + color: $gray-70; + font-size: 18px; + line-height: 26px; + } + + h2 { + margin-top: 8px; + font-size: 20px; + line-height: 28px; + font-weight: 700; + } + + > div { + margin-top: 80px; + } +} + +.letter__from { + padding: 80px 34px 0; + + &-content { + margin-top: 45px; + display: flex; + flex-direction: column; + justify-content: space-between; + background: #F1F3FF; + border-radius: 4px; + height: 315px; + padding: 16px 25px; + + h3 { + font-weight: 700; + font-size: 18px; + line-height: 1.5; + } + + div { + text-align: center; + + h3 { + text-align: left; + } + } + + > h3 { + text-align: right; + } + + p { + white-space: pre-line; + overflow-wrap: break-word; + font-size: 18px; + margin-top: 8px; + text-align: left; + } + } +} + +.goal-box { + width: 100%; + display: flex; + gap: 20px; + padding-top: 32px; + overflow: scroll; + padding-bottom: 50px; + + li { + p { + color: $gray-70 + } + + div { + width: 220px; + height: 235px; + border-radius: 6px; + background: #F2F2F5; + margin-top: 20px; + } + } +} + +.btn { + background: #fff; + position: fixed; + width: 100%; + left: 0; + bottom: 0; + padding: 16px 32px; + border-top: 1px solid #D8DADB; +} \ No newline at end of file diff --git a/src/app/goal/letter/arrived/page.tsx b/src/app/goal/letter/arrived/page.tsx new file mode 100644 index 0000000..8254c29 --- /dev/null +++ b/src/app/goal/letter/arrived/page.tsx @@ -0,0 +1,49 @@ +"use client" +import React, { useState } from "react" +import { useRouter, useSearchParams } from "next/navigation" +import styles from "./letter.module.scss" +import ProfileIcon from "@/assets/character/profile-default.svg" +import Button from "@/components/common/button/button" +import { useGetGoalLetter } from "@/queries/letter/useLetterQuery" +import BackHeader from "@/components/layout/header/BackHeader" + +const LetterModal = () => { + const router = useRouter() + const searchParams = useSearchParams() + const weeklyGoalId = searchParams.get("weeklyGoalId") + const { data } = useGetGoalLetter(Number(weeklyGoalId)) + const closeModal = () => { + router.back() + } + return ( +
+ +
+

1월 24일 ~ 1월 31일 목표

+

20분동안 걷기외 5개의 미션을 모두 마쳤어요!

+
+
+
+
+
+ +

To. 미니준

+

블라블라블라블라블라

+
+

From. 엄마가

+
+
+
+
+
+ ) +} + +export default LetterModal diff --git a/src/app/goal/letter/letter.module.scss b/src/app/goal/letter/letter.module.scss index 8809c8b..d70f02e 100644 --- a/src/app/goal/letter/letter.module.scss +++ b/src/app/goal/letter/letter.module.scss @@ -1,24 +1,40 @@ .letter { padding: 0 16px; + &__title { margin-top: 14px; + p { font-size: 18px; color: #8D8D8D; margin-bottom: 7px; } + strong { font-size: 20px; font-weight: 600; } } + &__field { margin-top: 27px; + h3 { font-size: 16px; } + > div { margin-top: 27px; } } + + &__btn { + background: #fff; + position: fixed; + width: 100%; + left: 0; + bottom: 0; + padding: 16px 32px; + border-top: 1px solid #D8DADB; + } } \ No newline at end of file diff --git a/src/app/goal/letter/page.tsx b/src/app/goal/letter/page.tsx index 7972b8a..68b078b 100644 --- a/src/app/goal/letter/page.tsx +++ b/src/app/goal/letter/page.tsx @@ -1,23 +1,56 @@ +"use client" import styles from "./letter.module.scss" -import BackHeader from "@/components/layout/header/BackHeader"; +import BackHeader from "@/components/layout/header/BackHeader" import ProfileIcon from "@/assets/character/profile-default.svg" -import TextArea from "@/components/common/textarea/textarea"; +import TextArea from "@/components/common/textarea/textarea" +import { useState } from "react" +import { useGoalLetterQuery } from "@/queries/letter/useLetterQuery" +import Button from "@/components/common/button/button" +import { useSearchParams } from "next/navigation" + const LetterCreate = () => { + const searchParams = useSearchParams() + const [content, setContent] = useState("") + const weeklyGoalId = searchParams.get("weeklyGoalId") + const { mutate } = useGoalLetterQuery(Number(weeklyGoalId)) + const handleChangeValue = (e) => { + setContent(e.target.value) + } + const onSubmit = () => { + mutate({ + content, + }) + } + return (
- +

아이가 목표를 멋지게 해냈어요!

칭찬 편지를 남겨볼까요?

To.미니준

-