Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

경북대 FE_이정민 6주차 과제 Step3 #84

Open
wants to merge 29 commits into
base: userjmmm
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a3f7313
chore: 로그인 및 관심목록 코드 준비
Jul 31, 2024
966fa17
refactor: useGetCategories 파일명 오타 수정
Jul 31, 2024
a2e0617
refactor: CategoryResponseData 타입을 Pageable에 맞추어 수정
Jul 31, 2024
0e7e5bb
refactor: 옵션 선택 시 총 결제 금액이 계산되도록 수정
Jul 31, 2024
6ec10aa
refactor: 옵션 핸들링 로직 수정
Jul 31, 2024
183d422
feat: 상단 네비게이션 바에서 백엔드 API 선택하는 기능 추가
Jul 31, 2024
fd3109d
refactor: baseUrl이 변경될 때 실시간으로 반영하도록 수정
Aug 1, 2024
f98715a
feat: 모든 카테고리를 받아올 수 있도록 하는 로직 추가
Aug 1, 2024
300f340
refactor: useGetProducts.ts에서 getProductsPath의 쿼리 매개변수 순서 변경
Aug 1, 2024
6895a87
feat: 서버에서 받아온 token을 저장해서 로그인 상태 유지되는 로직 추가
Aug 1, 2024
c8f8353
feat: 위시 조회시 Page<WishList>로 return 받을 수 있도록 Data 타입 추가
Aug 1, 2024
048cc4d
refactor: localStorage로 로그아웃 기능 가능하도록 수정
Aug 1, 2024
71dab77
feat: 변경된 API에 맞춰 위시 삭제 기능 추가
Aug 2, 2024
bd3b2a7
feat: 토큰 디코딩해서 사용자 이름 받아오는 기능 추가
Aug 2, 2024
e03b327
feat: 주문 페이지에서 상품 옵션까지 받아오는 기능 추가
Aug 2, 2024
33e7769
chore(ui): 카카오로 로그인 하는 로그인 페이지 UI 수정
Aug 2, 2024
c10a150
refactor: 옵션마다 post 요청 각각 진행하도록 수정
Aug 2, 2024
9edd99b
refactor: 재용님 BASE_URL 변경
Aug 2, 2024
a876457
refactor(ui): 주문하기 페이지에서 선택한 옵션을 모두 확인할 수 있게 UI 변경
Aug 2, 2024
4a41411
feat: 포인트 기능 - 포인트로 결제 시 10% 할인되는 기능 추가
Aug 2, 2024
b16ff7b
refactor: 결제하기 누를 때, API 요청을 직렬로 보내 포인트가 차감되도록 로직 수정
Aug 2, 2024
bba4c08
feat: 모바일에서도 옵션이 잘 뜨도록 반응형 코드 추가
Aug 4, 2024
de019d1
docs: 4단계 답변 READMD.md에 작성
Aug 4, 2024
6bedd4b
feat: 보유 포인트 조회 기능 추가
Aug 5, 2024
b393c36
feat: 주문 완료 후 홈으로 리다이렉트하는 로직 추가
Aug 5, 2024
461067f
refactor: 결제 실패 시 alert창 나오도록 하는 로직 수정
Aug 5, 2024
a408d21
feat: 보경 님 BASE_URL 수정
Aug 5, 2024
87f5d1c
refactor: 지호님 BASE URL 수정
Aug 5, 2024
839a3fd
refactor: 백엔드 api 디폴트값 변경
Aug 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -87,4 +89,4 @@
"overrides": {
"react-refresh": "0.11.0"
}
}
}
33 changes: 33 additions & 0 deletions public/token.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OAuth Callback</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
margin-top: 50px;
}
</style>
</head>
<body>
<h1>OAuth Callback</h1>
<p id="tokenDisplay">토큰을 기다리고 있습니다...</p>
<script>
const params = new URLSearchParams(window.location.search);
const token = params.get('tokenValue');

if (token) {
// 토큰이 존재하면 로컬 스토리지에 저장
console.log('Received token:', token);
localStorage.setItem('authToken', token);
window.location.href = '/';
} else {
console.error('Token not found in query parameters.');
tokenDisplay.textContent = 'Token not found in query parameters.';
}
</script>
</body>
</html>
59 changes: 43 additions & 16 deletions src/api/hooks/categories.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
54 changes: 46 additions & 8 deletions src/api/hooks/useGetCategories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CategoryData[]> => {
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<CategoryResponseData>(
`${getCategoriesPath()}?page=${currentPage}&size=100`
);
allCategories = [...allCategories, ...response.data.content];
hasNextPage = !response.data.last;
currentPage++;
}

export const getCategories = async () => {
const response = await fetchInstance.get<CategoryResponseData>(getCategoriesPath());
return response.data;
return allCategories;
};

export const useGetCategories = () =>
useQuery({
queryKey: categoriesQueryKey,
queryKey: ['categories'],
queryFn: getCategories,
});
4 changes: 2 additions & 2 deletions src/api/hooks/useGetProductDetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<GoodsDetailResponseData>(
Expand Down
4 changes: 2 additions & 2 deletions src/api/hooks/useGetProductOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ 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;

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<ProductOptionsResponseData>(
Expand Down
15 changes: 9 additions & 6 deletions src/api/hooks/useGetProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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<ProductsResponseData> => {
Expand All @@ -58,6 +60,7 @@ export const getProducts = async (params: RequestParams): Promise<ProductsRespon
};

type Params = Pick<RequestParams, 'maxResults' | 'categoryId'> & { initPageToken?: string };

export const useGetProducts = ({
categoryId,
maxResults = 20,
Expand All @@ -71,4 +74,4 @@ export const useGetProducts = ({
initialPageParam: initPageToken,
getNextPageParam: (lastPage) => lastPage.nextPageToken,
});
};
};
22 changes: 18 additions & 4 deletions src/api/instance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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({
Expand All @@ -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';
};
Loading