Skip to content

Commit

Permalink
Merge pull request #81 from Quad8/70-feat-마이페이지-=-기본-페이지-구현
Browse files Browse the repository at this point in the history
[Feat] 마이페이지 - 기본 페이지 구현
  • Loading branch information
bokeeeey authored Jun 17, 2024
2 parents 04cd4a4 + 90b8798 commit f2ccf17
Show file tree
Hide file tree
Showing 57 changed files with 900 additions and 226 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-error.log*

# local env files
.env*.local
.env*

# vercel
.vercel
Expand Down
4 changes: 4 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
const nextConfig = {
reactStrictMode: true,

env: {
NEXT_PUBLIC_KEYDEUK_API_BASE_URL: process.env.NEXT_PUBLIC_KEYDEUK_API_BASE_URL,
},

images: {
remotePatterns: [
{
Expand Down
23 changes: 22 additions & 1 deletion package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"react-colorful": "^5.6.1",
"react-dom": "^18",
"react-hook-form": "^7.51.4",
"react-toastify": "^10.0.5",
"sass": "^1.77.2",
"three": "^0.165.0",
"three-stdlib": "^2.30.3"
Expand Down
1 change: 1 addition & 0 deletions public/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as AlertIcon } from './svgs/alertIcon.svg';
export { default as ChevronIcon } from './svgs/chevron.svg';
export { default as EyeOffIcon } from './svgs/eyeOff.svg';
export { default as EyeOnIcon } from './svgs/eyeOn.svg';
Expand Down
3 changes: 3 additions & 0 deletions public/svgs/alertIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions src/api/securityAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getCookie } from '@/libs/manageCookie';

const BASE_URL = process.env.NEXT_PUBLIC_KEYDEUK_API_BASE_URL;

export const postRefreshToken = async (refreshToken: string) => {
const accessToken = await getCookie('accessToken');

try {
const res = await fetch(`${BASE_URL}/api/v1/reissue`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ refreshToken }),
});

const result = await res.json();

// console.log(result);

return result;
} catch (error) {
// console.error('짜잔 토큰 갱싱 실패~', error);

throw error;
}
};
79 changes: 79 additions & 0 deletions src/api/usersAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { getCookie } from '@/libs/manageCookie';
import { FieldValues } from 'react-hook-form';

const BASE_URL = process.env.NEXT_PUBLIC_KEYDEUK_API_BASE_URL;

/**
* 주어진 토큰을 사용하여 사용자 데이터를 호출
*
* @param {string} token - 인증 토큰입니다.
* @returns {Promise<Object>} - 사용자 데이터를 반환합니다.
* @throws {Error} - 요청이 실패한 경우 에러를 던집니다.
*/
export const getUserData = async () => {
const token = await getCookie('accessToken');

if (!token) {
return null;
}

try {
const res = await fetch(`${BASE_URL}/api/v1/users/me`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});

const data = await res.json();
return data;
} catch (error) {
throw error;
}
};

/**
* 주어진 payload로 사용자 프로필을 업데이트
*
* @param {FieldValues} payload - 사용자 프로필을 업데이트할 데이터입니다.
* @returns {Promise<Object>} - 응답 데이터를 반환합니다.
* @throws {Error} - 요청이 실패한 경우 에러를 던집니다.
*/
export const putEditProfile = async (payload: FieldValues) => {
const token = await getCookie('accessToken');

try {
const res = await fetch(`${BASE_URL}/api/v1/users/me`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(payload),
});

const result = await res.json();
return result;
} catch (error) {
throw error;
}
};

/**
* 주어진 닉네임이 사용 가능한지 확인
*
* @param {string} nickname - 확인할 닉네임입니다.
* @returns {Promise<Object>} - 닉네임이 사용 가능한지 여부를 나타내는 응답 데이터를 반환합니다.
* @throws {Error} - 요청이 실패한 경우 에러를 던집니다.
*/
export const checkNickname = async (nickname: string) => {
try {
const res = await fetch(`${BASE_URL}/api/v1/users/check/nickname?nickname=${nickname}`);

const result = await res.json();
return result;
} catch (error) {
throw error;
}
};
80 changes: 2 additions & 78 deletions src/app/(test)/bk/page.tsx
Original file line number Diff line number Diff line change
@@ -1,83 +1,7 @@
'use client';

import { InputField } from '@/components';
import Dropdown from '@/components/Dropdown/Dropdown';
import classNames from 'classnames/bind';
import { FormEvent } from 'react';

import Breadcrumb from '@/components/Breadcrumb/Breadcrumb';
import styles from './page.module.scss';

const cn = classNames.bind(styles);

const OPTIONS = ['인기순', '조회순', '최신순', '가격 낮은순', '가격 높은순', '직접 입력'];
import EditProfileModal from '@/app/my-info/_components/UserProfile/EditProfileModal/EditProfileModal';

