diff --git a/package-lock.json b/package-lock.json
index e0a8f5038..4aeeb8bef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"@emotion/styled": "^11.11.0",
"@tanstack/react-query": "^5.24.1",
"axios": "^1.6.7",
+ "base-64": "^1.0.0",
"framer-motion": "^11.0.6",
"glob": "^11.0.0",
"react": "^18.2.0",
@@ -39,6 +40,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "@types/base-64": "^1.0.2",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.82",
"@types/react": "^18.2.57",
@@ -9308,6 +9310,13 @@
"@babel/types": "^7.20.7"
}
},
+ "node_modules/@types/base-64": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@types/base-64/-/base-64-1.0.2.tgz",
+ "integrity": "sha512-uPgKMmM9fmn7I+Zi6YBqctOye4SlJsHKcisjHIMWpb2YKZRc36GpKyNuQ03JcT+oNXg1m7Uv4wU94EVltn8/cw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@@ -11693,6 +11702,12 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/base-64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
+ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
+ "license": "MIT"
+ },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
diff --git a/package.json b/package.json
index 8b38f4ba0..4774234a5 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@emotion/styled": "^11.11.0",
"@tanstack/react-query": "^5.24.1",
"axios": "^1.6.7",
+ "base-64": "^1.0.0",
"framer-motion": "^11.0.6",
"glob": "^11.0.0",
"react": "^18.2.0",
@@ -54,6 +55,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "@types/base-64": "^1.0.2",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.82",
"@types/react": "^18.2.57",
@@ -87,4 +89,4 @@
"overrides": {
"react-refresh": "0.11.0"
}
-}
\ No newline at end of file
+}
diff --git a/public/token.html b/public/token.html
new file mode 100644
index 000000000..c7bdcabc0
--- /dev/null
+++ b/public/token.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+ OAuth Callback
+
+
+
+ OAuth Callback
+ 토큰을 기다리고 있습니다...
+
+
+
\ No newline at end of file
diff --git a/src/api/hooks/categories.mock.ts b/src/api/hooks/categories.mock.ts
index 7b1ac46f2..c1d5545eb 100644
--- a/src/api/hooks/categories.mock.ts
+++ b/src/api/hooks/categories.mock.ts
@@ -8,21 +8,48 @@ export const categoriesMockHandler = [
}),
];
-const CATEGORIES_RESPONSE_DATA = [
- {
- id: 2920,
- name: '생일',
- description: '감동을 높여줄 생일 선물 리스트',
- color: '#5949a3',
- imageUrl:
- 'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png',
+const CATEGORIES_RESPONSE_DATA = {
+ totalElements: 2,
+ totalPages: 1,
+ first: true,
+ last: true,
+ size: 10,
+ content: [
+ {
+ id: 2920,
+ name: '생일',
+ description: '감동을 높여줄 생일 선물 리스트',
+ color: '#5949a3',
+ imageUrl:
+ 'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fgift%2Fhome%2Ftheme%2F292020231106_MXMUB.png',
+ },
+ {
+ id: 2930,
+ name: '교환권',
+ description: '놓치면 후회할 교환권 특가',
+ color: '#9290C3',
+ imageUrl:
+ 'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240131153049_5a22b137a8d346e9beb020a7a7f4254a.jpg',
+ },
+ ],
+ number: 0,
+ sort: {
+ empty: true,
+ sorted: false,
+ unsorted: true,
},
- {
- id: 2930,
- name: '교환권',
- description: '놓치면 후회할 교환권 특가',
- color: '#9290C3',
- imageUrl:
- 'https://img1.daumcdn.net/thumb/S104x104/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240131153049_5a22b137a8d346e9beb020a7a7f4254a.jpg',
+ numberOfElements: 2,
+ pageable: {
+ pageNumber: 0,
+ pageSize: 10,
+ sort: {
+ empty: true,
+ sorted: false,
+ unsorted: true,
+ },
+ offset: 0,
+ paged: true,
+ unpaged: false,
},
-];
+ empty: false,
+};
\ No newline at end of file
diff --git a/src/api/hooks/useGetCategories.ts b/src/api/hooks/useGetCategories.ts
index d93e4fc95..8bb5c4fed 100644
--- a/src/api/hooks/useGetCategories.ts
+++ b/src/api/hooks/useGetCategories.ts
@@ -2,20 +2,58 @@ import { useQuery } from '@tanstack/react-query';
import type { CategoryData } from '@/types';
-import { BASE_URL, fetchInstance } from '../instance';
+import { getBaseUrl, fetchInstance } from '../instance';
-export type CategoryResponseData = CategoryData[];
+export type CategoryResponseData = {
+ totalElements: number;
+ totalPages: number;
+ first: boolean;
+ last: boolean;
+ size: number;
+ content: CategoryData[];
+ number: number;
+ sort: {
+ empty: boolean;
+ sorted: boolean;
+ unsorted: boolean;
+ };
+ numberOfElements: number;
+ pageable: {
+ pageNumber: number;
+ pageSize: number;
+ sort: {
+ empty: boolean;
+ sorted: boolean;
+ unsorted: boolean;
+ };
+ offset: number;
+ paged: boolean;
+ unpaged: boolean;
+ };
+ empty: boolean;
+};
+
+export const getCategoriesPath = () => `${getBaseUrl()}/api/categories`;
+
+export const getCategories = async (): Promise => {
+ let allCategories: CategoryData[] = [];
+ let currentPage = 0;
+ let hasNextPage = true;
-export const getCategoriesPath = () => `${BASE_URL}/api/categories`;
-const categoriesQueryKey = [getCategoriesPath()];
+ while (hasNextPage) {
+ const response = await fetchInstance.get(
+ `${getCategoriesPath()}?page=${currentPage}&size=100`
+ );
+ allCategories = [...allCategories, ...response.data.content];
+ hasNextPage = !response.data.last;
+ currentPage++;
+ }
-export const getCategories = async () => {
- const response = await fetchInstance.get(getCategoriesPath());
- return response.data;
+ return allCategories;
};
export const useGetCategories = () =>
useQuery({
- queryKey: categoriesQueryKey,
+ queryKey: ['categories'],
queryFn: getCategories,
});
diff --git a/src/api/hooks/useGetProductDetail.ts b/src/api/hooks/useGetProductDetail.ts
index 539de0196..72023785c 100644
--- a/src/api/hooks/useGetProductDetail.ts
+++ b/src/api/hooks/useGetProductDetail.ts
@@ -2,7 +2,7 @@ import { useSuspenseQuery } from '@tanstack/react-query';
import type { ProductData } from '@/types';
-import { BASE_URL, fetchInstance } from '../instance';
+import { getBaseUrl, fetchInstance } from '../instance';
export type ProductDetailRequestParams = {
productId: string;
@@ -12,7 +12,7 @@ type Props = ProductDetailRequestParams;
export type GoodsDetailResponseData = ProductData;
-export const getProductDetailPath = (productId: string) => `${BASE_URL}/api/products/${productId}`;
+export const getProductDetailPath = (productId: string) => `${getBaseUrl()}/api/products/${productId}`;
export const getProductDetail = async (params: ProductDetailRequestParams) => {
const response = await fetchInstance.get(
diff --git a/src/api/hooks/useGetProductOptions.ts b/src/api/hooks/useGetProductOptions.ts
index a3bdc538f..1be861ee0 100644
--- a/src/api/hooks/useGetProductOptions.ts
+++ b/src/api/hooks/useGetProductOptions.ts
@@ -2,7 +2,7 @@ import { useSuspenseQuery } from '@tanstack/react-query';
import type { ProductOptionsData } from '@/types';
-import { BASE_URL, fetchInstance } from '../instance';
+import {getBaseUrl, fetchInstance } from '../instance';
import type { ProductDetailRequestParams } from './useGetProductDetail';
type Props = ProductDetailRequestParams;
@@ -10,7 +10,7 @@ type Props = ProductDetailRequestParams;
export type ProductOptionsResponseData = ProductOptionsData[];
export const getProductOptionsPath = (productId: string) =>
- `${BASE_URL}/api/products/${productId}/options`;
+ `${getBaseUrl()}/api/products/${productId}/options`;
export const getProductOptions = async (params: ProductDetailRequestParams) => {
const response = await fetchInstance.get(
diff --git a/src/api/hooks/useGetProducts.ts b/src/api/hooks/useGetProducts.ts
index 432f90d93..7fef29938 100644
--- a/src/api/hooks/useGetProducts.ts
+++ b/src/api/hooks/useGetProducts.ts
@@ -6,7 +6,7 @@ import {
import type { ProductData } from '@/types';
-import { BASE_URL } from '../instance';
+import { getBaseUrl } from '../instance';
import { fetchInstance } from './../instance/index';
type RequestParams = {
@@ -35,12 +35,14 @@ type ProductsResponseRawData = {
export const getProductsPath = ({ categoryId, pageToken, maxResults }: RequestParams) => {
const params = new URLSearchParams();
- params.append('categoryId', categoryId);
+ if (pageToken)
+ params.append('page', pageToken);
+ if (maxResults)
+ params.append('size', maxResults.toString());
params.append('sort', 'name,asc');
- if (pageToken) params.append('page', pageToken);
- if (maxResults) params.append('size', maxResults.toString());
+ params.append('categoryId', categoryId);
- return `${BASE_URL}/api/products?${params.toString()}`;
+ return `${getBaseUrl()}/api/products?${params.toString()}`;
};
export const getProducts = async (params: RequestParams): Promise => {
@@ -58,6 +60,7 @@ export const getProducts = async (params: RequestParams): Promise & { initPageToken?: string };
+
export const useGetProducts = ({
categoryId,
maxResults = 20,
@@ -71,4 +74,4 @@ export const useGetProducts = ({
initialPageParam: initPageToken,
getNextPageParam: (lastPage) => lastPage.nextPageToken,
});
-};
+};
\ No newline at end of file
diff --git a/src/api/instance/index.ts b/src/api/instance/index.ts
index b83ca1407..db0118b9f 100644
--- a/src/api/instance/index.ts
+++ b/src/api/instance/index.ts
@@ -2,6 +2,8 @@ import { QueryClient } from '@tanstack/react-query';
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
import axios from 'axios';
+let BASE_URL = localStorage.getItem('baseURL') || 'https://api.example.com';
+
const initInstance = (config: AxiosRequestConfig): AxiosInstance => {
const instance = axios.create({
timeout: 5000,
@@ -16,10 +18,8 @@ const initInstance = (config: AxiosRequestConfig): AxiosInstance => {
return instance;
};
-export const BASE_URL = 'https://api.example.com';
-// TODO: 추후 서버 API 주소 변경 필요
-export const fetchInstance = initInstance({
- baseURL: 'https://api.example.com',
+export let fetchInstance = initInstance({
+ baseURL: BASE_URL,
});
export const queryClient = new QueryClient({
@@ -32,3 +32,17 @@ export const queryClient = new QueryClient({
},
},
});
+
+export const updateBaseUrl = (newBaseUrl: string) => {
+ BASE_URL = newBaseUrl;
+ localStorage.setItem('baseURL', newBaseUrl);
+ fetchInstance = initInstance({
+ baseURL: BASE_URL,
+ });
+ // 선택한 이름마다 잘 받아오는지 확인용, 추후 삭제 예정
+ console.log('Updated fetchInstance:', fetchInstance.defaults.baseURL);
+};
+
+export const getBaseUrl = () => {
+ return localStorage.getItem('baseURL') || 'https://api.example.com';
+};
\ No newline at end of file
diff --git a/src/components/features/Category/CategoryProductsSection/index.tsx b/src/components/features/Category/CategoryProductsSection/index.tsx
index d2dae2c3b..4b1df1dbd 100644
--- a/src/components/features/Category/CategoryProductsSection/index.tsx
+++ b/src/components/features/Category/CategoryProductsSection/index.tsx
@@ -20,10 +20,14 @@ export const CategoryProductsSection = ({ categoryId }: Props) => {
categoryId,
});
- if (isLoading) return ;
- if (isError) return 에러가 발생했습니다.;
- if (!data) return <>>;
- if (data.pages[0].products.length <= 0) return 상품이 없어요.;
+ if (isLoading)
+ return ;
+ if (isError)
+ return 에러가 발생했습니다.;
+ if (!data)
+ return <>>;
+ if (data.pages[0].products.length <= 0)
+ return 상품이 없어요.;
const flattenGoodsList = data.pages.map((page) => page?.products ?? []).flat();
diff --git a/src/components/features/Goods/Detail/OptionItem/CountOptionItem.tsx b/src/components/features/Goods/Detail/OptionItem/CountOptionItem.tsx
index 2147d41fc..97de01420 100644
--- a/src/components/features/Goods/Detail/OptionItem/CountOptionItem.tsx
+++ b/src/components/features/Goods/Detail/OptionItem/CountOptionItem.tsx
@@ -12,7 +12,7 @@ type Props = {
export const CountOptionItem = ({
name,
- minValues = 1,
+ minValues = 0,
maxValues = 100,
value,
onChange,
diff --git a/src/components/features/Goods/Detail/OptionSection.tsx b/src/components/features/Goods/Detail/OptionSection.tsx
index 31f7e9839..2104f43ae 100644
--- a/src/components/features/Goods/Detail/OptionSection.tsx
+++ b/src/components/features/Goods/Detail/OptionSection.tsx
@@ -1,5 +1,5 @@
import styled from '@emotion/styled';
-import { useMemo, useState } from 'react';
+import { useMemo, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import {
@@ -13,6 +13,7 @@ import { getDynamicPath, RouterPath } from '@/routes/path';
import { orderHistorySessionStorage } from '@/utils/storage';
import { CountOptionItem } from './OptionItem/CountOptionItem';
+import { getBaseUrl } from '@/api/instance';
type Props = ProductDetailRequestParams;
@@ -24,10 +25,32 @@ export const OptionSection = ({ productId }: Props) => {
const { data: detail } = useGetProductDetail({ productId });
const { data: options } = useGetProductOptions({ productId });
- const [counts, setCounts] = useState({});
+ const [counts, setCounts] = useState(() => {
+ const initialCounts: CountState = {};
+ if (options) {
+ options.forEach(option => {
+ initialCounts[option.id] = '0';
+ });
+ }
+ return initialCounts;
+ });
+
+ useEffect(() => {
+ if (options) {
+ setCounts(prevCounts => {
+ const newCounts = { ...prevCounts };
+ options.forEach(option => {
+ if (!newCounts[option.id]) {
+ newCounts[option.id] = '0';
+ }
+ });
+ return newCounts;
+ });
+ }
+ }, [options]);
+
const totalPrice = useMemo(() => {
- if (!detail)
- return 0;
+ if (!detail) return 0;
const count = Object.values(counts).reduce((acc, countStr) => acc + Number(countStr), 0);
return detail.price * count;
}, [detail, counts]);
@@ -46,21 +69,23 @@ export const OptionSection = ({ productId }: Props) => {
}
try {
- const response = await fetch('/api/wishes', {
+ const requestBody = {
+ productId: parseInt(productId, 10),
+ quantity: null,
+ };
+
+ console.log('Sending request to add wish', requestBody); // 잘 받아오는지 확인 - 추후 삭제 예정
+
+ const response = await fetch(`${getBaseUrl()}/api/wishes`, {
method: 'POST',
headers: {
Authorization: `Bearer ${authInfo.token}`,
'Content-Type': 'application/json',
},
- body: JSON.stringify({
- productId: parseInt(productId, 10),
- name: detail?.name,
- price: detail?.price,
- imageUrl: detail?.imageUrl,
- }),
+ body: JSON.stringify(requestBody),
});
- if (response.status === 201) {
+ if (response.ok) {
alert('관심 등록 완료');
} else if (response.status === 400) {
alert('잘못된 요청입니다.');
@@ -89,10 +114,23 @@ export const OptionSection = ({ productId }: Props) => {
const totalCount = Object.values(counts).reduce((acc, countStr) => acc + Number(countStr), 0);
- orderHistorySessionStorage.set({
- id: parseInt(productId, 10),
- count: totalCount,
- });
+ if (totalCount === 0) {
+ alert('옵션을 선택해주세요.');
+ return;
+ }
+
+ const selectedOptions = options
+ .filter(option => Number(counts[option.id]) > 0)
+ .map(option => ({
+ productId: parseInt(productId, 10),
+ optionId: option.id,
+ quantity: Number(counts[option.id]),
+ message: '',
+ }));
+
+ console.log('선택된 옵션 및 수량:', selectedOptions); // 콘솔 로그 추가
+
+ orderHistorySessionStorage.set(selectedOptions);
navigate(RouterPath.order);
};
@@ -103,7 +141,7 @@ export const OptionSection = ({ productId }: Props) => {
setCounts(prev => ({ ...prev, [option.id]: value }))}
/>
))}
diff --git a/src/components/features/Home/CategorySection/index.tsx b/src/components/features/Home/CategorySection/index.tsx
index f0690cfd3..00f67e370 100644
--- a/src/components/features/Home/CategorySection/index.tsx
+++ b/src/components/features/Home/CategorySection/index.tsx
@@ -8,12 +8,20 @@ import { getDynamicPath } from '@/routes/path';
import { breakpoints } from '@/styles/variants';
import { CategoryItem } from './CategoryItem';
+import { LoadingView } from '@/components/common/View/LoadingView';
export const CategorySection = () => {
- const { data, isLoading, isError } = useGetCategories();
+ const { data: categories, isLoading, isError } = useGetCategories();
- if (isLoading || isError) return null;
- if (!data) return null;
+ if (isLoading) {
+ return ;
+ }
+ if (isError) {
+ return 문제가 발생했습니다. 나중에 다시 시도해주세요.;
+ }
+ if (!categories || categories.length === 0 ) {
+ return 카테고리가 없습니다.;
+ }
return (
@@ -24,7 +32,7 @@ export const CategorySection = () => {
md: 6,
}}
>
- {data.map((category) => (
+ {categories.map((category) => (
@@ -42,3 +50,13 @@ const Wrapper = styled.section`
padding: 45px 52px 23px;
}
`;
+const ErrorMessage = styled.div`
+ padding: 20px;
+ text-align: center;
+ color: red;
+`;
+
+const EmptyMessage = styled.div`
+ padding: 20px;
+ text-align: center;
+`;
\ No newline at end of file
diff --git a/src/components/features/Layout/Header.tsx b/src/components/features/Layout/Header.tsx
index eeac55071..e7b9a6dc7 100644
--- a/src/components/features/Layout/Header.tsx
+++ b/src/components/features/Layout/Header.tsx
@@ -1,18 +1,37 @@
import styled from '@emotion/styled';
import { Link, useNavigate } from 'react-router-dom';
+import { useState, useEffect, ChangeEvent } from 'react';
import { Container } from '@/components/common/layouts/Container';
import { useAuth } from '@/provider/Auth';
import { getDynamicPath, RouterPath } from '@/routes/path';
+import { updateBaseUrl, queryClient } from '@/api/instance';
export const Header = () => {
const navigate = useNavigate();
const authInfo = useAuth();
+ const [selectedApi, setSelectedApi] = useState('default');
+
+ useEffect(() => {
+ const storedBaseUrl = localStorage.getItem('baseURL');
+ if (storedBaseUrl) {
+ setSelectedApi(storedBaseUrl);
+ updateBaseUrl(storedBaseUrl);
+ }
+ }, []);
const handleLogin = () => {
navigate(getDynamicPath.login());
};
+ const handleApiChange = (event: ChangeEvent) => {
+ const newApi = event.target.value;
+ setSelectedApi(newApi);
+ localStorage.setItem('baseURL', newApi);
+ updateBaseUrl(newApi);
+ queryClient.invalidateQueries();
+ };
+
return (
@@ -23,6 +42,13 @@ export const Header = () => {
/>
+
{authInfo ? (
navigate(RouterPath.myAccount)}>내 계정
) : (
@@ -49,7 +75,10 @@ export const Wrapper = styled.header`
const Logo = styled.img`
height: ${HEADER_HEIGHT};
`;
-const RightWrapper = styled.div``;
+const RightWrapper = styled.div`
+ display: flex;
+ align-items: center;
+`;
const LinkButton = styled.p`
align-items: center;
@@ -57,4 +86,5 @@ const LinkButton = styled.p`
color: #000;
text-decoration: none;
cursor: pointer;
+ margin-left: 20px;
`;
diff --git a/src/components/features/Order/OrderForm/Fields/MessageCardFields.tsx b/src/components/features/Order/OrderForm/Fields/MessageCardFields.tsx
index 41497e56d..e91be6112 100644
--- a/src/components/features/Order/OrderForm/Fields/MessageCardFields.tsx
+++ b/src/components/features/Order/OrderForm/Fields/MessageCardFields.tsx
@@ -1,21 +1,23 @@
import { Textarea } from '@chakra-ui/react';
import styled from '@emotion/styled';
-
import { useOrderFormContext } from '@/hooks/useOrderFormContext';
export const MessageCardFields = () => {
- const { register } = useOrderFormContext();
+ const { register, watch, setValue } = useOrderFormContext();
+ const message = watch('message');
return (
@@ -30,4 +32,4 @@ const Wrapper = styled.div`
const TextMessageWrapper = styled.div`
width: 100%;
padding: 12px 30px 16px;
-`;
+`;
\ No newline at end of file
diff --git a/src/components/features/Order/OrderForm/GoodsInfo/index.tsx b/src/components/features/Order/OrderForm/GoodsInfo/index.tsx
index caced66c7..ac9cf6dd0 100644
--- a/src/components/features/Order/OrderForm/GoodsInfo/index.tsx
+++ b/src/components/features/Order/OrderForm/GoodsInfo/index.tsx
@@ -1,6 +1,6 @@
import styled from '@emotion/styled';
-
import { useGetProductDetail } from '@/api/hooks/useGetProductDetail';
+import { useGetProductOptions } from '@/api/hooks/useGetProductOptions';
import { Image } from '@/components/common/Image';
import { Spacing } from '@/components/common/layouts/Spacing';
import type { OrderHistory } from '@/types';
@@ -8,11 +8,17 @@ import type { OrderHistory } from '@/types';
import { LabelText } from '../Common/LabelText';
type Props = {
- orderHistory: OrderHistory;
+ orderHistory: OrderHistory[];
};
+
export const GoodsInfo = ({ orderHistory }: Props) => {
- const { id, count } = orderHistory;
- const { data: detail } = useGetProductDetail({ productId: id.toString() });
+ const productId = orderHistory[0].productId;
+ const { data: detail, isLoading: isDetailLoading } = useGetProductDetail({ productId: productId.toString() });
+ const { data: options, isLoading: isOptionsLoading } = useGetProductOptions({ productId: productId.toString() });
+
+ if (isDetailLoading || isOptionsLoading) {
+ return 정보를 불러오는 중입니다...
;
+ }
return (
@@ -24,9 +30,16 @@ export const GoodsInfo = ({ orderHistory }: Props) => {
-
- {detail.name} X {count}개
-
+ {detail.name}
+ {orderHistory.map((order, index) => {
+ const selectedOption = options.find(option => option.id === order.optionId);
+ return (
+
+ {selectedOption ? `${selectedOption.name} X ${order.quantity}개` : '옵션 없음'}
+
+ );
+ })}
+ {orderHistory[0].message}
@@ -70,3 +83,21 @@ const GoodsInfoTextTitle = styled.p`
overflow: hidden;
font-weight: 400;
`;
+
+const GoodsInfoTextMessage = styled.p`
+ font-size: 12px;
+ line-height: 16px;
+ color: #666;
+ margin-top: 4px;
+ white-space: pre-wrap;
+ word-break: break-word;
+`;
+
+const GoodsInfoTextOption = styled.p`
+ font-size: 12px;
+ line-height: 16px;
+ color: #666;
+ margin-top: 4px;
+ white-space: pre-wrap;
+ word-break: break-word;
+`;
\ No newline at end of file
diff --git a/src/components/features/Order/OrderForm/OrderInfo/index.tsx b/src/components/features/Order/OrderForm/OrderInfo/index.tsx
index 971241499..0bb841633 100644
--- a/src/components/features/Order/OrderForm/OrderInfo/index.tsx
+++ b/src/components/features/Order/OrderForm/OrderInfo/index.tsx
@@ -1,23 +1,36 @@
import { Divider } from '@chakra-ui/react';
import styled from '@emotion/styled';
-
import { useGetProductDetail } from '@/api/hooks/useGetProductDetail';
+import { useGetProductOptions } from '@/api/hooks/useGetProductOptions';
import { Button } from '@/components/common/Button';
import { Spacing } from '@/components/common/layouts/Spacing';
import type { OrderHistory } from '@/types';
-
import { HeadingText } from '../Common/HeadingText';
import { LabelText } from '../Common/LabelText';
import { CashReceiptFields } from '../Fields/CashReceiptFields';
+import { LoadingView } from '@/components/common/View/LoadingView';
type Props = {
- orderHistory: OrderHistory;
+ orderHistory: OrderHistory[];
};
+
export const OrderFormOrderInfo = ({ orderHistory }: Props) => {
- const { id, count } = orderHistory;
+ const productId = orderHistory[0].productId;
+ const { data: detail, isLoading: isDetailLoading } = useGetProductDetail({ productId: productId.toString() });
+ const { data: options, isLoading: isOptionsLoading } = useGetProductOptions({ productId: productId.toString() });
+
+ if (isDetailLoading || isOptionsLoading) {
+ return ;
+ }
+
+ if (!detail || !options) {
+ return 상품 정보를 불러오는 중 오류가 발생했습니다.
;
+ }
- const { data: detail } = useGetProductDetail({ productId: id.toString() });
- const totalPrice = detail.price * count;
+ const totalPrice = orderHistory.reduce((total, order) => {
+ const selectedOption = options.find(option => option.id === order.optionId);
+ return total + (selectedOption ? detail.price * order.quantity : 0);
+ }, 0);
return (
@@ -27,6 +40,23 @@ export const OrderFormOrderInfo = ({ orderHistory }: Props) => {
+
+ 상품명
+ {detail.name}
+
+ {orderHistory.map((order, index) => {
+ const selectedOption = options.find(option => option.id === order.optionId);
+ return (
+
+ 선택한 옵션
+ {selectedOption ? `${selectedOption.name} X ${order.quantity}개` : '옵션 없음'}
+
+ );
+ })}
+
+ 메시지
+ {orderHistory[0].message}
+
최종 결제금액
{totalPrice}원
@@ -56,4 +86,4 @@ const ItemWrapper = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
-`;
+`;
\ No newline at end of file
diff --git a/src/components/features/Order/OrderForm/index.test.tsx b/src/components/features/Order/OrderForm/index.test.tsx
index 0a00757e3..6e58b7daa 100644
--- a/src/components/features/Order/OrderForm/index.test.tsx
+++ b/src/components/features/Order/OrderForm/index.test.tsx
@@ -1,25 +1,26 @@
import '@testing-library/jest-dom/extend-expect';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { fireEvent,render, screen } from '@testing-library/react';
-import { FormProvider,useForm } from 'react-hook-form';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { FormProvider, useForm } from 'react-hook-form';
-import type { OrderFormData } from '@/types';
+import type { OrderFormData, OrderHistory } from '@/types';
import { OrderForm } from '.';
const queryClient = new QueryClient();
-const Wrapper = ({ children, orderHistory }: { children: React.ReactNode, orderHistory: { id: number, count: number } }) => {
+const Wrapper = ({ children, orderHistory }: { children: React.ReactNode, orderHistory: OrderHistory[] }) => {
const methods = useForm({
defaultValues: {
- productId: orderHistory.id,
- productQuantity: orderHistory.count,
+ optionId: orderHistory[0].optionId,
+ productQuantity: orderHistory[0].quantity,
senderId: 0,
receiverId: 0,
hasCashReceipt: false,
cashReceiptType: 'PERSONAL',
cashReceiptNumber: '',
+ message: orderHistory[0].message, // 변경
},
});
@@ -31,10 +32,14 @@ const Wrapper = ({ children, orderHistory }: { children: React.ReactNode, orderH
};
describe('OrderForm 컴포넌트', () => {
- const orderHistory = {
- id: 1,
- count: 2,
- };
+ const orderHistory: OrderHistory[] = [
+ {
+ productId: 1,
+ optionId: 1,
+ quantity: 2,
+ message: '',
+ },
+ ];
test('폼이 렌더링되는지 확인', () => {
render(
@@ -42,7 +47,7 @@ describe('OrderForm 컴포넌트', () => {
);
- expect(screen.getByText('주문 정보')).toBeInTheDocument();
+ expect(screen.getByText('결제 정보')).toBeInTheDocument();
});
test('유효하지 않은 입력값에 대해 에러 메시지 표시', () => {
@@ -51,7 +56,7 @@ describe('OrderForm 컴포넌트', () => {
);
- fireEvent.submit(screen.getByRole('form'));
+ fireEvent.submit(screen.getByRole('button', { name: /결제하기/i }));
expect(screen.getByText('메시지를 입력해주세요.')).toBeInTheDocument();
});
@@ -62,9 +67,9 @@ describe('OrderForm 컴포넌트', () => {
);
-
+
fireEvent.change(screen.getByPlaceholderText('선물과 함께 보낼 메시지를 적어보세요'), { target: { value: 'Test message' } });
- fireEvent.submit(screen.getByRole('form'));
+ fireEvent.submit(screen.getByRole('button', { name: /결제하기/i }));
expect(screen.queryByText('메시지를 입력해주세요.')).not.toBeInTheDocument();
});
@@ -102,18 +107,18 @@ describe('OrderForm 컴포넌트', () => {
);
- fireEvent.submit(screen.getByRole('form'));
+ fireEvent.submit(screen.getByRole('button', { name: /결제하기/i }));
expect(screen.getByText('메시지를 입력해주세요.')).toBeInTheDocument();
fireEvent.click(screen.getByLabelText(/현금영수증 신청/i));
fireEvent.change(screen.getByRole('combobox'), { target: { value: 'PERSONAL' } });
fireEvent.change(screen.getByPlaceholderText('(-없이) 숫자만 입력해주세요.'), { target: { value: 'abc' } });
- fireEvent.submit(screen.getByRole('form'));
+ fireEvent.submit(screen.getByRole('button', { name: /결제하기/i }));
expect(screen.getByText('현금영수증 번호는 숫자로만 입력해주세요.')).toBeInTheDocument();
fireEvent.change(screen.getByPlaceholderText('선물과 함께 보낼 메시지를 적어보세요'), { target: { value: 'Test message' } });
fireEvent.change(screen.getByPlaceholderText('(-없이) 숫자만 입력해주세요.'), { target: { value: '1234567890' } });
- fireEvent.submit(screen.getByRole('form'));
+ fireEvent.submit(screen.getByRole('button', { name: /결제하기/i }));
expect(screen.queryByText('메시지를 입력해주세요.')).not.toBeInTheDocument();
expect(screen.queryByText('현금영수증 번호는 숫자로만 입력해주세요.')).not.toBeInTheDocument();
});
diff --git a/src/components/features/Order/OrderForm/index.tsx b/src/components/features/Order/OrderForm/index.tsx
index 859939e31..9d6cc452c 100644
--- a/src/components/features/Order/OrderForm/index.tsx
+++ b/src/components/features/Order/OrderForm/index.tsx
@@ -1,34 +1,36 @@
import styled from '@emotion/styled';
import { FormProvider, useForm } from 'react-hook-form';
-
import { Spacing } from '@/components/common/layouts/Spacing';
import { SplitLayout } from '@/components/common/layouts/SplitLayout';
import type { OrderFormData, OrderHistory } from '@/types';
-
import { HEADER_HEIGHT } from '../../Layout/Header';
import { GoodsInfo } from './GoodsInfo';
import { OrderFormMessageCard } from './MessageCard';
import { OrderFormOrderInfo } from './OrderInfo';
+import { getBaseUrl } from '@/api/instance';
+import { useAuth } from '@/provider/Auth';
+import { orderHistorySessionStorage } from '@/utils/storage';
type Props = {
- orderHistory: OrderHistory;
+ orderHistory: OrderHistory[];
};
export const OrderForm = ({ orderHistory }: Props) => {
- const { id, count } = orderHistory;
-
const methods = useForm({
defaultValues: {
- productId: id,
- productQuantity: count,
+ optionId: orderHistory[0]?.optionId ?? 0,
+ productQuantity: orderHistory[0]?.quantity ?? 0,
senderId: 0,
receiverId: 0,
hasCashReceipt: false,
+ message: orderHistory[0]?.message ?? '',
},
});
+
const { handleSubmit } = methods;
+ const authInfo = useAuth();
- const handleForm = (values: OrderFormData) => {
+ const handleForm = async (values: OrderFormData) => {
const { errorMessage, isValid } = validateOrderForm(values);
if (!isValid) {
@@ -36,11 +38,62 @@ export const OrderForm = ({ orderHistory }: Props) => {
return;
}
- console.log('values', values);
- alert('주문이 완료되었습니다.');
+ const orders = orderHistorySessionStorage.get() as OrderHistory[];
+
+ const requests = orders.map(order => {
+ const requestBody = {
+ optionId: order.optionId,
+ quantity: order.quantity,
+ message: values.message,
+ };
+
+ console.log('서버로 전송하는 요청 데이터:', JSON.stringify(requestBody, null, 2));
+
+ return fetch(`${getBaseUrl()}/api/orders`, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${authInfo?.token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(requestBody),
+ credentials: 'include',
+ })
+ .then(async response => {
+ if (!response.ok) {
+ const errorBody = await response.text();
+ console.error(`옵션 ID ${order.optionId} 주문 실패:`, response.status, errorBody);
+ return { success: false, optionId: order.optionId, error: errorBody };
+ }
+ const data = await response.json();
+ console.log(`옵션 ID ${order.optionId} 주문 성공:`, data);
+ return { success: true, optionId: order.optionId, data };
+ })
+ .catch(error => {
+ console.error(`옵션 ID ${order.optionId} 주문 에러:`, error);
+ return { success: false, optionId: order.optionId, error: error.message };
+ });
+ });
+
+ try {
+ const results = await Promise.all(requests);
+ const successfulOrders = results.filter(result => result.success);
+ const failedOrders = results.filter(result => !result.success);
+
+ if (successfulOrders.length > 0) {
+ console.log('성공한 주문:', successfulOrders);
+ alert(`${successfulOrders.length}개의 주문이 완료되었습니다.`);
+ }
+
+ if (failedOrders.length > 0) {
+ console.error('실패한 주문:', failedOrders);
+ alert(`${failedOrders.length}개의 주문 중 오류가 발생했습니다.`);
+ }
+ } catch (error) {
+ console.error('주문 처리 중 예외 발생:', error);
+ alert('주문 처리 중 예기치 않은 오류가 발생했습니다.');
+ }
};
- // Submit 버튼을 누르면 form이 제출되는 것을 방지하기 위한 함수
const preventEnterKeySubmission = (e: React.KeyboardEvent) => {
const target = e.target as HTMLFormElement;
if (e.key === 'Enter' && !['TEXTAREA'].includes(target.tagName)) {
@@ -64,6 +117,8 @@ export const OrderForm = ({ orderHistory }: Props) => {
};
const validateOrderForm = (values: OrderFormData): { errorMessage?: string; isValid: boolean } => {
+ console.log('유효성 검사 값:', values);
+
if (values.hasCashReceipt) {
if (!values.cashReceiptNumber) {
return {
@@ -80,17 +135,17 @@ const validateOrderForm = (values: OrderFormData): { errorMessage?: string; isVa
}
}
- if (values.messageCardTextMessage.length < 1) {
+ if (values.message.length < 1) {
return {
errorMessage: '메시지를 입력해주세요.',
isValid: false,
};
}
- if (values.messageCardTextMessage.length > 100) {
+ if (values.message.length > 100) {
return {
- errorMessage: '메시지는 100자 이내로 입력해주세요.',
- isValid: false,
+ errorMessage: '메시지는 100자 이내로 입력해주세요.',
+ isValid: false,
};
}
@@ -102,4 +157,4 @@ const validateOrderForm = (values: OrderFormData): { errorMessage?: string; isVa
const Wrapper = styled.div`
border-left: 1px solid #e5e5e5;
height: calc(100vh - ${HEADER_HEIGHT});
-`;
+`;
\ No newline at end of file
diff --git a/src/hooks/useCurrentCategory.ts b/src/hooks/useCurrentCategory.ts
index 6faad3a18..3f43de070 100644
--- a/src/hooks/useCurrentCategory.ts
+++ b/src/hooks/useCurrentCategory.ts
@@ -6,15 +6,17 @@ import { getCurrentCategory } from '@/components/features/Category/CategoryHeroS
type Props = { categoryId: string };
export const useCurrentCategory = ({ categoryId }: Props) => {
- const { data, isLoading, isError } = useGetCategories();
+ const { data: categories, isLoading, isError } = useGetCategories();
const isRender = useMemo(() => {
- if (isLoading || isError) return false;
- if (!data) return false;
+ if (isLoading || isError)
+ return false;
+ if (!categories)
+ return false;
return true;
- }, [data, isLoading, isError]);
+ }, [categories, isLoading, isError]);
- const currentTheme = getCurrentCategory(categoryId, data ?? []);
+ const currentTheme = getCurrentCategory(categoryId, categories ?? []);
return {
isRender,
diff --git a/src/hooks/useHandleOrderHistory.ts b/src/hooks/useHandleOrderHistory.ts
index f213946f9..1d5e2370b 100644
--- a/src/hooks/useHandleOrderHistory.ts
+++ b/src/hooks/useHandleOrderHistory.ts
@@ -6,14 +6,14 @@ import type { OrderHistory } from '@/types';
import { orderHistorySessionStorage } from '@/utils/storage';
export const useHandleOrderHistory = () => {
- const [orderHistory, setOrderHistory] = useState();
+ const [orderHistory, setOrderHistory] = useState([]);
const navigate = useNavigate();
+
useEffect(() => {
const prevOrderHistory = orderHistorySessionStorage.get();
- if (!prevOrderHistory) {
+ if (!prevOrderHistory || prevOrderHistory.length === 0) {
alert('주문 내역이 없습니다.\n메인 화면으로 이동합니다.');
-
navigate(RouterPath.home);
return;
}
@@ -22,4 +22,4 @@ export const useHandleOrderHistory = () => {
}, [navigate]);
return { orderHistory };
-};
+};
\ No newline at end of file
diff --git a/src/hooks/useOrderFormContext.ts b/src/hooks/useOrderFormContext.ts
index 0a0124ef6..812241a72 100644
--- a/src/hooks/useOrderFormContext.ts
+++ b/src/hooks/useOrderFormContext.ts
@@ -2,4 +2,4 @@ import { useFormContext } from 'react-hook-form';
import type { OrderFormData } from '@/types';
-export const useOrderFormContext = useFormContext;
+export const useOrderFormContext = () => useFormContext();
\ No newline at end of file
diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx
index dab8c50b4..4a949fa25 100644
--- a/src/pages/Login/index.tsx
+++ b/src/pages/Login/index.tsx
@@ -1,80 +1,25 @@
import styled from '@emotion/styled';
-import { useState } from 'react';
-import { useNavigate, useSearchParams } from 'react-router-dom';
-
import KAKAO_LOGO from '@/assets/kakao_logo.svg';
import { Button } from '@/components/common/Button';
-import { UnderlineTextField } from '@/components/common/Form/Input/UnderlineTextField';
-import { Spacing } from '@/components/common/layouts/Spacing';
import { breakpoints } from '@/styles/variants';
-import { authSessionStorage } from '@/utils/storage';
+import { getBaseUrl } from '@/api/instance';
export const LoginPage = () => {
- const [email, setEmail] = useState('');
- const [password, setPassword] = useState('');
- const [queryParams] = useSearchParams();
- const navigate = useNavigate();
-
- const handleConfirm = async () => {
- if (!email || !password) {
- alert('아이디와 비밀번호를 입력해주세요.');
- return;
- }
-
- try {
- const response = await fetch('/api/members/login', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ email, password }),
- });
-
- if (response.status === 200) {
- const data = await response.json();
- authSessionStorage.set(JSON.stringify({ token: data.token, email: data.email }));
- const redirectUrl = queryParams.get('redirect') ?? `${window.location.origin}/`;
- window.location.replace(redirectUrl);
- } else if (response.status === 403) {
- alert('이메일 또는 비밀번호가 잘못되었습니다.');
- } else {
- alert('로그인에 실패했습니다.');
- }
- } catch (error) {
- console.error('로그인 에러:', error);
- alert('로그인 에러 발생');
- }
+ const handleKakaoLogin = () => {
+ const baseUrl = getBaseUrl();
+ window.location.href = `${baseUrl}/oauth/kakao`;
};
return (
- setEmail(e.target.value)} />
-
- setPassword(e.target.value)}
- />
-
-
-
-
);
@@ -103,17 +48,4 @@ const FormWrapper = styled.article`
border: 1px solid rgba(0, 0, 0, 0.12);
padding: 60px 52px;
}
-`;
-
-const Footer = styled.div`
- margin-top: 20px;
- display: flex;
- justify-content: center;
-`;
-
-const RegisterLink = styled.span`
- font-size: 12px;
- color: rgba(0, 0, 0, 0.6);
- cursor: pointer;
- text-decoration: underline;
`;
\ No newline at end of file
diff --git a/src/pages/MyAccount/index.tsx b/src/pages/MyAccount/index.tsx
index 32f0eb5cc..0186d26dc 100644
--- a/src/pages/MyAccount/index.tsx
+++ b/src/pages/MyAccount/index.tsx
@@ -1,70 +1,127 @@
import { Box, Button, HStack, Image, Text, VStack } from '@chakra-ui/react';
import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
+import base64 from 'base-64';
-import { RouterPath } from '@/routes/path';
-import { authSessionStorage } from '@/utils/storage';
+import { fetchInstance, getBaseUrl } from '@/api/instance';
-interface Product {
- id: number;
- name: string;
- price: number;
- imageUrl: string;
+interface WishlistItem {
+ wishId: number;
+ productId: number;
+ productName: string;
+ productPrice: number;
+ productImageUrl: string;
}
-interface WishlistItem {
- id: number;
- product: Product;
+interface WishlistResponseData {
+ totalElements: number;
+ totalPages: number;
+ first: boolean;
+ last: boolean;
+ size: number;
+ content: WishlistItem[];
+ number: number;
+ sort: {
+ empty: boolean;
+ sorted: boolean;
+ unsorted: boolean;
+ };
+ numberOfElements: number;
+ pageable: {
+ pageNumber: number;
+ pageSize: number;
+ sort: {
+ empty: boolean;
+ sorted: boolean;
+ unsorted: boolean;
+ };
+ offset: number;
+ paged: boolean;
+ unpaged: boolean;
+ };
+ empty: boolean;
}
export const MyAccountPage = () => {
- const authInfo = JSON.parse(authSessionStorage.get() || '{}');
+ const authInfo = localStorage.getItem('authToken');
const [wishlist, setWishlist] = useState([]);
const [page, setPage] = useState(0);
const [totalPages, setTotalPages] = useState(0);
+ const [userName, setUserName] = useState("사용자");
useEffect(() => {
- const fetchWishlist = async () => {
+ if (authInfo) {
try {
- const response = await fetch(`/api/wishes?page=${page}&size=10&sort=createdDate,desc`, {
+ const [headerEncoded, payloadEncoded, signature] = authInfo.split('.');
+ const payload = JSON.parse(base64.decode(payloadEncoded));
+
+ console.log('Token Header:', JSON.parse(base64.decode(headerEncoded)));
+ console.log('Token Payload:', payload);
+ console.log('Token Signature:', signature);
+ console.log('Payload Keys:', Object.keys(payload));
+
+ if (payload.sub) {
+ const email = payload.sub;
+ const displayName = email.split('@')[0];
+ setUserName(displayName);
+ }
+ } catch (error) {
+ console.error('토큰 디코딩 에러:', error);
+ }
+ } else {
+ setUserName("사용자");
+ }
+ }, [authInfo]);
+
+ const getWishlistPath = () => `${getBaseUrl()}/api/wishes`;
+
+ const fetchWishlist = async (page: number) => {
+ try {
+ const response = await fetchInstance.get(
+ `${getWishlistPath()}?page=${page}&size=10&sort=createdDate,desc`,
+ {
headers: {
- Authorization: `Bearer ${authInfo.token}`,
+ Authorization: `Bearer ${authInfo}`,
},
- });
-
- if (response.status === 200) {
- const data = await response.json();
- setWishlist(data.content);
- setTotalPages(data.totalPages);
- } else if (response.status === 401) {
- alert('토큰이 유효하지 않습니다. 다시 로그인해주세요.');
- } else {
- alert('위시 리스트를 불러오는데 실패했습니다.');
+ withCredentials: true,
}
- } catch (error) {
- console.error('위시 리스트 조회 에러:', error);
- alert('위시 리스트 조회 에러가 발생했습니다.');
+ );
+
+ if (response.status === 200) {
+ const data = response.data;
+ console.log('Response data:', data);
+ setWishlist(data.content);
+ setTotalPages(data.totalPages);
+ } else if (response.status === 403) {
+ alert('토큰이 유효하지 않습니다. 다시 로그인해주세요.');
+ } else {
+ alert('위시 리스트를 불러오는데 실패했습니다.');
}
- };
+ } catch (error) {
+ console.error('위시 리스트 조회 에러:', error);
+ alert('위시 리스트 조회 에러가 발생했습니다.');
+ }
+ };
- fetchWishlist();
- }, [page, authInfo.token]);
+ useEffect(() => {
+ fetchWishlist(page);
+ }, [page, authInfo]);
const handleDelete = async (wishId: number) => {
try {
- const response = await fetch(`/api/wishes/${wishId}`, {
+ const response = await fetch(`${getBaseUrl()}/api/wishes/${wishId}`, {
method: 'DELETE',
headers: {
- Authorization: `Bearer ${authInfo.token}`,
+ Authorization: `Bearer ${authInfo}`,
},
});
- if (response.status === 204) {
- setWishlist((prev) => prev.filter((item) => item.id !== wishId));
+ if (response.ok) {
+ setWishlist((prev) => prev.filter((item) => item.wishId !== wishId));
alert('관심 목록에서 삭제되었습니다.');
- } else if (response.status === 404) {
+ } else if (response.status === 400) {
alert('관심 상품을 찾을 수 없습니다.');
- } else if (response.status === 401) {
+ } else if (response.status === 403) {
alert('토큰이 유효하지 않습니다. 다시 로그인해주세요.');
} else {
alert('관심 목록 삭제에 실패했습니다.');
@@ -76,32 +133,31 @@ export const MyAccountPage = () => {
};
const handleLogout = () => {
- authSessionStorage.set(undefined);
-
- const redirectURL = `${window.location.origin}${RouterPath.home}`;
+ localStorage.removeItem('authToken');
+ const redirectURL = `${window.location.origin}`;
window.location.replace(redirectURL);
};
return (
- {authInfo?.email}님 안녕하세요!
+ {authInfo ? userName : "로그인이 필요합니다"}님 안녕하세요!
{wishlist.length > 0 ? (
wishlist.map((item) => (
-
+
-
+
- {item.product.name}
- {item.product.price}원
+ {item.productName}
+ {item.productPrice}원
diff --git a/src/pages/Order/index.tsx b/src/pages/Order/index.tsx
index 1db6b35da..76b03fb06 100644
--- a/src/pages/Order/index.tsx
+++ b/src/pages/Order/index.tsx
@@ -6,10 +6,11 @@ import { useHandleOrderHistory } from '@/hooks/useHandleOrderHistory';
export const OrderPage = () => {
const { orderHistory } = useHandleOrderHistory();
- if (!orderHistory) return ;
+ if (!orderHistory.length) return ;
+
return (
} rejectedFallback={에러 페이지
}>
);
-};
+};
\ No newline at end of file
diff --git a/src/provider/Auth/index.tsx b/src/provider/Auth/index.tsx
index 77cbb73a9..f6bbd11e0 100644
--- a/src/provider/Auth/index.tsx
+++ b/src/provider/Auth/index.tsx
@@ -1,8 +1,6 @@
import type { ReactNode } from 'react';
import { createContext, useContext, useEffect, useState } from 'react';
-import { authSessionStorage } from '@/utils/storage';
-
type AuthInfo = {
id: string;
name: string;
@@ -12,7 +10,7 @@ type AuthInfo = {
export const AuthContext = createContext(undefined);
export const AuthProvider = ({ children }: { children: ReactNode }) => {
- const currentAuthToken = authSessionStorage.get();
+ const currentAuthToken = localStorage.getItem('authToken');
const [isReady, setIsReady] = useState(!currentAuthToken);
const [authInfo, setAuthInfo] = useState(undefined);
@@ -32,4 +30,4 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
return {children};
};
-export const useAuth = () => useContext(AuthContext);
+export const useAuth = () => useContext(AuthContext);
\ No newline at end of file
diff --git a/src/types/index.ts b/src/types/index.ts
index 33c397459..582c5183c 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -32,14 +32,16 @@ export type GoodsDetailOptionItemData = {
};
export type OrderHistory = {
- id: number;
- count: number;
+ productId: number;
+ optionId: number;
+ quantity: number;
+ message: string;
};
export type OrderFormData = {
- productId: number;
+ optionId: number;
productQuantity: number;
- messageCardTextMessage: string;
+ message: string;
senderId: number;
receiverId: number;
hasCashReceipt: boolean;
diff --git a/src/utils/storage/index.ts b/src/utils/storage/index.ts
index 5d9229525..3c57e1dba 100644
--- a/src/utils/storage/index.ts
+++ b/src/utils/storage/index.ts
@@ -26,5 +26,5 @@ export const orderHistorySessionStorage = initStorage('orderHistory', sessionSto
interface StorageKey {
authToken?: string;
- orderHistory?: OrderHistory;
+ orderHistory?: OrderHistory[];
}