Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Collection50 committed Aug 22, 2024
2 parents b7b3d65 + 7aed0ed commit c940fc4
Show file tree
Hide file tree
Showing 82 changed files with 1,875 additions and 64 deletions.
80 changes: 80 additions & 0 deletions .pnp.cjs

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

Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"node": "20.2.0"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-popover": "^1.1.1",
Expand Down
54 changes: 44 additions & 10 deletions src/apis/http.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { serverErrorType } from '@/types/api';
import { isProductionEnv } from '@/utils/common';
import type {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
InternalAxiosRequestConfig,
Method,
} from 'axios';
import axios from 'axios';
import { getCookie } from 'cookies-next';
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig, Method } from 'axios';
import { deleteCookie, getCookie, setCookie } from 'cookies-next';
import { postRefresh } from './refresh';

const axiosInstance: AxiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
Expand All @@ -27,17 +36,42 @@ axiosInstance.interceptors.request.use(async (requestConfig: InternalAxiosReques
return config;
});

axiosInstance.interceptors.response.use(async (response: AxiosResponse) => {
if (!isProductionEnv) {
// eslint-disable-next-line no-console
console.log(response);
}
return response;
});
axiosInstance.interceptors.response.use(
(response: AxiosResponse) => {
if (!isProductionEnv) {
// eslint-disable-next-line no-console
console.log(response);
}

return response;
},

async (error: AxiosError) => {
if (axios.isAxiosError(error)) {
const data = error.response?.data as serverErrorType;
const refreshToken = getCookie('refreshToken');
if (refreshToken) {
if (data?.status === 'UNAUTHORIZED') {
deleteCookie('accessToken');
deleteCookie('refreshToken');
try {
const response = await postRefresh({ refreshToken });
setCookie('accessToken', response.data.accessToken);
setCookie('refreshToken', response.data.refreshToken);
} catch (refreshError) {
window.location.href = '/login';
}
}
}
}

return Promise.reject(error);
},
);

const createApiMethod =
(instance: AxiosInstance, method: Method) =>
<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> =>
<T>(config: AxiosRequestConfig): Promise<AxiosResponse> =>
instance({ ...config, method });

export const http = {
Expand Down
31 changes: 31 additions & 0 deletions src/apis/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { PostLoginRequest, PostLoginResponse } from '@/types/login';
import { useMutation } from '@tanstack/react-query';
import { AxiosResponse } from 'axios';
import { setCookie } from 'cookies-next';
import { http } from './http';

export const postLogin = async ({ loginId, password }: PostLoginRequest): Promise<PostLoginResponse> => {
const response: AxiosResponse<PostLoginResponse> = await http.post<PostLoginResponse>({
url: '/users/test/login',
data: {
loginId,
password,
},
});

return response.data;
};

export const loginMutation = () =>
useMutation({
mutationFn: postLogin,
onSuccess: (data) => {
setCookie('accessToken', data.accessToken);
setCookie('refreshToken', data.refreshToken);
window.location.href = '/';
alert('로그인 성공');
},
onError: () => {
alert('로그인 실패');
},
});
19 changes: 19 additions & 0 deletions src/apis/logout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useMutation } from '@tanstack/react-query';
import { deleteCookie } from 'cookies-next';
import { http } from './http';

export const getLogout = async () => {
return await http.get({
url: '/users/logout',
});
};

export const logoutMutation = () =>
useMutation({
mutationFn: getLogout,
onSuccess: () => {
deleteCookie('accessToken');
deleteCookie('refreshToken');
window.location.href = '/';
},
});
11 changes: 11 additions & 0 deletions src/apis/refresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { PostLoginResponse, PostRefreshRequest } from '@/types/login';
import { http } from './http';

export const postRefresh = ({ refreshToken }: PostRefreshRequest) => {
return http.post<PostLoginResponse>({
url: '/users/refreshToken',
data: {
refreshToken,
},
});
};
10 changes: 6 additions & 4 deletions src/app/(sidebar)/(my-info)/components/InfoCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState } from 'react';
import { mockInfoCount, mockInfoList } from '../mock';
import { cn } from '@/utils/tailwind-util';
import { Icon } from '@/system/components';
import { InfoCardItem } from './InfoCardItem';
import { InfoCard } from '@/components/InfoCard';

const INFO_OPTIONS = ['경험 정리', '자기소개서', '면접 질문'] as const;

