diff --git a/src/components/ui/header/index.const.ts b/src/components/ui/header/index.const.ts
index b4fc1ce..89bd46b 100644
--- a/src/components/ui/header/index.const.ts
+++ b/src/components/ui/header/index.const.ts
@@ -27,8 +27,8 @@ export const backgroundImages = [
];
export const generateKeyword = (point: number) => {
- if (point <= 500) return "coffee";
- else if (point > 500) return "convenienceStore";
+ if (point <= 5000) return "coffee";
+ else if (point > 5000 && point < 10000) return "convenienceStore";
else return "reward";
};
@@ -41,7 +41,7 @@ export const generateIcon = (
};
export const generateProgressPercent = (point: number) => {
- if (point <= 500) return (point / 500) * 100;
- else if (point > 500) return (point / 1000) * 100;
+ if (point <= 5000) return (point / 5000) * 100;
+ else if (point > 5000 && point < 10000) return (point / 10000) * 100;
else return 100;
};
diff --git a/src/components/ui/header/profile-header.tsx b/src/components/ui/header/profile-header.tsx
index 8f8a755..689a712 100644
--- a/src/components/ui/header/profile-header.tsx
+++ b/src/components/ui/header/profile-header.tsx
@@ -8,7 +8,10 @@ import { Progress } from "@/components/ui/progress";
import { useEffect, useMemo, useState } from "react";
import InfoIcon from "@/asset/common/info.svg?react";
+import { Link } from "react-router";
+import { useUserStore } from "@/stores/user";
+import { paths } from "@/config/paths";
import {
backgroundImages,
generateIcon,
@@ -16,37 +19,50 @@ import {
generateProgressPercent
} from "./index.const";
-/** @todo 전역 값 사용, 관련 데이터 전역 값으로 수정 */
-const fakePoint1 = 550;
-
function ProfileHeader() {
- /** @todo 전역 상태 정의(사용자명, 포인트) **/
+ const { userName, point } = useUserStore();
const [bgImage, setBgImage] = useState("");
- /** @todo 전역 상태 사용(포인트) **/
const rewarndInfo = useMemo(() => {
- const keyword = generateKeyword(fakePoint1);
+ const keyword = generateKeyword(point);
const Icon = generateIcon(keyword);
- const progressPercent = generateProgressPercent(fakePoint1);
+ const progressPercent = generateProgressPercent(point);
const currentPointText = (
<>
- 현재 포인트 {fakePoint1}p
- >
- );
- const targetPointText = (
- <>
- 커피쿠폰까지 {fakePoint1}p
+ 현재 포인트 {point}p
>
);
+ const generateTagetPointText = () => {
+ if (keyword === "coffee") {
+ return (
+ <>
+ 커피쿠폰까지 {5000 - point}p
+ >
+ );
+ } else if (keyword === "convenienceStore") {
+ return (
+ <>
+ 편의점쿠폰까지{" "}
+ {10000 - point}p
+ >
+ );
+ } else {
+ return (
+
+ 리워드를 수령해주세요.
+
+ );
+ }
+ };
return {
icon: ,
progressPercent,
currentPointText,
- targetPointText
+ targetPointText: generateTagetPointText()
};
- }, []);
+ }, [point]);
useEffect(() => {
const randomBg =
@@ -76,8 +92,8 @@ function ProfileHeader() {
- {/* @todo 전역 Username 사용 */}
- 안녕하세요! 홍길동님
+ 안녕하세요!
+ {userName}님
오늘도 일단 가볼까요?
diff --git a/src/components/ui/navbar/index.const.ts b/src/components/ui/navbar/index.const.ts
index 7882c08..6e9fe00 100644
--- a/src/components/ui/navbar/index.const.ts
+++ b/src/components/ui/navbar/index.const.ts
@@ -12,7 +12,8 @@ export const NOT_VISIBLE_NAVBAR_PAGES = [
paths.auth.agreement.getHref(),
paths.auth.phoneNumber.getHref(),
paths.map.search.getHref(),
- paths.map.certification.getHref()
+ paths.map.certification.getHref(),
+ paths.auth.login.getHref()
];
const isEqualPath = (locationPath: string, path: string) => {
@@ -38,7 +39,7 @@ export const generateNavbarInfo = (locationPath: string) => [
label: "내정보",
activeIcon: User,
notActiveIcon: UserDisable,
- to: "/profile",
- isActvie: isEqualPath(locationPath, "/profile")
+ to: "/user/my-page",
+ isActvie: isEqualPath(locationPath, "/user/my-page")
}
];
diff --git a/src/features/auth/api/auth.ts b/src/features/auth/api/auth.ts
index 2a5a49c..cb006fb 100644
--- a/src/features/auth/api/auth.ts
+++ b/src/features/auth/api/auth.ts
@@ -22,6 +22,6 @@ export function postPhoneNumber({ data }: { data: { phoneNumber: string } }) {
return POST({ url: `${BASE_PATH}/user/phone`, data });
}
-export function postTermsAgree({ data }: { data: { userId: number } }) {
- return POST({ url: `/terms/agree`, data });
+export function postTermsAgree(pathParam: { userId: number }) {
+ return POST({ url: `/api/v1/terms/agree/${pathParam.userId}` });
}
diff --git a/src/features/auth/components/Agreement.tsx b/src/features/auth/components/Agreement.tsx
index f9d285d..8f0ccfe 100644
--- a/src/features/auth/components/Agreement.tsx
+++ b/src/features/auth/components/Agreement.tsx
@@ -5,6 +5,7 @@ import CheckedItemIcon from "@/asset/agreement/checked-item.svg?url";
import UncheckedAllIcon from "@/asset/agreement/unchecked-all.svg?url";
import UncheckedItemIcon from "@/asset/agreement/unchecked-item.svg?url";
import { postTermsAgree } from "@/features/auth/api/auth";
+import { getUserInfo } from "@/features/user/api/user";
import { useNavigate } from "react-router";
import { useAuthStore } from "@/stores/auth-store";
@@ -68,11 +69,11 @@ const Agreement = () => {
if (!userId) return;
try {
- const response = await postTermsAgree({
- data: { userId }
- });
+ await postTermsAgree({ userId });
- if (response.data.success) {
+ const { phoneNumber } = await getUserInfo({ pathParam: userId });
+
+ if (!phoneNumber) {
navigate(paths.auth.phoneNumber.path);
} else {
throw new Error("약관 동의에 실패했습니다.");
@@ -159,7 +160,7 @@ const Agreement = () => {
disabled={!checkState.all}
onClick={handleSubmit}
className={`fixed bottom-6 ml-[20px] flex h-[56px] w-[335px] items-center justify-center rounded-[8px] py-[10px] text-[16px] font-semibold leading-[20px] ${
- !checkState.all
+ checkState.all
? "cursor-pointer bg-[#3CC360] text-white"
: "cursor-not-allowed bg-gray-300 text-gray-500"
}`}
diff --git a/src/features/auth/components/PhoneNumber.tsx b/src/features/auth/components/PhoneNumber.tsx
index d9cecab..33e49fc 100644
--- a/src/features/auth/components/PhoneNumber.tsx
+++ b/src/features/auth/components/PhoneNumber.tsx
@@ -22,8 +22,7 @@ const PhoneNumber = () => {
const response = await postPhoneNumber({
data: { phoneNumber }
});
-
- if (response.data.success) {
+ if (response.message === "전화번호가 성공적으로 저장됨") {
navigate(paths.home.path);
} else {
throw new Error("약관 동의에 실패했습니다.");
diff --git a/src/features/auth/routes/GoogleCallback.tsx b/src/features/auth/routes/GoogleCallback.tsx
index 0d7a8ac..258f2d3 100644
--- a/src/features/auth/routes/GoogleCallback.tsx
+++ b/src/features/auth/routes/GoogleCallback.tsx
@@ -12,7 +12,7 @@ import { useUserStore } from "@/stores/user";
import { paths } from "@/config/paths";
const GoogleCallback = (): JSX.Element | null => {
- const { setUserName, setPoint } = useUserStore();
+ const { setUserName, setPoint, setUserId } = useUserStore();
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const setTokens = useAuthStore((state) => state.setTokens);
@@ -28,10 +28,12 @@ const GoogleCallback = (): JSX.Element | null => {
throw new Error("구글 인증에 실패했습니다.");
}
- const { accessToken, refreshToken, username, userId } = response.data;
+ const { accessToken, refreshToken, username, id, userId } =
+ response.data;
- setTokens(accessToken, refreshToken, userId);
+ setTokens(accessToken, refreshToken, id);
setUserName(username);
+ setUserId(userId);
const { totalPoints } = await getPoint({ pathParams: { userId } });
setPoint(totalPoints);
@@ -44,7 +46,7 @@ const GoogleCallback = (): JSX.Element | null => {
setIsLoading(false);
}
},
- [navigate, setTokens, setUserName, setPoint]
+ [navigate, setTokens, setUserName, setPoint, setUserId]
);
useEffect(() => {
diff --git a/src/features/auth/routes/KakaoCallback.tsx b/src/features/auth/routes/KakaoCallback.tsx
index f9d9fdd..478c970 100644
--- a/src/features/auth/routes/KakaoCallback.tsx
+++ b/src/features/auth/routes/KakaoCallback.tsx
@@ -12,7 +12,7 @@ import { useUserStore } from "@/stores/user";
import { paths } from "@/config/paths";
const KakaoCallback = (): JSX.Element | null => {
- const { setUserName, setPoint } = useUserStore();
+ const { setUserName, setPoint, setUserId } = useUserStore();
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const setTokens = useAuthStore((state) => state.setTokens);
@@ -30,15 +30,19 @@ const KakaoCallback = (): JSX.Element | null => {
throw new Error("카카오 인증에 실패했습니다.");
}
- const { accessToken, refreshToken, username, userId } = response.data;
+ const { accessToken, refreshToken, username, id } = response.data;
- setTokens(accessToken, refreshToken, userId);
+ console.log(response.data);
+
+ setTokens(accessToken, refreshToken, id);
setUserName(username);
+ setUserId(id);
- const { totalPoints } = await getPoint({ pathParams: { userId } });
+ const { totalPoints } = await getPoint({ pathParams: { userId: id } });
setPoint(totalPoints);
navigate(paths.home.path);
+ // navigate(paths.auth.agreement.path);
} catch (error) {
console.error(error);
setIsError(true);
@@ -46,7 +50,7 @@ const KakaoCallback = (): JSX.Element | null => {
setIsLoading(false);
}
},
- [navigate, setTokens, setUserName, setPoint]
+ [navigate, setTokens, setUserName, setPoint, setUserId]
);
useEffect(() => {
diff --git a/src/features/goal/api/goal.ts b/src/features/goal/api/goal.ts
index cb39fb2..2e72256 100644
--- a/src/features/goal/api/goal.ts
+++ b/src/features/goal/api/goal.ts
@@ -3,27 +3,32 @@ import { GoalData } from "@/features/goal/types/goal-create";
import type { R } from "@/types/common.ts";
import { GET, POST } from "@/lib/axios";
+import { RoOnlyPathParamsType, RoOnlyQueryType } from "@/lib/axios/utils";
import type { CompleteGoal, ProgressGoal } from "../types";
import { GOALS_CHECK, GOALS_COMPLETE } from "./path";
-export function getGoalsCheck(): R {
- return GET({ url: GOALS_CHECK });
+export function getGoalsCheck({
+ query
+}: RoOnlyQueryType<{ userId: number }>): R {
+ return GET({ url: GOALS_CHECK, params: query });
}
-export function getGoalsComplete(): R {
- return GET({ url: GOALS_COMPLETE });
+export function getGoalsComplete({
+ pathParams: { userId }
+}: RoOnlyPathParamsType<{ userId: number }>): R {
+ return GET({ url: GOALS_COMPLETE(userId) });
}
export function postCreateGoal({ data }: { data: GoalData }) {
return POST({
- url: `${BASE_PATH}`,
+ url: `${BASE_PATH}/create`,
data
});
}
export function postCreateTempSaveGoal({ data }: { data: GoalData }) {
return POST({
- url: `${BASE_PATH}`,
+ url: `${BASE_PATH}/create`,
data
});
}
diff --git a/src/features/goal/api/path.ts b/src/features/goal/api/path.ts
index bba9fe7..cec56aa 100644
--- a/src/features/goal/api/path.ts
+++ b/src/features/goal/api/path.ts
@@ -3,4 +3,5 @@ import { generatePathByBase, genreateBasePath } from "@/lib/axios/utils";
export const BASE_PATH = genreateBasePath("goals", "v1");
export const GOALS_CHECK = generatePathByBase(BASE_PATH, "check");
-export const GOALS_COMPLETE = generatePathByBase(BASE_PATH, "complete");
+export const GOALS_COMPLETE = (userId: number) =>
+ generatePathByBase(GOALS_CHECK, "complete", String(userId));
diff --git a/src/features/goal/components/create-goal/BalanceInfo.tsx b/src/features/goal/components/create-goal/BalanceInfo.tsx
index 8c56aca..e82c25b 100644
--- a/src/features/goal/components/create-goal/BalanceInfo.tsx
+++ b/src/features/goal/components/create-goal/BalanceInfo.tsx
@@ -15,13 +15,13 @@ const BalanceInfo: React.FC = ({ balancePoint }) => (
-
+
보유 포인트
-
+
- {balancePoint}
+ {balancePoint.toLocaleString()}
p
diff --git a/src/features/goal/components/goal/complete-goal.tsx b/src/features/goal/components/goal/complete-goal.tsx
index d1feb30..54a7009 100644
--- a/src/features/goal/components/goal/complete-goal.tsx
+++ b/src/features/goal/components/goal/complete-goal.tsx
@@ -3,20 +3,26 @@ import { useMemo } from "react";
import { useQuery } from "@tanstack/react-query";
import { join, map } from "es-toolkit/compat";
+import { useUserStore } from "@/stores/user";
import { generate_qo_getGoalsComplete } from "@/lib/react-query/queryOptions/goals";
import { generateDateString } from "./index.const";
function CompleteGoal() {
+ const { userId } = useUserStore();
const { data: completedGoals = [] } = useQuery(
- generate_qo_getGoalsComplete()
+ generate_qo_getGoalsComplete(userId)
);
const transformedCompletedGoals = useMemo(
() =>
map(completedGoals, (goal) => {
const dateString = `${generateDateString(goal.startDate)} ~ ${generateDateString(goal.endDate)}`;
- const days = goal.days.length === 7 ? "매일" : join(goal.days, ",");
- const achivePercentString = `${goal.achivePercent}% 달성`;
+ const daysArr = goal.dayOfWeek.split(",");
+ const days = daysArr.length === 7 ? "매일" : join(daysArr, ",");
+ const achivePercent = Math.floor(
+ (goal.achievedCount / goal.targetCount) * 100
+ );
+ const achivePercentString = `${achivePercent}% 달성`;
return { ...goal, dateString, days, achivePercentString };
}),
diff --git a/src/features/goal/components/goal/progress-goal.tsx b/src/features/goal/components/goal/progress-goal.tsx
index 4f97cdb..21faebd 100644
--- a/src/features/goal/components/goal/progress-goal.tsx
+++ b/src/features/goal/components/goal/progress-goal.tsx
@@ -1,8 +1,10 @@
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 { useUserStore } from "@/stores/user";
import { generate_qo_getGoalsCheck } from "@/lib/react-query/queryOptions/goals";
import type { CertificationInfo } from "../../types";
import {
@@ -15,28 +17,29 @@ import {
function ProgressGoal() {
const todayString = generateTodyString();
+ const { userId } = useUserStore();
- const { data: progressGoals = [] } = useQuery(generate_qo_getGoalsCheck());
+ const { data: progressGoals = [] } = useQuery(
+ generate_qo_getGoalsCheck(userId)
+ );
const certificationInfoMaps = useMemo(() => {
const generateMap = (certificationInfo: CertificationInfo[]) => {
return new Map(
- map(certificationInfo, ({ date, isCertification }) => [
- date,
- isCertification
- ])
+ map(certificationInfo, ({ date, verified }) => [date, verified])
);
};
- return map(progressGoals, ({ certificationInfo }) =>
- generateMap(certificationInfo)
+ return map(progressGoals, ({ dateAuthentication }) =>
+ generateMap(dateAuthentication)
);
}, [progressGoals]);
const transformedProgressGoals = useMemo(
() =>
map(progressGoals, (goal, index) => {
- const transFormViewDays = map(goal.viewDays, (viewDay) => {
+ console.log(progressGoals);
+ const transFormViewDays = map(goal.calender, (viewDay) => {
const isCertificationInfo = certificationInfoMaps[index].has(viewDay);
if (!isCertificationInfo) {
@@ -56,7 +59,10 @@ function ProgressGoal() {
: generateNonCertificationItem({ viewDay, day, isToday });
});
const dateString = `${generateDateString(goal.startDate)} ~ ${generateDateString(goal.endDate)}`;
- const days = goal.days.length === 7 ? "매일" : join(goal.days, ",");
+ const daysArr = map(goal.dayOfWeek.split(","), (day) =>
+ DAYS_STRING_MAP.get(day)
+ );
+ const days = daysArr.length === 7 ? "매일" : join(daysArr, ",");
const lastWeekDate = slice(transFormViewDays, 0, 7);
const thiwWeekDate = slice(transFormViewDays, -7);
@@ -73,7 +79,7 @@ function ProgressGoal() {
return transformedProgressGoals.length > 0 ? (
transformedProgressGoals.map(
- ({ id, name, dateString, days, goalDaycnt, allDays }, idx) => (
+ ({ id, name, dateString, days, targetCount, allDays }, idx) => (
{dateString}
- {goalDaycnt}일
+ {targetCount}일
{days}
diff --git a/src/features/goal/routes/CreateGoal.tsx b/src/features/goal/routes/CreateGoal.tsx
index 9d8ed44..0c466d5 100644
--- a/src/features/goal/routes/CreateGoal.tsx
+++ b/src/features/goal/routes/CreateGoal.tsx
@@ -13,14 +13,18 @@ import { DAY_MAPPING } from "@/features/goal/components/create-goal/goal.constan
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 { debounce } from "es-toolkit";
+import { useQueryClient } from "@tanstack/react-query";
+import { format } from "date-fns";
+import { debounce, isNull } from "es-toolkit";
import { Map, MapMarker } from "react-kakao-maps-sdk";
import { useLocation, useNavigate } from "react-router";
import { toast } from "react-toastify";
import { Nullable } from "@/types/common";
import { useAuthStore } from "@/stores/auth-store";
+import { useUserStore } from "@/stores/user";
import { paths } from "@/config/paths";
+import { generate_qo_getGoals as generate_qo_home } from "@/lib/react-query/queryOptions/home.ts";
interface CreateGoalProps {
goalId?: number;
@@ -29,7 +33,9 @@ interface CreateGoalProps {
const CreateGoal: React.FC = ({ goalId }) => {
const location = useLocation();
const navigate = useNavigate();
- const userId = useAuthStore((state) => state.userId);
+ const client = useQueryClient();
+ const { userId } = useAuthStore();
+ const { setPoint } = useUserStore();
const [goalName, setGoalName] = useState(
location.state?.goalName || ""
@@ -45,7 +51,9 @@ const CreateGoal: React.FC = ({ goalId }) => {
const [balancePoint, setBalancePoint] = useState(0);
const [selectedDays, setSelectedDays] = useState([]);
const [center, setCenter] = useState({ lat: 33.450701, lng: 126.570667 });
- const [position, setPosition] = useState(null);
+ const [position, setPosition] = useState<{ lat: number; lng: number } | null>(
+ null
+ );
const isFormValid = useMemo(() => {
return (
@@ -57,8 +65,8 @@ const CreateGoal: React.FC = ({ goalId }) => {
}, [goalName, startDate, endDate, selectedDays]);
const fetchBalancePoint = useCallback(async (): Promise => {
- if (!userId) return;
try {
+ if (!userId) return;
const { totalPoints } = await getPoint({ userId });
setBalancePoint(totalPoints);
} catch (error) {
@@ -78,11 +86,25 @@ const CreateGoal: React.FC = ({ goalId }) => {
);
useEffect(() => {
- if (location.state) {
+ if (isNull(location.state)) return;
+
+ console.log(location.state);
+
+ if (location.state.position && location.state.placeName) {
const { position, placeName } = location.state;
setTargetLocation(placeName);
setPosition(position);
setCenter(position);
+
+ const goalInfo = JSON.parse(localStorage.getItem("goalInfo") ?? "{}");
+
+ if (goalInfo === "{}") return;
+
+ const { goalName, startDate, endDate, selectedDays } = goalInfo;
+ setGoalName(goalName);
+ setStartDate(startDate ? new Date(startDate) : null);
+ setEndDate(endDate ? new Date(endDate) : null);
+ setSelectedDays(selectedDays);
} else {
navigator.geolocation.getCurrentPosition(
({ coords: { latitude, longitude } }) => {
@@ -138,28 +160,44 @@ const CreateGoal: React.FC = ({ goalId }) => {
}
const goalData: GoalData = {
- goal: {
- userId,
- name: goalName,
- startDate: startDate ? startDate.toISOString() : null,
- endDate: endDate ? endDate.toISOString() : null,
- locationName: targetLocation
- },
+ userId,
+ name: goalName,
+ startDate: startDate ? format(startDate, "yyyy-MM-dd") : null,
+ endDate: endDate ? format(endDate, "yyyy-MM-dd") : null,
+ locationName: targetLocation,
status,
- days: selectedDays.map((day) => DAY_MAPPING[day])
+ latitude: position?.lat ?? 0,
+ longitude: position?.lng ?? 0,
+ selectedDays: selectedDays.map((day) => DAY_MAPPING[day])
};
try {
status === GoalStatus.DRAFT
? await postCreateTempSaveGoal({ data: goalData })
: await postCreateGoal({ data: goalData });
- navigate(paths.goal.root.path);
+
+ setPoint(useUserStore.getState().point - 200);
+ const homeKey = generate_qo_home.DELETE_KEY(userId);
+ client.invalidateQueries({ queryKey: homeKey });
+
+ navigate(paths.home.path);
} catch (error) {
console.error(error);
}
};
- const navigatePositionSearch = () => navigate(paths.map.search.getHref());
+ const navigatePositionSearch = () => {
+ navigate(paths.map.search.getHref());
+ localStorage.setItem(
+ "goalInfo",
+ JSON.stringify({
+ goalName,
+ startDate,
+ endDate,
+ selectedDays
+ })
+ );
+ };
return (
diff --git a/src/features/goal/types/goal-create.tsx b/src/features/goal/types/goal-create.tsx
index 86bdb62..1f41658 100644
--- a/src/features/goal/types/goal-create.tsx
+++ b/src/features/goal/types/goal-create.tsx
@@ -4,13 +4,13 @@ export enum GoalStatus {
}
export type GoalData = {
- goal: {
- userId: number;
- name: string;
- startDate: string | null;
- endDate: string | null;
- locationName: string;
- };
+ userId: number;
+ name: string;
+ startDate: string | null;
+ endDate: string | null;
+ latitude: number;
+ longitude: number;
+ locationName: string;
status: GoalStatus;
- days: string[];
+ selectedDays: string[];
};
diff --git a/src/features/goal/types/index.ts b/src/features/goal/types/index.ts
index 29db28b..89652a6 100644
--- a/src/features/goal/types/index.ts
+++ b/src/features/goal/types/index.ts
@@ -1,26 +1,29 @@
-import type { WeekDay } from "@/types/date";
+export type STATUS = "DRAF" | "ACTIVE" | "COMPLETE";
export interface CertificationInfo {
date: string;
- isCertification: boolean;
+ verified: boolean;
}
export interface ProgressGoal {
id: number;
+ userId: number;
name: string;
+ status: STATUS;
startDate: string;
endDate: string;
- goalDaycnt: number;
- days: WeekDay[];
- viewDays: string[];
- certificationInfo: CertificationInfo[];
+ targetCount: number;
+ dayOfWeek: string;
+ calender: string[];
+ dateAuthentication: CertificationInfo[];
}
export interface CompleteGoal {
id: number;
name: string;
- achivePercent: number;
startDate: string;
endDate: string;
- days: WeekDay[];
+ dayOfWeek: string;
+ targetCount: number;
+ achievedCount: number;
}
diff --git a/src/features/home/api/home.ts b/src/features/home/api/home.ts
index d91b818..9823a7c 100644
--- a/src/features/home/api/home.ts
+++ b/src/features/home/api/home.ts
@@ -1,8 +1,11 @@
import { R } from "@/types/common";
import { GET } from "@/lib/axios";
+import { RoOnlyPathParamsType } from "@/lib/axios/utils";
import { Goals } from "../types";
-import { BASE_PATH } from "./paths";
+import { HOME_PATH } from "./paths";
-export function getGoals(): R
{
- return GET({ url: BASE_PATH });
+export function getGoals({
+ pathParams: { userId }
+}: RoOnlyPathParamsType<{ userId: number }>): R {
+ return GET({ url: HOME_PATH(userId) });
}
diff --git a/src/features/home/api/paths.ts b/src/features/home/api/paths.ts
index 0b3de6d..058926d 100644
--- a/src/features/home/api/paths.ts
+++ b/src/features/home/api/paths.ts
@@ -1,3 +1,6 @@
-import { genreateBasePath } from "@/lib/axios/utils";
+import { generatePathByBase, genreateBasePath } from "@/lib/axios/utils";
export const BASE_PATH = genreateBasePath("goals", "v1");
+
+export const HOME_PATH = (userId: number) =>
+ generatePathByBase(BASE_PATH, String(userId));
diff --git a/src/features/home/components/goal-list.tsx b/src/features/home/components/goal-list.tsx
index c05a549..32f05aa 100644
--- a/src/features/home/components/goal-list.tsx
+++ b/src/features/home/components/goal-list.tsx
@@ -1,5 +1,4 @@
import { map } from "es-toolkit/compat";
-import { Link } from "react-router";
import type { TransformedGoals } from "../types";
@@ -22,41 +21,41 @@ function GoalList({ goalCount, transformGoals }: GoalListProps) {
{map(
transformGoals,
({
- id,
+ goalId,
title,
- name,
+ goalName,
lastRowText,
- isAchieved,
- isTemporarySaved,
+ achievedToday,
+ status,
buttonText,
- redirectionUrl
+ redirectionCallback
}) => (
{title}
- {name}
+ {goalName}
{lastRowText}
-
-
-
+
+
)
diff --git a/src/features/home/components/home.tsx b/src/features/home/components/home.tsx
index b553594..aacc315 100644
--- a/src/features/home/components/home.tsx
+++ b/src/features/home/components/home.tsx
@@ -6,15 +6,18 @@ import ActvieAddIcon from "@/asset/common/plus-actvie.svg?react";
import InactiveAddIcon from "@/asset/common/plus-inactive.svg?react";
import { useQuery } from "@tanstack/react-query";
import { map } from "es-toolkit/compat";
-import { Link } from "react-router";
+import { Link, useNavigate } from "react-router";
+import { useUserStore } from "@/stores/user";
import { paths } from "@/config/paths";
import { generate_qo_getGoals } from "@/lib/react-query/queryOptions/home";
import GoalList from "./goal-list";
import { generatDdateText, generateDayText } from "./index.const";
const HomePage = () => {
- const { data: goals = [] } = useQuery(generate_qo_getGoals());
+ const navigate = useNavigate();
+ const { userId } = useUserStore();
+ const { data: goals = [] } = useQuery(generate_qo_getGoals(userId));
const goalCount = useMemo(() => {
if (goals.length === 0) return 0;
@@ -41,24 +44,30 @@ const HomePage = () => {
const transformGoals = useMemo(
() =>
map(goals, (item) => {
- const lastRowText = item.isTemporarySaved
- ? "목표등록을 마무리하고 바로 시작해보세요!"
- : `${generatDdateText(item.startDate, item.endDate)} ${generateDayText(item.days)}`;
- const buttonText = `${item.isTemporarySaved ? "완성하기" : "인증하기"} >`;
- const redirectionUrl = item.isTemporarySaved
- ? paths.goal.create.getHref()
- : paths.map.certification.getHref();
+ const lastRowText =
+ item.status === "DRAFT"
+ ? "목표등록을 마무리하고 바로 시작해보세요!"
+ : `${generatDdateText(item.startDate, item.endDate)} ${generateDayText(item.dayOfWeek)}`;
+ const buttonText = `${item.status === "DRAFT" ? "완성하기" : "인증하기"} >`;
+ const redirectionCallback = () => {
+ const url =
+ item.status === "DRAFT"
+ ? paths.goal.create.getHref()
+ : paths.map.certification.getHref();
+
+ navigate(url, { state: { goalId: item.goalId, userId } });
+ };
return {
...item,
- title: `${item.isTemporarySaved ? "임시저장" : "진행중"} 목표`,
- name: item.name,
+ title: `${item.status === "DRAFT" ? "임시저장" : "진행중"} 목표`,
+ name: item.goalName,
lastRowText,
buttonText,
- redirectionUrl
+ redirectionCallback
};
}),
- [goals]
+ [goals, navigate, userId]
);
return (
diff --git a/src/features/home/components/index.const.ts b/src/features/home/components/index.const.ts
index 16165b5..9a866b6 100644
--- a/src/features/home/components/index.const.ts
+++ b/src/features/home/components/index.const.ts
@@ -1,8 +1,6 @@
import { join, map, replace } from "es-toolkit/compat";
-import { WeekDay } from "../types";
-
-export const DAYS_STRING_MAP = new Map([
+export const DAYS_STRING_MAP = new Map([
["MON", "월"],
["TUE", "화"],
["WED", "수"],
@@ -19,9 +17,10 @@ export const generatDdateText = (startDate: string, endDate: string) => {
return `${replacedStartDate} ~ ${replacedEndDate}`;
};
-export const generateDayText = (days: WeekDay[]) => {
- const transformDays = map(days, (day) => DAYS_STRING_MAP.get(day));
- const day = days.length === 7 ? "매일" : join(transformDays, ", ");
+export const generateDayText = (days: string) => {
+ const dayArr = days.split(",");
+ const transformDays = map(dayArr, (day) => DAYS_STRING_MAP.get(day));
+ const day = dayArr.length === 7 ? "매일" : join(transformDays, ", ");
return day;
};
diff --git a/src/features/home/types/index.ts b/src/features/home/types/index.ts
index c66c55e..3812866 100644
--- a/src/features/home/types/index.ts
+++ b/src/features/home/types/index.ts
@@ -1,18 +1,18 @@
-export type WeekDay = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN";
+export type STATUS = "DRAFT" | "ACTIVE" | "COMPLETE";
export interface Goals {
- id: number;
- name: string;
+ goalId: number;
+ goalName: string;
startDate: string;
endDate: string;
- days: WeekDay[];
- isTemporarySaved: boolean;
- isAchieved: boolean;
+ dayOfWeek: string;
+ status: STATUS;
+ achievedToday: boolean;
}
export type TransformedGoals = Goals & {
title: string;
lastRowText: string;
buttonText: string;
- redirectionUrl: string;
+ redirectionCallback: () => void;
};
diff --git a/src/features/map-certification/api/index.ts b/src/features/map-certification/api/index.ts
index 71ea063..86da477 100644
--- a/src/features/map-certification/api/index.ts
+++ b/src/features/map-certification/api/index.ts
@@ -1,17 +1,24 @@
import type { R } from "@/types/common";
import { GET, POST } from "@/lib/axios";
-import type { RoOnlyPathParamsType } from "@/lib/axios/utils";
-import type { Goals } from "../types";
-import { GOALS, GOALS_ACHIEVE } from "./paths";
+import type {
+ RoDataAndPathParamsType,
+ RoOnlyPathParamsType
+} from "@/lib/axios/utils";
+import type { GoalDetail } from "../types";
+import { GOALS_ACHIEVE, GOALS_DETAIL } from "./paths";
export function getGoals({
- pathParams: { id }
-}: RoOnlyPathParamsType<{ id: number }>): R {
- return GET({ url: GOALS(id) });
+ pathParams: { goalId }
+}: RoOnlyPathParamsType<{ goalId: number }>): R {
+ return GET({ url: GOALS_DETAIL(goalId) });
}
export function postGoalsAchieve({
- pathParams: { id }
-}: RoOnlyPathParamsType<{ id: number }>): R<{ point: number }> {
- return POST({ url: GOALS_ACHIEVE(id) });
+ pathParams: { goalId },
+ data
+}: RoDataAndPathParamsType<
+ { userId: number; latitude: number; longitude: number },
+ { goalId: number }
+>): R<{ totalPoints: number; bonusPoints: number }> {
+ return POST({ url: GOALS_ACHIEVE(goalId), data });
}
diff --git a/src/features/map-certification/api/paths.ts b/src/features/map-certification/api/paths.ts
index 6f6c46f..e7116af 100644
--- a/src/features/map-certification/api/paths.ts
+++ b/src/features/map-certification/api/paths.ts
@@ -2,7 +2,11 @@ import { generatePathByBase, genreateBasePath } from "@/lib/axios/utils";
export const BASE_PATH = genreateBasePath("goals", "v1");
-export const GOALS = (id: number) => generatePathByBase(BASE_PATH, String(id));
-export const GOALS_ACHIEVE = (id: number) => {
- return generatePathByBase(GOALS(id), "achieve");
+export const GOALS_CHECK = generatePathByBase(BASE_PATH, "check");
+
+export const GOALS_DETAIL = (goalId: number) =>
+ generatePathByBase(GOALS_CHECK, String(goalId));
+
+export const GOALS_ACHIEVE = (goalId: number) => {
+ return generatePathByBase(BASE_PATH, String(goalId), "achieve");
};
diff --git a/src/features/map-certification/components/certification-button.tsx b/src/features/map-certification/components/certification-button.tsx
index fb65a60..e330def 100644
--- a/src/features/map-certification/components/certification-button.tsx
+++ b/src/features/map-certification/components/certification-button.tsx
@@ -5,7 +5,12 @@ interface CertificationButtonProps {
buttonDisabled: boolean;
isContainRadar: boolean;
isPending: boolean;
- mutate: UseMutateFunction<{ point: number }, AxiosError, void, unknown>;
+ mutate: UseMutateFunction<
+ { totalPoints: number; bonusPoints: number },
+ AxiosError,
+ void,
+ unknown
+ >;
}
function CertificationButton({
diff --git a/src/features/map-certification/components/map-certification.const.ts b/src/features/map-certification/components/map-certification.const.ts
index 720ff71..c206ee3 100644
--- a/src/features/map-certification/components/map-certification.const.ts
+++ b/src/features/map-certification/components/map-certification.const.ts
@@ -5,7 +5,7 @@ export const generateInitialGoalsData = () => ({
endDate: "",
latitude: 33.450701,
longitude: 126.570667,
- days: []
+ dayOfWeek: ""
});
export const DAYS_STRING_MAP = new Map([
diff --git a/src/features/map-certification/components/map-certification.tsx b/src/features/map-certification/components/map-certification.tsx
index ef0e426..e6d5c71 100644
--- a/src/features/map-certification/components/map-certification.tsx
+++ b/src/features/map-certification/components/map-certification.tsx
@@ -8,7 +8,7 @@ import { getDistance } from "@/utils/map";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { join, map, replace } from "es-toolkit/compat";
import { Circle, Map, MapMarker } from "react-kakao-maps-sdk";
-import { useNavigate } from "react-router";
+import { useLocation, useNavigate } from "react-router";
import { useUserStore } from "@/stores/user";
import {
@@ -17,6 +17,7 @@ import {
generate_qo_getGoalsComplete,
generate_qo_postGoalsAchieve
} from "@/lib/react-query/queryOptions/goals";
+import { generate_qo_getGoals as generate_qo_home } from "@/lib/react-query/queryOptions/home.ts";
import useUserLocation from "@/hooks/useUserLocation";
import CertificationButton from "./certification-button";
import GoalsInfo from "./goals-info";
@@ -31,7 +32,9 @@ function MapCertification() {
// Hooks
const { addPoint } = useUserStore();
const navigate = useNavigate();
+ const location = useLocation();
const client = useQueryClient();
+ const { userId, goalId } = location.state || {};
const { center, position, setCenterToMyPosition, updateCenterWhenMapMoved } =
useUserLocation();
@@ -40,34 +43,40 @@ function MapCertification() {
name,
startDate,
endDate,
- days,
+ dayOfWeek: days,
latitude: serverLatitude,
longitude: serverLongitude
} = generateInitialGoalsData()
- } = useQuery(generate_qo_getGoals(1));
+ } = useQuery(generate_qo_getGoals(goalId));
const { mutate, isPending } = useMutation({
- ...generate_qo_postGoalsAchieve(1),
+ ...generate_qo_postGoalsAchieve({
+ goalId,
+ userId,
+ latitude: position.lat,
+ longitude: position.lng
+ }),
onSuccess: (data) => {
- const progressGoalKey = generate_qo_getGoalsCheck.DELETE_KEY();
- const completeGoalKey = generate_qo_getGoalsComplete.DELETE_KEY();
+ const progressGoalKey = generate_qo_getGoalsCheck.DELETE_KEY(userId);
+ const completeGoalKey = generate_qo_getGoalsComplete.DELETE_KEY(userId);
+ const homeKey = generate_qo_home.DELETE_KEY(userId);
Promise.all([
client.invalidateQueries({ queryKey: progressGoalKey }),
- client.invalidateQueries({ queryKey: completeGoalKey })
- // client.invalidateQueries({ queryKey: ["home"] }) // 홈페이지 데이터 무효화
+ client.invalidateQueries({ queryKey: completeGoalKey }),
+ client.invalidateQueries({ queryKey: homeKey })
]);
- addPoint(data.point);
-
+ addPoint(data.totalPoints);
navigate("/map/certification/success", {
- state: { name, point: data.point }
+ state: { name, point: data.bonusPoints }
});
}
});
// useMemos
const isContainRadar = useMemo(() => {
+ console.log(position.lat, position.lng, serverLatitude, serverLongitude);
if (!serverLatitude || !serverLongitude) return false;
const distance =
@@ -83,8 +92,9 @@ function MapCertification() {
}, [serverLatitude, serverLongitude, position]);
const dayString = useMemo(() => {
- const transformDays = map(days, (day) => DAYS_STRING_MAP.get(day));
- const day = days.length === 7 ? "매일" : join(transformDays, ", ");
+ const daysArr = days.split(",");
+ const transformDays = map(daysArr, (day) => DAYS_STRING_MAP.get(day));
+ const day = daysArr.length === 7 ? "매일" : join(transformDays, ", ");
return day;
}, [days]);
diff --git a/src/features/map-certification/components/success.tsx b/src/features/map-certification/components/success.tsx
index b1cf60f..47e8558 100644
--- a/src/features/map-certification/components/success.tsx
+++ b/src/features/map-certification/components/success.tsx
@@ -11,7 +11,6 @@ function MapCertificationSuccess() {
} = location;
const navigateToHome = () => {
- /** @todo 인증 페이지의 인증하기 버튼 기반 쿼리 무효화했기 떄문에 홈페이지 이동 시 최신 데이터가 렌더링되어야함 */
const homePath = paths.home;
navigate(homePath.getHref());
diff --git a/src/features/map-certification/types/index.ts b/src/features/map-certification/types/index.ts
index c6936bc..a8801f8 100644
--- a/src/features/map-certification/types/index.ts
+++ b/src/features/map-certification/types/index.ts
@@ -1,11 +1,12 @@
-type WeekDay = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN";
+export type STATUS = "DRAF" | "ACTIVE" | "COMPLETE";
-export interface Goals {
- id: number;
+export interface GoalDetail {
name: string;
startDate: string;
endDate: string;
latitude: number;
longitude: number;
- days: WeekDay[];
+ dayOfWeek: string;
+ status: STATUS;
+ achievedToday: boolean;
}
diff --git a/src/features/point/api/path.ts b/src/features/point/api/path.ts
index f262bcd..4ccdab1 100644
--- a/src/features/point/api/path.ts
+++ b/src/features/point/api/path.ts
@@ -1,3 +1,3 @@
import { genreateBasePath } from "@/lib/axios/utils";
-export const BASE_PATH = genreateBasePath("point");
+export const BASE_PATH = genreateBasePath("points");
diff --git a/src/features/reward/api/path.ts b/src/features/reward/api/path.ts
index 487cbeb..736e5ed 100644
--- a/src/features/reward/api/path.ts
+++ b/src/features/reward/api/path.ts
@@ -2,7 +2,8 @@ import { generatePathByBase, genreateBasePath } from "@/lib/axios/utils";
export const BASE_PATH = genreateBasePath("points", "v1");
-export const POINTS = (userId: string) => generatePathByBase(BASE_PATH, userId);
+export const POINTS = (userId: number) =>
+ generatePathByBase(BASE_PATH, String(userId));
-export const POINT_DEDUC = (userId: string) =>
- generatePathByBase(BASE_PATH, userId, "deduc");
+export const POINT_DEDUC = (userId: number) =>
+ generatePathByBase(BASE_PATH, String(userId), "deduc");
diff --git a/src/features/reward/api/reward.ts b/src/features/reward/api/reward.ts
index 492af7b..c1e6825 100644
--- a/src/features/reward/api/reward.ts
+++ b/src/features/reward/api/reward.ts
@@ -1,17 +1,27 @@
import type { R } from "@/types/common";
import { GET, POST } from "@/lib/axios";
-import type { RoOnlyPathParamsType } from "@/lib/axios/utils";
+import type {
+ RoDataAndPathParamsType,
+ RoOnlyPathParamsType
+} from "@/lib/axios/utils";
import type { Point } from "../types";
import { POINT_DEDUC, POINTS } from "./path";
export function postRewards({
- pathParams: { userId }
-}: RoOnlyPathParamsType<{ userId: string }>): R<{ point: number }> {
- return POST({ url: POINT_DEDUC(userId) });
+ pathParams: { userId },
+ data
+}: RoDataAndPathParamsType<
+ { points: number; pointType: string; description: string },
+ { userId: number }
+>): R<{
+ point: number;
+ status: string;
+}> {
+ return POST({ url: POINT_DEDUC(userId), data });
}
export function getPoint({
pathParams: { userId }
-}: RoOnlyPathParamsType<{ userId: string }>): R {
+}: RoOnlyPathParamsType<{ userId: number }>): R {
return GET({ url: POINTS(userId) });
}
diff --git a/src/features/reward/components/index.const.ts b/src/features/reward/components/index.const.ts
index e4c0329..4ad1870 100644
--- a/src/features/reward/components/index.const.ts
+++ b/src/features/reward/components/index.const.ts
@@ -9,18 +9,22 @@ export const generateCoupons = (point: number, isPending: boolean) => {
{
id: 1,
name: "스타벅스 쿠폰(5,000p 소모)",
- cost: 5000,
+ points: 5000,
image: coffeeCouponUrl,
isDisabled: point < 5000,
- className: `${baseClass} ${point >= 5000 && !isPending ? "bg-green-500" : "cursor-not-allowed bg-gray-400"}`
+ className: `${baseClass} ${point >= 5000 && !isPending ? "bg-green-500" : "cursor-not-allowed bg-gray-400"}`,
+ pointType: "GIFT_STARBUCKS",
+ description: "스타벅스 기프티콘 지급"
},
{
id: 2,
name: "편의점 1만원 쿠폰(10,000p 소모)",
- cost: 10000,
+ points: 10000,
image: convenienceStoreCouponUrl,
isDisabled: point < 10000,
- className: `${baseClass} ${point >= 10000 && !isPending ? "bg-green-500" : "cursor-not-allowed bg-gray-400"}`
+ className: `${baseClass} ${point >= 10000 && !isPending ? "bg-green-500" : "cursor-not-allowed bg-gray-400"}`,
+ pointType: "GIFT_COUPON",
+ description: "CU 기프티콘 지급"
}
];
};
diff --git a/src/features/reward/components/reward.tsx b/src/features/reward/components/reward.tsx
index c6c7b63..6dc844f 100644
--- a/src/features/reward/components/reward.tsx
+++ b/src/features/reward/components/reward.tsx
@@ -15,6 +15,11 @@ export default function Reward() {
const { mutate, isPending } = useMutation({
...generate_qo_postRewards(userId),
onSuccess: (data) => {
+ if (data.status === "error") {
+ toast.error("리워드 신청에 실패했습니다.");
+ return;
+ }
+
setPoint(data.point);
toast(, {
position: "bottom-center",
@@ -26,6 +31,18 @@ export default function Reward() {
}
});
+ const applyReward = ({
+ points,
+ pointType,
+ description
+ }: {
+ points: number;
+ pointType: string;
+ description: string;
+ }) => {
+ mutate({ points, pointType, description });
+ };
+
const loaledPoint = useMemo(() => `${point.toLocaleString()}p`, [point]);
const coupons = useMemo(
() => generateCoupons(point, isPending),
@@ -44,29 +61,41 @@ export default function Reward() {
- {map(coupons, ({ id, image, name, isDisabled, className }) => (
-
+ {map(
+ coupons,
+ ({
+ id,
+ image,
+ name,
+ isDisabled,
+ className,
+ points,
+ pointType,
+ description
+ }) => (
+ key={id}
+ className="relative flex items-center gap-4 overflow-hidden rounded-xl"
+ >
+
-
- {name}
-
+
+ {name}
+
-
-
- ))}
+
+
+ )
+ )}
);
diff --git a/src/features/user/api/user.ts b/src/features/user/api/user.ts
index 2f865f5..62bcaf9 100644
--- a/src/features/user/api/user.ts
+++ b/src/features/user/api/user.ts
@@ -1,10 +1,10 @@
-import { BASE_PATH } from "@/features/auth/api/paths";
+import { BASE_PATH } from "@/features/user/api/paths";
import { DELETE, GET, POST } from "@/lib/axios";
export function getUserInfo({ pathParam }: { pathParam: number }) {
return GET({
- url: `${BASE_PATH}?userId=${pathParam}`
+ url: `${BASE_PATH}/${pathParam}/check`
});
}
diff --git a/src/features/user/routes/Account.tsx b/src/features/user/routes/Account.tsx
index ca9d199..36f07a9 100644
--- a/src/features/user/routes/Account.tsx
+++ b/src/features/user/routes/Account.tsx
@@ -100,7 +100,7 @@ const AccountPage: React.FC = () => {
휴대폰 번호
- {userInfo?.phone}
+ {userInfo?.phoneNumber}
diff --git a/src/features/user/routes/MyPage.tsx b/src/features/user/routes/MyPage.tsx
index eb32ecf..566283b 100644
--- a/src/features/user/routes/MyPage.tsx
+++ b/src/features/user/routes/MyPage.tsx
@@ -1,3 +1,5 @@
+import { ProfileHeader } from "@/components/ui/header";
+
import React, { useEffect, useState } from "react";
import { getUserInfo, postLogout } from "@/features/user/api/user";
@@ -11,16 +13,15 @@ const MyPage: React.FC = () => {
const [userInfo, setUserInfo] = useState
(null);
const navigate = useNavigate();
const userId: number | null = useAuthStore((state) => state.userId);
-
+ const { logout } = useAuthStore();
useEffect(() => {
- if (!userId) {
- return;
- }
+ if (!userId) return;
const fetchUserInfo = async (): Promise => {
try {
const response = await getUserInfo({ pathParam: userId });
- setUserInfo(response.data);
+
+ setUserInfo(response);
} catch (error) {
console.error("사용자 정보를 불러오는 데 실패했습니다.", error);
}
@@ -34,6 +35,7 @@ const MyPage: React.FC = () => {
try {
await postLogout({ pathParam: userId });
+ logout();
navigate(paths.home.path);
} catch (error) {
console.error("로그아웃 실패:", error);
@@ -45,130 +47,89 @@ const MyPage: React.FC = () => {
console.error("사용자 정보가 없습니다.");
return;
}
+ console.log(userInfo, "userinfo");
navigate(paths.user.account.path, { state: { ...userInfo, userId } });
};
+ const navigateReward = () => navigate(paths.profile.reward.path);
+
return (
-
-
-
-
- 내정보
-
-
+
-
-
-
- 안녕하세요!{" "}
-
- {userInfo ? userInfo?.name : "홍길동"}
-
- 님
-
-
- 오늘도 일단 가볼까요?
-
-
-
-
-
-
- 현재 포인트:{" "}
-
- {userInfo ? userInfo.points : 3000}p
-
-
-
- 커피 쿠폰까지{" "}
-
- {userInfo ? 5000 - userInfo.points : 500}p
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
);
diff --git a/src/features/user/types/user-info.tsx b/src/features/user/types/user-info.tsx
index a12fb3e..cbfdcdf 100644
--- a/src/features/user/types/user-info.tsx
+++ b/src/features/user/types/user-info.tsx
@@ -5,7 +5,7 @@ export enum SocialType {
export type UserInfo = {
name: string;
- phone: string;
+ phoneNumber: string;
email?: string;
socialType: SocialType;
points: number;
diff --git a/src/lib/axios/index.ts b/src/lib/axios/index.ts
index f7f0857..29ad696 100644
--- a/src/lib/axios/index.ts
+++ b/src/lib/axios/index.ts
@@ -87,14 +87,23 @@ const requestInterceptor: Interceptor
= {
const responseInterceptor: Interceptor = {
onFulfilled: (config) => {
- if (
- config.data &&
- config.headers["content-type"]?.toString().includes("application/json")
- ) {
- try {
- config.data = JSON.parse(config.data);
- } catch {
- throw new Error("Axios Parse Error");
+ const isJson = config.headers["content-type"]?.includes("application/json");
+
+ if (isJson) {
+ if (
+ config.data &&
+ config.headers["content-type"]?.toString().includes("application/json")
+ ) {
+ try {
+ if (config.data === "목표 생성 성공") return config;
+ if (typeof config.data === "string") {
+ config.data = JSON.parse(config.data);
+ } else if (typeof config.data !== "object") {
+ throw new Error("Invalid JSON response");
+ }
+ } catch {
+ throw new Error("Axios Parse Error");
+ }
}
}
diff --git a/src/lib/react-query/queryOptions/goals.ts b/src/lib/react-query/queryOptions/goals.ts
index 2800583..b404f29 100644
--- a/src/lib/react-query/queryOptions/goals.ts
+++ b/src/lib/react-query/queryOptions/goals.ts
@@ -2,62 +2,87 @@ import { getGoalsCheck, getGoalsComplete } from "@/features/goal/api/goal";
import { GOALS_CHECK, GOALS_COMPLETE } from "@/features/goal/api/path";
import type { CompleteGoal, ProgressGoal } from "@/features/goal/types";
import { getGoals, postGoalsAchieve } from "@/features/map-certification/api";
-import { GOALS } from "@/features/map-certification/api/paths";
-import { Goals } from "@/features/map-certification/types";
+import { GOALS_DETAIL } from "@/features/map-certification/api/paths";
+import { GoalDetail } from "@/features/map-certification/types";
import { simpleGenerateSecond } from "@/utils/date";
import { UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
import { AxiosError } from "axios";
interface UseQueryGoalsOptions
- extends UseQueryOptions {}
-type GenerateQoGetGoals = (id: number) => UseQueryGoalsOptions;
-export const generate_qo_getGoals: GenerateQoGetGoals = (id) => {
+ extends UseQueryOptions {}
+type GenerateQoGetGoals = (goalId: number) => UseQueryGoalsOptions;
+export const generate_qo_getGoals: GenerateQoGetGoals = (goalId) => {
return {
- queryKey: [GOALS(id)],
- queryFn: () => getGoals({ pathParams: { id } }).then((data) => data),
+ queryKey: [GOALS_DETAIL(goalId)],
+ queryFn: () =>
+ getGoals({ pathParams: { goalId } }).then((data) => {
+ console.log(data, "목표 인증 데이터");
+ return data;
+ }),
staleTime: simpleGenerateSecond([[10, "s"]])
};
};
interface UseMutationGoalsAchieveOptions
- extends UseMutationOptions<{ point: number }, AxiosError, void, unknown> {}
-type GenerateQoPostGoalsAchieve = (
- id: number
-) => UseMutationGoalsAchieveOptions;
-export const generate_qo_postGoalsAchieve: GenerateQoPostGoalsAchieve = (
- id
-) => {
+ extends UseMutationOptions<
+ { totalPoints: number; bonusPoints: number },
+ AxiosError,
+ void,
+ unknown
+ > {}
+type GenerateQoPostGoalsAchieve = ({
+ goalId,
+ userId,
+ latitude,
+ longitude
+}: {
+ goalId: number;
+ userId: number;
+ latitude: number;
+ longitude: number;
+}) => UseMutationGoalsAchieveOptions;
+export const generate_qo_postGoalsAchieve: GenerateQoPostGoalsAchieve = ({
+ goalId,
+ userId,
+ latitude,
+ longitude
+}) => {
return {
- mutationFn: () => postGoalsAchieve({ pathParams: { id } })
+ mutationFn: () =>
+ postGoalsAchieve({
+ pathParams: { goalId },
+ data: { userId, latitude, longitude }
+ })
};
};
interface UseQueryGoalsCheckOptions
- extends UseQueryOptions {
- DELETE_KEY?: [string];
-}
-type GenerateQoGetGoalsCheck = () => UseQueryGoalsCheckOptions;
-export const generate_qo_getGoalsCheck = (() => {
+ extends UseQueryOptions {}
+type GenerateQoGetGoalsCheck = (userId: number) => UseQueryGoalsCheckOptions;
+export const generate_qo_getGoalsCheck = ((userId) => {
const options: UseQueryGoalsCheckOptions = {
- queryKey: [GOALS_CHECK],
- queryFn: () => getGoalsCheck().then((data) => data)
+ queryKey: [GOALS_CHECK, userId],
+ queryFn: () => getGoalsCheck({ query: { userId } }).then((data) => data)
};
return options;
-}) as GenerateQoGetGoalsCheck & { DELETE_KEY: () => [string] };
+}) as GenerateQoGetGoalsCheck & { DELETE_KEY: (userId: string) => string[] };
-generate_qo_getGoalsCheck.DELETE_KEY = () => [GOALS_CHECK];
+generate_qo_getGoalsCheck.DELETE_KEY = (userId) => [GOALS_CHECK, userId];
interface UseQueryGoalsCompleteOptions
extends UseQueryOptions {}
-type GenerateQoGetGoalsComplete = () => UseQueryGoalsCompleteOptions;
-export const generate_qo_getGoalsComplete = (() => {
+type GenerateQoGetGoalsComplete = (
+ userId: number
+) => UseQueryGoalsCompleteOptions;
+export const generate_qo_getGoalsComplete = ((userId) => {
const options: UseQueryGoalsCompleteOptions = {
- queryKey: [GOALS_COMPLETE],
- queryFn: () => getGoalsComplete().then((data) => data)
+ queryKey: [GOALS_COMPLETE(userId)],
+ queryFn: () =>
+ getGoalsComplete({ pathParams: { userId } }).then((data) => data)
};
return options;
-}) as GenerateQoGetGoalsComplete & { DELETE_KEY: () => [string] };
+}) as GenerateQoGetGoalsComplete & { DELETE_KEY: (userId: number) => string[] };
-generate_qo_getGoalsComplete.DELETE_KEY = () => [GOALS_COMPLETE];
+generate_qo_getGoalsComplete.DELETE_KEY = (userId) => [GOALS_COMPLETE(userId)];
diff --git a/src/lib/react-query/queryOptions/home.ts b/src/lib/react-query/queryOptions/home.ts
index f4d1351..95336fb 100644
--- a/src/lib/react-query/queryOptions/home.ts
+++ b/src/lib/react-query/queryOptions/home.ts
@@ -6,14 +6,18 @@ import { AxiosError } from "axios";
interface UseQueryGoalsOptions
extends UseQueryOptions {}
-type GenerateQoGetGoals = () => UseQueryGoalsOptions;
+type GenerateQoGetGoals = (userId: number) => UseQueryGoalsOptions;
-export const generate_qo_getGoals: GenerateQoGetGoals = () => {
- return {
- queryKey: [BASE_PATH],
+export const generate_qo_getGoals = ((userId) => {
+ const options: UseQueryGoalsOptions = {
+ queryKey: [BASE_PATH, userId],
queryFn: () =>
- getGoals().then((data) => {
+ getGoals({ pathParams: { userId } }).then((data) => {
return data;
})
};
-};
+
+ return options;
+}) as GenerateQoGetGoals & { DELETE_KEY: (userId: number) => [string, number] };
+
+generate_qo_getGoals.DELETE_KEY = (userId) => [BASE_PATH, userId];
diff --git a/src/lib/react-query/queryOptions/reward.ts b/src/lib/react-query/queryOptions/reward.ts
index f2e6bf7..dca8ba9 100644
--- a/src/lib/react-query/queryOptions/reward.ts
+++ b/src/lib/react-query/queryOptions/reward.ts
@@ -2,11 +2,23 @@ import { postRewards } from "@/features/reward/api/reward";
import { UseMutationOptions } from "@tanstack/react-query";
import { AxiosError } from "axios";
+interface RewardData {
+ points: number;
+ pointType: string;
+ description: string;
+}
+
interface UseMutationRewardsOptions
- extends UseMutationOptions<{ point: number }, AxiosError, void, unknown> {}
-type GenerateQoPostRewards = (userId: string) => UseMutationRewardsOptions;
+ extends UseMutationOptions<
+ { point: number; status: string },
+ AxiosError,
+ RewardData,
+ unknown
+ > {}
+type GenerateQoPostRewards = (userId: number) => UseMutationRewardsOptions;
export const generate_qo_postRewards: GenerateQoPostRewards = (userId) => {
return {
- mutationFn: () => postRewards({ pathParams: { userId } })
+ mutationFn: (data: RewardData) =>
+ postRewards({ pathParams: { userId }, data })
};
};
diff --git a/src/stores/auth-store.ts b/src/stores/auth-store.ts
index 9c81b1b..6710b40 100644
--- a/src/stores/auth-store.ts
+++ b/src/stores/auth-store.ts
@@ -5,18 +5,22 @@ interface AuthState {
refreshToken: string | null;
isAuthenticated: boolean;
userId: number | null;
+ socialId: number | null;
setTokens: (
accessToken: string,
refreshToken: string,
userId: number
) => void;
+ logout: () => void;
}
export const useAuthStore = create((set) => ({
accessToken: null,
refreshToken: null,
isAuthenticated: false,
+ socialId: null,
userId: null,
setTokens: (accessToken, refreshToken, userId) =>
- set({ accessToken, refreshToken, isAuthenticated: true, userId })
+ set({ accessToken, refreshToken, isAuthenticated: true, userId }),
+ logout: () => set({ isAuthenticated: false })
}));
diff --git a/src/stores/user.ts b/src/stores/user.ts
index 58e8bb7..258e82b 100644
--- a/src/stores/user.ts
+++ b/src/stores/user.ts
@@ -3,16 +3,18 @@ import { create } from "zustand";
interface UserState {
userName: string;
point: number;
- userId: string;
+ userId: number;
+ setUserId: (userId: number) => void;
setPoint: (point: number) => void;
addPoint: (addPoint: number) => void;
setUserName: (userName: string) => void;
}
export const useUserStore = create((set) => ({
- userName: "park",
- point: 100000,
- userId: "0",
+ userName: "",
+ point: 0,
+ userId: 0,
+ setUserId: (userId) => set({ userId }),
setPoint: (point) => set({ point }),
addPoint: (addPoint: number) =>
set((state) => ({ point: state.point + addPoint })),
diff --git a/src/testing/mocks/handlers/browser.ts b/src/testing/mocks/handlers/browser.ts
index 454e61f..a676037 100644
--- a/src/testing/mocks/handlers/browser.ts
+++ b/src/testing/mocks/handlers/browser.ts
@@ -35,7 +35,7 @@ export const handlers = [
return HttpResponse.json({ message: "success" });
}),
- http.post(`${ENDPOINT_URL}${POINT_DEDUC("1")}`, async () => {
+ http.post(`${ENDPOINT_URL}${POINT_DEDUC(1)}`, async () => {
await delay(500);
return HttpResponse.json({ point: 10000 });