Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
03dabfa
refactor : 키관리 페이지 공통내용 컴포넌트화
happyboyhj-redtable Feb 18, 2025
dd823ee
feat : 로그인/회원가입 페이지 엔터키로 submit 가능
happyboyhj-redtable Feb 18, 2025
8467893
feat : ProtectedRoute 설정 & 로그인 후 이전페이지로 리다이렉트
happyboyhj-redtable Feb 18, 2025
e788019
refactor : 인증정보 방식 변경 (세션스토리지 저장후 zustand에서 최초 1회만 체크)
happyboyhj-redtable Feb 18, 2025
fc6c0f8
refactor : API 호출 hook 위치변경
happyboyhj-redtable Feb 18, 2025
c3a0638
refactor : API 라우터 상수화
happyboyhj-redtable Feb 19, 2025
c9b4587
refactor : 키관리 페이지 컴포넌트 이름 변경
happyboyhj-redtable Feb 19, 2025
82cabba
feat : 키 갱신 기능
happyboyhj-redtable Feb 19, 2025
cdaae14
refactor : 키관리 관련 컴포넌트명 변경
happyboyhj-redtable Feb 19, 2025
1cbe0ac
refactor : 키관리 관련 컴포넌트명 변경
happyboyhj-redtable Feb 19, 2025
272b8c3
chore : proxy host env로 이동
happyboyhj-redtable Feb 19, 2025
48af003
feat : 키 갱신 - 엔드포인트, res 업데이트
happyboyhj-redtable Feb 20, 2025
d6a64d1
feat : 키 조회 API 연결
happyboyhj-redtable Feb 20, 2025
96957cd
feat : NotIssed 진행중
happyboyhj-redtable Feb 20, 2025
078ce62
refactor : NotIssued 작업완료
H-Genie Feb 20, 2025
202f3f8
feat : Sidebar 사용자 이름 표시
H-Genie Feb 20, 2025
09c0eb5
feat : 로그아웃 기능 진행중
H-Genie Feb 20, 2025
ea2a521
feat : 로그아웃 기능 완성
H-Genie Feb 20, 2025
060e5e4
refactor : 아이콘 컴포넌트 모듈화 / style : 로그아웃 버튼 정렬
H-Genie Feb 20, 2025
fbbb68c
fix : lint 오류 수정
H-Genie Feb 21, 2025
f0201cd
chore : api 호스트 수정
H-Genie Feb 23, 2025
b42895b
fix : apiWrapper prefix 제거
H-Genie Feb 23, 2025
eebc5bb
fix : api 엔드포인트 수정
H-Genie Feb 24, 2025
9738bfb
fix : production모드에서 API URL 수정
H-Genie Feb 24, 2025
a6bfe71
fix : production모드에서 API URL 수정(2)
H-Genie Feb 24, 2025
58c86ee
fix : production모드에서 API URL 수정(3)
H-Genie Feb 24, 2025
2e88e70
fix : production모드에서 API URL 수정(4)
H-Genie Feb 24, 2025
28bb5b1
feat : 거래내역 API 연결
H-Genie Feb 25, 2025
dc73a5b
chore : msw 삭제
H-Genie Feb 25, 2025
2a954e4
fix : CORS 해제
H-Genie Feb 25, 2025
bc39cb8
style : 거래내역 페이지 스크롤
H-Genie Feb 27, 2025
45dd7aa
feat : 더보기 버튼으로 페이징 기능 구현
H-Genie Feb 27, 2025
930fbb1
feat : keepPreviousData
H-Genie Feb 27, 2025
eddbfe2
feat : transactions 스타일 조정
H-Genie Feb 27, 2025
98dab57
feat : shadcn Select 진행중
H-Genie Feb 27, 2025
20cea3e
style : 필터 섹션 퍼블리싱
H-Genie Feb 27, 2025
273095b
feat : 필터 기능 완성
H-Genie Feb 27, 2025
afafae0
fix : 검색 쿼리 수정
H-Genie Feb 27, 2025
e5a1046
feat : 사용방법 링크 수정
happyboyhj-redtable Mar 5, 2025
62a6558
refactor : TransactionPage / 전체적인 코드 정리
happyboyhj-redtable Mar 5, 2025
425848f
chore : KeyManagament 불필요한 타입 정리
happyboyhj-redtable Mar 5, 2025
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
45 changes: 0 additions & 45 deletions src/components/template/ApiKeys/Issued.tsx