Expand Down Expand Up @@ -46,11 +46,13 @@ export function InfoCardList() {
<Icon name="add" color="#08F29B" />
</button>
</div>
<div className="grid grid-cols-[repeat(auto-fill,minmax(343px,1fr))] gap-[16px]">
<ul className="grid grid-cols-[repeat(auto-fill,minmax(343px,1fr))] gap-[16px]">
{infoList.map((info) => (
<InfoCardItem key={info.id} {...info} />
<li className="min-w-[343px]">
<InfoCard key={info.id} {...info} />
</li>
))}
</div>
</ul>
</section>
);
}
4 changes: 2 additions & 2 deletions src/app/(sidebar)/(my-info)/mock.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { InfoCard } from '@/types/info';
import { InfoCardType } from '@/types/info';

export const mockInfoCount = {
'경험 정리': 1,
자기소개서: 3,
'면접 질문': 2,
};

export const mockInfoList: InfoCard[] = [
export const mockInfoList: InfoCardType[] = [
{
id: 1,
title: '제목',
Expand Down
2 changes: 2 additions & 0 deletions src/app/(sidebar)/(my-info)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

import { Icon } from '@/system/components';
import { InfoCardList } from './components/InfoCardList';

Expand Down
80 changes: 80 additions & 0 deletions src/app/(sidebar)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use client';

import { loginMutation } from '@/apis/login';
import { logoutMutation } from '@/apis/logout';
import { SSRSafeSuspense } from '@/lib';
import { Button } from '@/system/components';
import { getCookie } from 'cookies-next';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { InputField } from '../my-recruit/components/NewRecruitDialogContent/InputField';

export default function Page() {
const router = useRouter();

const [loginId, setLoginId] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [isLogin, setIsLogin] = useState<boolean>(false);

const { mutate: loginMutate } = loginMutation();
const { mutate: logoutMutate } = logoutMutation();

useEffect(() => {
const token = getCookie('accessToken');
setIsLogin(token != null);
}, []);

const handleLogin = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
loginMutate({ loginId, password });
};

return (
<SSRSafeSuspense
fallback={
<div className="w-full h-full flex justify-center items-center">
<div>로딩 중...</div>
</div>
}>
<div className="w-full h-full flex justify-center items-center">
{isLogin ? (
<div className="flex flex-col gap-4">
<div>이미 로그인 되어있습니다.</div>
<Button
className="bg-neutral-95 flex items-center gap-[4px] py-[8px] px-[16px] rounded-[6px]"
onClick={() => {
router.replace('/');
}}>
<span className="w-full text-label1 text-white font-semibold">홈으로</span>
</Button>
<Button
className="bg-neutral-95 flex items-center gap-[4px] py-[8px] px-[16px] rounded-[6px]"
onClick={() => {
logoutMutate();
}}>
<span className="w-full text-label1 text-white font-semibold">로그아웃</span>
</Button>
</div>
) : (
<form onSubmit={handleLogin}>
<div className="flex flex-col justify-center gap-10 mb-15">
<InputField value={loginId} onChange={(e) => setLoginId(e.target.value)} placeholder="이메일" />
<InputField
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="비밀번호"
className="mb-4"
/>
</div>
<div className="flex gap-2 flex-col">
<Button className="bg-neutral-95 flex items-center gap-[4px] py-[8px] px-[16px] rounded-[6px]">
<span className="w-full text-label1 text-white font-semibold">로그인</span>
</Button>
</div>
</form>
)}
</div>
</SSRSafeSuspense>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { If } from '@/components/If';
import { If } from '@/system/utils/If';
import { ComponentProps, ReactNode } from 'react';

interface Props extends ComponentProps<'input'> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<<<<<<< HEAD
import { Spacing } from '@/components/Spacing';
=======
import { Spacing } from '@/system/utils/Spacing';
>>>>>>> main
import { TouchButton } from '@/components/TouchButton';
import { Dialog } from '@/system/components/Dialog/ShadcnDialog';
import { color } from '@/system/token/color';
Expand All @@ -17,7 +21,7 @@ import { motion } from 'framer-motion';
import { Popover, PopoverContent, PopoverTrigger } from '@/system/components/Popover/Popover';
import { Calendar } from '@/system/components/Calendar/Calendar';
import { format } from 'date-fns/format';
import { If } from '@/components/If';
import { If } from '@/system/utils/If';

interface Props {
onSubmit: () => void;
Expand Down
Loading

0 comments on commit c940fc4

Please sign in to comment.