diff --git a/src/app/routes/goal/create-goal.tsx b/src/app/routes/goal/create-goal.tsx
index 907ec0a..bd462da 100644
--- a/src/app/routes/goal/create-goal.tsx
+++ b/src/app/routes/goal/create-goal.tsx
@@ -1,9 +1,18 @@
+import { useMemo } from "react";
+
import CreateGoal from "@/features/goal/routes/CreateGoal";
-import { useParams } from "react-router";
+import { useLocation } from "react-router";
const CreateGoalView = () => {
- const { goalId } = useParams<{ goalId?: string }>();
- const parsedGoalId: number | undefined = goalId ? Number(goalId) : undefined;
+ const { search } = useLocation();
+ const parsedGoalId = useMemo(() => {
+ const queryParams = new URLSearchParams(search);
+ const goalId = queryParams.get("goalId");
+ const numberedGoalId = goalId ? Number(goalId) : -1;
+ localStorage.setItem("goalId", String(numberedGoalId));
+
+ return numberedGoalId;
+ }, [search]);
return ;
};
diff --git a/src/components/ui/header/index.const.ts b/src/components/ui/header/index.const.ts
index 89bd46b..67cca50 100644
--- a/src/components/ui/header/index.const.ts
+++ b/src/components/ui/header/index.const.ts
@@ -17,7 +17,8 @@ export const HEADER_TITLE_MAP = new Map([
[paths.goal.create.getHref(), "목표추가"],
[paths.profile.reward.getHref(), "리워드 신청"],
[paths.auth.agreement.getHref(), "약관동의"],
- [paths.auth.phoneNumber.getHref(), "정보입력"]
+ [paths.auth.phoneNumber.getHref(), "정보입력"],
+ [paths.user.account.getHref(), "계정관리"]
]);
export const backgroundImages = [
diff --git a/src/components/ui/header/profile-header.tsx b/src/components/ui/header/profile-header.tsx
index 689a712..b2d0738 100644
--- a/src/components/ui/header/profile-header.tsx
+++ b/src/components/ui/header/profile-header.tsx
@@ -19,7 +19,7 @@ import {
generateProgressPercent
} from "./index.const";
-function ProfileHeader() {
+function ProfileHeader({ popoverContent }: { popoverContent: string }) {
const { userName, point } = useUserStore();
const [bgImage, setBgImage] = useState("");
@@ -85,15 +85,15 @@ function ProfileHeader() {
side="left"
className="w-50 p-2 text-xs text-green-500"
>
- 목표는 최대 3개까지 생성 가능합니다.
+ {popoverContent}
= ({
onTempSave,
onSave,
- isFormValid
+ isFormValid,
+ formStatus
}) => (
diff --git a/src/features/goal/components/create-goal/date-pick.tsx b/src/features/goal/components/create-goal/date-pick.tsx
index c36f713..a27a83f 100644
--- a/src/features/goal/components/create-goal/date-pick.tsx
+++ b/src/features/goal/components/create-goal/date-pick.tsx
@@ -56,9 +56,11 @@ const DatePick = () => {
};
const handleConfirm = () => {
+ const goalId = Number(localStorage.getItem("goalId"));
+ const url = `${paths.goal.create.path}${goalId !== -1 ? `?goalId=${goalId}` : ""}`;
if (!dateState.selectedDate) return;
if (mode === "end") {
- navigate(paths.goal.create.path, {
+ navigate(url, {
state: {
startDate: dateState.startDate,
endDate: dateState.selectedDate,
@@ -66,7 +68,7 @@ const DatePick = () => {
}
});
} else {
- navigate(paths.goal.create.path, {
+ navigate(url, {
state: { startDate: dateState.selectedDate, goalName }
});
}
diff --git a/src/features/goal/components/create-goal/goal.constants.ts b/src/features/goal/components/create-goal/goal.constants.ts
index c259f79..b5356d3 100644
--- a/src/features/goal/components/create-goal/goal.constants.ts
+++ b/src/features/goal/components/create-goal/goal.constants.ts
@@ -9,3 +9,13 @@ export const DAY_MAPPING: Record = {
금: "FRI",
토: "SAT"
};
+
+export const REVERSE_DAY_MAPPING: Record = {
+ SUN: "일",
+ MON: "월",
+ TUE: "화",
+ WED: "수",
+ THU: "목",
+ FRI: "금",
+ SAT: "토"
+};
diff --git a/src/features/goal/components/goal/progress-goal.tsx b/src/features/goal/components/goal/progress-goal.tsx
index 21faebd..0b86392 100644
--- a/src/features/goal/components/goal/progress-goal.tsx
+++ b/src/features/goal/components/goal/progress-goal.tsx
@@ -2,7 +2,7 @@ import { useMemo } from "react";
import { DAYS_STRING_MAP } from "@/features/map-certification/components/map-certification.const.ts";
import { useQuery } from "@tanstack/react-query";
-import { join, map, slice } from "es-toolkit/compat";
+import { filter, join, map, slice } from "es-toolkit/compat";
import { useUserStore } from "@/stores/user";
import { generate_qo_getGoalsCheck } from "@/lib/react-query/queryOptions/goals";
@@ -23,22 +23,29 @@ function ProgressGoal() {
generate_qo_getGoalsCheck(userId)
);
+ const activeProgressGoals = useMemo(
+ () => filter(progressGoals, ({ status }) => status === "ACTIVE"),
+ [progressGoals]
+ );
+
const certificationInfoMaps = useMemo(() => {
const generateMap = (certificationInfo: CertificationInfo[]) => {
return new Map(
- map(certificationInfo, ({ date, verified }) => [date, verified])
+ map(certificationInfo, ({ achievedAt, achievedSuccess }) => [
+ achievedAt,
+ achievedSuccess
+ ])
);
};
- return map(progressGoals, ({ dateAuthentication }) =>
+ return map(activeProgressGoals, ({ dateAuthentication }) =>
generateMap(dateAuthentication)
);
- }, [progressGoals]);
+ }, [activeProgressGoals]);
const transformedProgressGoals = useMemo(
() =>
- map(progressGoals, (goal, index) => {
- console.log(progressGoals);
+ map(activeProgressGoals, (goal, index) => {
const transFormViewDays = map(goal.calender, (viewDay) => {
const isCertificationInfo = certificationInfoMaps[index].has(viewDay);
@@ -59,7 +66,7 @@ function ProgressGoal() {
: generateNonCertificationItem({ viewDay, day, isToday });
});
const dateString = `${generateDateString(goal.startDate)} ~ ${generateDateString(goal.endDate)}`;
- const daysArr = map(goal.dayOfWeek.split(","), (day) =>
+ const daysArr = map((goal.dayOfWeek || "").split(","), (day) =>
DAYS_STRING_MAP.get(day)
);
const days = daysArr.length === 7 ? "매일" : join(daysArr, ",");
@@ -74,7 +81,7 @@ function ProgressGoal() {
};
}),
- [progressGoals, certificationInfoMaps, todayString]
+ [activeProgressGoals, certificationInfoMaps, todayString]
);
return transformedProgressGoals.length > 0 ? (
diff --git a/src/features/goal/routes/CreateGoal.tsx b/src/features/goal/routes/CreateGoal.tsx
index 0c466d5..b9edc1e 100644
--- a/src/features/goal/routes/CreateGoal.tsx
+++ b/src/features/goal/routes/CreateGoal.tsx
@@ -3,19 +3,24 @@ import { useCallback, useEffect, useMemo, useState } from "react";
import containTargetUrl from "@/asset/map/contain-target.svg?url";
import {
getTempGoal,
+ patchTempGoal,
postCreateGoal,
postCreateTempSaveGoal
} from "@/features/goal/api/goal";
import BalanceInfo from "@/features/goal/components/create-goal/BalanceInfo";
import DatePicker from "@/features/goal/components/create-goal/DatePicker";
import DayPicker from "@/features/goal/components/create-goal/DayPicker";
-import { DAY_MAPPING } from "@/features/goal/components/create-goal/goal.constants";
+import {
+ DAY_MAPPING,
+ REVERSE_DAY_MAPPING
+} from "@/features/goal/components/create-goal/goal.constants";
import SaveButtons from "@/features/goal/components/create-goal/SaveButtons";
import { GoalData, GoalStatus } from "@/features/goal/types/goal-create";
import { getPoint } from "@/features/point/api/point";
import { useQueryClient } from "@tanstack/react-query";
import { format } from "date-fns";
import { debounce, isNull } from "es-toolkit";
+import { map } from "es-toolkit/compat";
import { Map, MapMarker } from "react-kakao-maps-sdk";
import { useLocation, useNavigate } from "react-router";
import { toast } from "react-toastify";
@@ -27,7 +32,7 @@ import { paths } from "@/config/paths";
import { generate_qo_getGoals as generate_qo_home } from "@/lib/react-query/queryOptions/home.ts";
interface CreateGoalProps {
- goalId?: number;
+ goalId: number;
}
const CreateGoal: React.FC = ({ goalId }) => {
@@ -60,9 +65,10 @@ const CreateGoal: React.FC = ({ goalId }) => {
goalName.trim().length >= 2 &&
!!startDate &&
!!endDate &&
- selectedDays.length > 0
+ selectedDays.length > 0 &&
+ targetLocation.trim().length > 0
);
- }, [goalName, startDate, endDate, selectedDays]);
+ }, [goalName, startDate, endDate, selectedDays, targetLocation]);
const fetchBalancePoint = useCallback(async (): Promise => {
try {
@@ -85,11 +91,13 @@ const CreateGoal: React.FC = ({ goalId }) => {
[]
);
+ const formStatus = useMemo(() => {
+ return goalId === -1 ? GoalStatus.ACTIVE : GoalStatus.DRAFT;
+ }, [goalId]);
+
useEffect(() => {
if (isNull(location.state)) return;
- console.log(location.state);
-
if (location.state.position && location.state.placeName) {
const { position, placeName } = location.state;
setTargetLocation(placeName);
@@ -120,24 +128,37 @@ const CreateGoal: React.FC = ({ goalId }) => {
}, [fetchBalancePoint, userId]);
useEffect(() => {
- if (!goalId) return;
+ const locationStateGoalId = location.state?.goalId;
+ if (goalId === -1 || !locationStateGoalId) return;
const fetchGoalData = async (): Promise => {
try {
- const { goal } = await getTempGoal({ goalId });
- const { name = "", startDate, endDate, locationName = "" } = goal; // ✅ goal 내부 값도 구조 분해!
+ const res = await getTempGoal({ goalId });
+ const {
+ name = "",
+ startDate,
+ endDate,
+ locationName = "",
+ dayOfWeek,
+ latitude,
+ longitude
+ } = res; // ✅ goal 내부 값도 구조 분해!
setGoalName(name);
setStartDate(startDate ? new Date(startDate) : null);
setEndDate(endDate ? new Date(endDate) : null);
setTargetLocation(locationName);
+ setPosition({ lat: latitude, lng: longitude });
+ setSelectedDays(
+ map(dayOfWeek.split(","), (item) => REVERSE_DAY_MAPPING[item])
+ );
} catch (error) {
console.error("임시 목표 데이터 불러오기 실패:", error);
}
};
fetchGoalData();
- }, [goalId]);
+ }, [goalId, location.state]);
const handleDateClick = (mode: "start" | "end"): void => {
navigate(paths.goal.date.path, {
@@ -172,9 +193,14 @@ const CreateGoal: React.FC = ({ goalId }) => {
};
try {
- status === GoalStatus.DRAFT
- ? await postCreateTempSaveGoal({ data: goalData })
- : await postCreateGoal({ data: goalData });
+ if (formStatus === GoalStatus.ACTIVE) {
+ status === GoalStatus.DRAFT
+ ? await postCreateTempSaveGoal({ data: goalData })
+ : await postCreateGoal({ data: goalData });
+ } else {
+ if (goalId === -1) return;
+ await patchTempGoal({ data: goalData, pathParam: { goalId } });
+ }
setPoint(useUserStore.getState().point - 200);
const homeKey = generate_qo_home.DELETE_KEY(userId);
@@ -194,7 +220,8 @@ const CreateGoal: React.FC = ({ goalId }) => {
goalName,
startDate,
endDate,
- selectedDays
+ selectedDays,
+ goalId
})
);
};
@@ -269,6 +296,7 @@ const CreateGoal: React.FC = ({ goalId }) => {
onTempSave={() => handleSaveWithStatus(GoalStatus.DRAFT)}
onSave={() => handleSaveWithStatus(GoalStatus.ACTIVE)}
isFormValid={isFormValid}
+ formStatus={formStatus}
/>
);
diff --git a/src/features/goal/types/index.ts b/src/features/goal/types/index.ts
index 89652a6..22615b1 100644
--- a/src/features/goal/types/index.ts
+++ b/src/features/goal/types/index.ts
@@ -1,8 +1,8 @@
export type STATUS = "DRAF" | "ACTIVE" | "COMPLETE";
export interface CertificationInfo {
- date: string;
- verified: boolean;
+ achievedAt: string;
+ achievedSuccess: boolean;
}
export interface ProgressGoal {
diff --git a/src/features/home/components/home.tsx b/src/features/home/components/home.tsx
index aacc315..6a0775d 100644
--- a/src/features/home/components/home.tsx
+++ b/src/features/home/components/home.tsx
@@ -52,7 +52,7 @@ const HomePage = () => {
const redirectionCallback = () => {
const url =
item.status === "DRAFT"
- ? paths.goal.create.getHref()
+ ? `${paths.goal.create.getHref()}?goalId=${item.goalId}`
: paths.map.certification.getHref();
navigate(url, { state: { goalId: item.goalId, userId } });
@@ -72,7 +72,7 @@ const HomePage = () => {
return (
-
+
diff --git a/src/features/home/components/index.const.ts b/src/features/home/components/index.const.ts
index 9a866b6..647e991 100644
--- a/src/features/home/components/index.const.ts
+++ b/src/features/home/components/index.const.ts
@@ -18,6 +18,8 @@ export const generatDdateText = (startDate: string, endDate: string) => {
};
export const generateDayText = (days: string) => {
+ if (!days) return "";
+
const dayArr = days.split(",");
const transformDays = map(dayArr, (day) => DAYS_STRING_MAP.get(day));
const day = dayArr.length === 7 ? "매일" : join(transformDays, ", ");
diff --git a/src/features/map-certification/api/index.ts b/src/features/map-certification/api/index.ts
index 86da477..357b450 100644
--- a/src/features/map-certification/api/index.ts
+++ b/src/features/map-certification/api/index.ts
@@ -19,6 +19,6 @@ export function postGoalsAchieve({
}: RoDataAndPathParamsType<
{ userId: number; latitude: number; longitude: number },
{ goalId: number }
->): R<{ totalPoints: number; bonusPoints: number }> {
+>): R<{ totalPoints: number; currentPoints: number }> {
return POST({ url: GOALS_ACHIEVE(goalId), data });
}
diff --git a/src/features/map-certification/components/certification-button.tsx b/src/features/map-certification/components/certification-button.tsx
index e330def..8d9ab05 100644
--- a/src/features/map-certification/components/certification-button.tsx
+++ b/src/features/map-certification/components/certification-button.tsx
@@ -6,7 +6,7 @@ interface CertificationButtonProps {
isContainRadar: boolean;
isPending: boolean;
mutate: UseMutateFunction<
- { totalPoints: number; bonusPoints: number },
+ { totalPoints: number; currentPoints: number },
AxiosError,
void,
unknown
diff --git a/src/features/map-certification/components/goals-info.tsx b/src/features/map-certification/components/goals-info.tsx
index 8a64f55..674cf74 100644
--- a/src/features/map-certification/components/goals-info.tsx
+++ b/src/features/map-certification/components/goals-info.tsx
@@ -8,16 +8,16 @@ interface GoalsInfoProps {
function GoalsInfo({ name, timeString, dayString }: GoalsInfoProps) {
return (
-
-
+
+
{name}
-
+
목표기간
{timeString}
-
+
반복요일
{dayString}
diff --git a/src/features/map-certification/components/map-certification.tsx b/src/features/map-certification/components/map-certification.tsx
index e6d5c71..1ccfd37 100644
--- a/src/features/map-certification/components/map-certification.tsx
+++ b/src/features/map-certification/components/map-certification.tsx
@@ -26,7 +26,7 @@ import {
generateInitialGoalsData
} from "./map-certification.const";
-const DISTANCE_DIFFRENCE = 20;
+const DISTANCE_DIFFRENCE = 100;
function MapCertification() {
// Hooks
@@ -69,14 +69,13 @@ function MapCertification() {
addPoint(data.totalPoints);
navigate("/map/certification/success", {
- state: { name, point: data.bonusPoints }
+ state: { name, point: data.currentPoints }
});
}
});
// useMemos
const isContainRadar = useMemo(() => {
- console.log(position.lat, position.lng, serverLatitude, serverLongitude);
if (!serverLatitude || !serverLongitude) return false;
const distance =
@@ -119,7 +118,7 @@ function MapCertification() {