export default function Page() {
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();

// const formData = new FormData(e.currentTarget);
// const payload = Object.fromEntries(formData.entries());
// console.log(payload);
};

// const handleClick = (e: MouseEvent<HTMLInputElement>) => {
// console.log('onClick', true, e.currentTarget.value);
// };
const handleClick = () => {};

return (
<form onSubmit={handleSubmit}>
<InputField
label='비밀번호'
id='이메일'
name='인풋'
type='password'
placeholder='이메일을 입력해 주세요'
suffixIcon='eye'
sizeVariant='md'
/>
<br />
<br />
<br />
<br />
<br />
<br />
<div style={{ width: '600px' }}>
<Dropdown
options={OPTIONS}
name='드롭다운'
sizeVariant='md'
placeholder='fdsa'
onClick={handleClick}
className={cn('dropdown')}
/>
</div>
<br />
<br />
<br />
<br />
<br />
<br />
{/* <TextField label='텍스트필드' id='텍스트' name='텍스트' placeholder='최소 20자 이상 입력해 주세요' /> */}
<br />
<br />
<br />
<br />
<br />
<br />
{/* <RadioField options={OPTIONS} label='숫자' value='6' /> */}
{/* <ItemOverview /> */}
<Breadcrumb />
<br />
<br />
<br />
<br />
<br />
<br />
<button style={{ border: '1px solid', width: '100%', height: '10vh', textAlign: 'center' }} type='submit'>
테스트
</button>
</form>
);
return <EditProfileModal />;
}
8 changes: 4 additions & 4 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Header } from '@/components';
import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query';
import type { Metadata } from 'next';
import { ReactNode } from 'react';

import { getUserData } from '@/api/usersAPI';
import { Header } from '@/components';
import { Providers } from './providers';

import '@/styles/reset.css';
Expand All @@ -18,9 +20,7 @@ export default async function RootLayout({
}>) {
const queryClient = new QueryClient();

// 유저 data api 호출
// const Token = cookies().get('accessToken')?.value ?? null;
// await queryClient.prefetchQuery({ queryKey: ['userData'], queryFn: () => getUserData(Token), });
await queryClient.prefetchQuery({ queryKey: ['userData'], queryFn: getUserData });

return (
<html lang='ko'>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// init
26 changes: 26 additions & 0 deletions src/app/my-info/_components/DeliveryStatus/DeliveryStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import classNames from 'classnames/bind';
import styles from './DeliveryStatus.module.scss';

const cn = classNames.bind(styles);

const DELIVERY_STATUS_LIST = [
{ label: '주문접수', count: 1 },
{ label: '결제완료', count: 0 },
{ label: '배송준비중', count: 0 },
{ label: '배송중', count: 0 },
{ label: '배송완료', count: 0 },
];

export default function DeliveryStatus() {
return (
<ul className={cn('status-list')}>
{DELIVERY_STATUS_LIST.map((status, i) => (
<li key={status.label} className={cn('status-item')}>
<span className={cn('status-count', { active: status.count > 0 })}>{status.count}</span>
<span className={cn('status-label', { active: status.count > 0 })}>{status.label}</span>
{i < DELIVERY_STATUS_LIST.length - 1 && <span className={cn('separator')}>&gt;</span>}
</li>
))}
</ul>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.empty-case {
flex-direction: column;
width: 100%;
gap: 2.4rem;
padding: 8rem 3.2rem;
border-radius: 0.8rem;
@include flex-center;

.empty-case-text {
color: $gray-40;
@include pretendard-20-600;
}

.empty-case-button {
width: 12.8rem;
height: 4rem;
@include pretendard-16-500;
}
}

.background-color {
background-color: $gray-10;
}
32 changes: 32 additions & 0 deletions src/app/my-info/_components/MyInfoEmptyCase/MyInfoEmptyCase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Button } from '@/components';
import { ROUTER } from '@/constants/route';
import { AlertIcon } from '@/public/index';
import classNames from 'classnames/bind';
import Link from 'next/link';
import styles from './MyInfoEmptyCase.module.scss';

const cn = classNames.bind(styles);

interface MyInfoEmptyCaseProps {
children: string;
isBackgroundColor?: boolean;
}

export default function MyInfoEmptyCase({ children, isBackgroundColor }: MyInfoEmptyCaseProps) {
return (
<div className={cn('empty-case', { 'background-color': isBackgroundColor })}>
<AlertIcon />
<p className={cn('empty-case-text')}>{children}</p>
<Button
as={Link}
href={ROUTER.SHOP.ALL}
backgroundColor='background-gray-40'
className={cn('empty-case-button')}
paddingVertical={8}
radius={4}
>
키득 둘러보기
</Button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.recent {
@include flex-column(4rem);

.recent-title {
color: $black;
@include pretendard-20-600;
}
}
Loading

0 comments on commit f2ccf17

Please sign in to comment.