This file was deleted.

24 changes: 0 additions & 24 deletions src/components/template/ApiKeys/NotIssued.tsx

This file was deleted.

8 changes: 8 additions & 0 deletions src/components/template/IconComponnt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as LucideIcons from 'lucide-react';

const IconComponnt = ({ icon }: { icon: keyof typeof LucideIcons }) => {
const LucideIcon = LucideIcons[icon] as LucideIcons.LucideIcon;
return <LucideIcon />;
};

export default IconComponnt;
23 changes: 23 additions & 0 deletions src/components/template/Key/Info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Link } from 'react-router-dom';

const ApiKeyInfo = () => {
return (
<div className='mb-8'>
<p className='text-center text-light-40 text-sm mb-4'>
한 계정당 키는 한개만 발급가능합니다
</p>
<p className='text-center text-light-40 text-sm mb-4'>
갱신하면 새로운 키가 발급되어 기존의 키는 사용할 수 없습니다
</p>
<Link
to={'https://github.com/FC-InnerCircle-ICD2/fintech-FE-SDK'}
target='_blank'
className='block text-center text-primary hover:underline'
>
API 사용방법
</Link>
</div>
);
};

export default ApiKeyInfo;
62 changes: 62 additions & 0 deletions src/components/template/Key/Issued.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Button } from '@components/ui/button';
import Info from './Info';
import Separator from './Separator';
import { createToastSuccess } from '@lib/toast';
import { useKeys } from '@hooks/api/useKeys';
import { useAuthStore } from '@store/authStore';
import { useState } from 'react';

const Issued = ({ issuedApiKey }: { issuedApiKey: string }) => {
const [apiKey, setApiKey] = useState<string>(issuedApiKey);
const { mutate: renewKey } = useKeys();
const { auth } = useAuthStore();

const handleCopy = () => {
const apiKey = document.getElementById('api-key')!.textContent!;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어짜피 apiKey를 상태값으로 저장하고 있는 것 아닌가요? 로그인 가능한 계정이 무엇인지 몰라서 테스트는 못해봤지만, 아마 apiKey 상태값의 초기값으로 props가 들어가있고 handleRenew에서 set하고 있어서 apiKey 상태값으로 복사가 가능해보이는데요!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 그게 아니라면 useRef로 가져올 수도 있을 것 같아요!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React스럽게요!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

직접적으로 태그의 스타일을 변경하는 경우라면 useRef를 사용했겠지만 단순히 값만 읽어오는 것이기에 불필요한 동작이라고 생각합니다

navigator.clipboard.writeText(apiKey).then(() => {
createToastSuccess('', 'API 키를 복사하였습니다.');
});
};

const handleRenew = () => {
renewKey(
{ id: auth!.id },
{
onSuccess: ({ data }) => {
setApiKey(data.apiKey);
createToastSuccess('', 'API 키가 성공적으로 갱신되었습니다.');
},
},
);
};

return (
<div className='bg-white rounded-3xl shadow-lg p-24 flex flex-col items-center'>
<h1
className='text-2xl font-bold text-primary text-center mb-4'
id='api-key'
>
{apiKey}
</h1>

<p
className='mb-4 text-xs text-primary cursor-pointer hover:underline'
onClick={handleCopy}
>
복사하기
</p>

<Separator />

<Info />

<Separator />

<Button size={'rounded'} onClick={handleRenew}>
갱신하기
</Button>
</div>
);
};

export default Issued;
42 changes: 42 additions & 0 deletions src/components/template/Key/NotIssued.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Button } from '@components/ui/button';
import Info from './Info';
import { useAuthStore } from '@store/authStore';
import { useKeys } from '@hooks/api/useKeys';
import { createToastSuccess } from '@lib/toast';

const NotIssued = ({
onIssueSuccess,
}: {
onIssueSuccess: (value: string) => void;
}) => {
const { auth } = useAuthStore();
const { mutate: renewKey } = useKeys();

const handleIssue = () => {
renewKey(
{ id: auth!.id },
{
onSuccess: ({ data }) => {
createToastSuccess('', 'API 키가 성공적으로 발급되었습니다.');
onIssueSuccess(data.apiKey);
},
},
);
};

return (
<div className='bg-white rounded-3xl shadow-lg p-24 flex flex-col items-center'>
<h1 className='text-2xl font-bold text-primary text-center mb-10'>
키 발급하기
</h1>

<Info />

<Button size={'rounded'} onClick={handleIssue}>
발급하기
</Button>
</div>
);
};

export default NotIssued;
7 changes: 7 additions & 0 deletions src/components/template/Key/Separator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const Separator = () => {
return (
<div role='separator' className='w-full h-[1px] bg-light-border mb-8' />
);
};

export default Separator;
6 changes: 2 additions & 4 deletions src/components/template/SideBar/SideBarItem.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import * as LucideIcons from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import type { SideBarItemProps } from '@type/sidebar';

import IconComponnt from '@components/template/IconComponnt';
const SideBarItem = ({ icon, name, link, isActive }: SideBarItemProps) => {
const navigate = useNavigate();
const IconComponent = LucideIcons[icon] as LucideIcons.LucideIcon;

return (
<li
Expand All @@ -13,7 +11,7 @@ const SideBarItem = ({ icon, name, link, isActive }: SideBarItemProps) => {
}`}
onClick={() => navigate(link)}
>
<IconComponent />
<IconComponnt icon={icon} />
<p>{name}</p>
</li>
);
Expand Down
17 changes: 14 additions & 3 deletions src/components/template/SideBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { sideBarItems } from '@constants/sidebar.ts';
import SideBarItem from './SideBarItem.tsx';
import { useLocation } from 'react-router-dom';

import { useAuthStore } from '@store/authStore';
import IconComponnt from '../IconComponnt.tsx';
const SideBar = () => {
const { pathname } = useLocation();
const { auth, logout } = useAuthStore();

return (
<aside className='w-60 h-screen bg-[#f1f1f1] p-12'>
<figure className='flex flex-col items-center gap-2'>
<img src='/logo.png' className='w-8' />
<h4 className='font-bold text-xl truncate'>Admin</h4>
<h4 className='font-bold text-xl truncate'>{auth?.name}</h4>
</figure>

<nav>
<nav className='flex flex-col h-[calc(100%-100px)]'>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아마 여기도 aside 요소에 flex flex-col 주고 nav 요소에 flex-1 걸어버리면 원하시는 것과 동일한 형태가 나오지 않을까 합니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지난번에 알려주신대로 수정했는데 제가 의도한것과 다르게 동작합니다
알려주신대로 하면 그냥 비율을 유지한채로 폭이 줄어들때 비례해서 줄어드는거고
제가 의도한건 들어들더라도 의도한 최소값은 유지하는거였습니다
이왕 수정한거는 그냥 두겠습니다

<ul>
{sideBarItems.map((item) => (
<SideBarItem
Expand All @@ -24,6 +26,15 @@ const SideBar = () => {
/>
))}
</ul>
<ul className='mt-auto px-4'>
<li
className='cursor-pointer text-center flex gap-2'
onClick={() => logout()}
Comment thread
H-Genie marked this conversation as resolved.
>
<IconComponnt icon='LogOut' />
<p>로그아웃</p>
</li>
</ul>
</nav>
</aside>
);
Expand Down
17 changes: 17 additions & 0 deletions src/components/template/auth/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useAuthStore } from '@store/authStore';
import { Navigate, useLocation } from 'react-router-dom';
import { ROUTES } from '@constants/routes';
interface ProtectedRouteProps {
children: React.ReactNode;
}

export const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
const { auth } = useAuthStore();
const location = useLocation();

if (!auth) {
return <Navigate to={ROUTES.LOGIN} state={{ from: location }} replace />;
}

return <>{children}</>;
};
5 changes: 4 additions & 1 deletion src/constants/apiEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ export const API_ENDPOINTS = {
PAYMENTS: getPaymentsEndpoint,
TRANSACTIONS: getTransactionEndpoint,
},
KEY: `${BASE_URL}/key`,
MANAGEMENT: {
KEYS: `${BASE_URL}/keys`,
ID: (id: string) => `${BASE_URL}/keys/${id}`,
},
} as const;
7 changes: 7 additions & 0 deletions src/constants/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const ROUTES = {
MAIN: '/',
LOGIN: '/login',
SIGNUP: '/signup',
TRANSACTIONS: '/transactions',
KEY_MANAGEMENT: '/key-management',
};
5 changes: 3 additions & 2 deletions src/constants/sidebar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as LucideIcons from 'lucide-react';
import { ROUTES } from '@constants/routes';

export const sideBarItems: {
name: string;
Expand All @@ -8,11 +9,11 @@ export const sideBarItems: {
{
name: '거래내역',
icon: 'FileText',
link: '/transactions',
link: ROUTES.TRANSACTIONS,
},
{
name: '키 관리',
icon: 'KeyRound',
link: '/api-keys',
link: ROUTES.KEY_MANAGEMENT,
},
];
13 changes: 9 additions & 4 deletions src/hooks/useAuth.ts → src/hooks/api/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import { apiClient } from '@lib/api/apiClient';
import { API_ENDPOINTS } from '@constants/apiEndpoints';
import type { LoginReq, LoginRes, SignupReq } from '@type/auth';
import { createToastSuccess, createToastError } from '@lib/toast';
import { useNavigate } from 'react-router-dom';
import { useNavigate, useLocation } from 'react-router-dom';
import { useAuthStore } from '@store/authStore';
import { ROUTES } from '@constants/routes';

export const useLogin = () => {
const navigate = useNavigate();
const setAuth = useAuthStore((state) => state.setAuth);
const location = useLocation();

return useMutation({
mutationFn: (data: LoginReq) =>
apiClient.post<LoginReq, LoginRes>(API_ENDPOINTS.LOGIN, data),
onSuccess: (response: LoginRes) => {
sessionStorage.setItem('auth', JSON.stringify(response.data));
Comment thread
H-Genie marked this conversation as resolved.
setAuth(response.data);
createToastSuccess('로그인 성공', '메인 페이지로 이동합니다');
navigate('/');
createToastSuccess('', '로그인되었습니다');

const from = location.state?.from?.pathname || '/';
navigate(from);
},
onError: createToastError('로그인 실패'),
});
Expand All @@ -29,7 +34,7 @@ export const useSignup = () => {
mutationFn: (data: SignupReq) => apiClient.post(API_ENDPOINTS.SIGNUP, data),
onSuccess: () => {
createToastSuccess('회원가입 성공', '로그인 페이지로 이동합니다');
navigate('/login');
navigate(ROUTES.LOGIN);
},
onError: createToastError('회원가입 실패'),
});
Expand Down
18 changes: 18 additions & 0 deletions src/hooks/api/useKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { apiClient } from '@lib/api/apiClient';
import { API_ENDPOINTS } from '@constants/apiEndpoints';
import type { KeyReq, KeyRes } from '@type/key';

export const useKeys = () => {
return useMutation({
mutationFn: (data: KeyReq) =>
apiClient.post<KeyReq, KeyRes>(API_ENDPOINTS.MANAGEMENT.KEYS, data),
});
};

export const useKeysId = (id: string) => {
return useQuery({
queryKey: ['get-key'],
queryFn: () => apiClient.get<KeyRes>(API_ENDPOINTS.MANAGEMENT.ID(id)),
});
};
File renamed without changes.
Loading