Skip to content

Commit 59e7870

Browse files
authored
Merge pull request #256 from boostcampwm-2024/feat/#250-guest-and-select-count
게스트 기능 및 좌석 개수 변경, 이탈 방지 기능 추가
2 parents 85ae82c + 238033d commit 59e7870

File tree

18 files changed

+672
-28
lines changed

18 files changed

+672
-28
lines changed

front/package-lock.json

Lines changed: 356 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

front/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"postcss": "^8.4.47",
4444
"prettier": "^3.3.3",
4545
"prettier-plugin-tailwindcss": "^0.6.8",
46+
"react-select": "^5.8.3",
4647
"tailwindcss": "^3.4.14",
4748
"typescript": "~5.6.2",
4849
"typescript-eslint": "^8.11.0",

front/src/App.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { RouterProvider } from 'react-router-dom';
22

3+
import ConfirmContainer from '@/components/Confirm/ConfirmContainer.tsx';
34
import ToastContainer from '@/components/Toast/ToastContainer.tsx';
45

56
import AuthProvider from '@/providers/AuthProvider';
7+
import ConfirmProvider from '@/providers/ConfirmProvider.tsx';
68
import QueryProvider from '@/providers/QueryProvider';
79
import router from '@/routes/index';
810
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
@@ -11,8 +13,11 @@ function App() {
1113
return (
1214
<QueryProvider>
1315
<AuthProvider>
14-
<RouterProvider router={router} />
15-
<ToastContainer />
16+
<ConfirmProvider>
17+
<RouterProvider router={router} />
18+
<ToastContainer />
19+
<ConfirmContainer />
20+
</ConfirmProvider>
1621
<ReactQueryDevtools />
1722
</AuthProvider>
1823
</QueryProvider>

front/src/api/user.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export const postSignup = (data: UserData) => apiClient.post(API.USER.SIGNUP, da
66
export const postLogin = (data: UserData) => apiClient.post(API.USER.LOGIN, data);
77
export const postLogout = () => apiClient.post(API.USER.LOGOUT);
88
export const getUser = () => apiClient.get(API.USER.INFORMATION).then((res) => res.data);
9+
export const getGuestLogin = () => apiClient.get(API.USER.LOGIN_AS_GUEST).then((res) => res.data);
10+
911
export type UserData = {
1012
loginId: string;
1113
loginPassword: string;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import useConfirm from '@/hooks/useConfirm.tsx';
2+
3+
import Button from '@/components/common/Button.tsx';
4+
5+
export default function ConfirmContainer() {
6+
const { confirmValue } = useConfirm();
7+
if (confirmValue == null) {
8+
return null;
9+
}
10+
11+
return (
12+
<div className="fixed left-0 top-0 z-10 flex h-full w-full items-center justify-center bg-surface/30">
13+
<div className="z-[999] flex min-w-[240px] max-w-[480px] flex-col gap-4 rounded-xl border bg-surface-card px-6 py-3">
14+
<div>
15+
<h1 className="text-heading2">{confirmValue.title}</h1>
16+
<span className="whitespace-pre-line text-display1">{`${confirmValue.description}`}</span>
17+
</div>
18+
<div className="ml-auto flex gap-4">
19+
{confirmValue.cancel && (
20+
<Button size={'middle'} intent={'outline'} onClick={confirmValue.cancel.handleClick}>
21+
<span className="text-label1 text-typo">{`${confirmValue.cancel.text}`}</span>
22+
</Button>
23+
)}
24+
<Button size={'middle'} color={confirmValue.ok.color} onClick={confirmValue.ok.handleClick}>
25+
<span className="text-label1 text-typo-display">{`${confirmValue.ok.text}`}</span>
26+
</Button>
27+
</div>
28+
</div>
29+
</div>
30+
);
31+
}

front/src/components/Navbar/index.tsx

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { Link, useNavigate } from 'react-router-dom';
22

33
import { CustomError } from '@/api/axios.ts';
44
import { deleteReservation, getReservation } from '@/api/reservation.ts';
5-
import { postLogout } from '@/api/user.ts';
5+
import { getGuestLogin, postLogout } from '@/api/user.ts';
66

77
import { useAuthContext } from '@/hooks/useAuthContext.tsx';
8+
import useConfirm from '@/hooks/useConfirm.tsx';
89

910
import ReservationCard from '@/components/Navbar/ReservationCard.tsx';
1011
import { toast } from '@/components/Toast/index.ts';
@@ -14,15 +15,25 @@ import Popover from '@/components/common/Popover';
1415
import Separator from '@/components/common/Separator.tsx';
1516

1617
import type { Reservation } from '@/type/reservation.ts';
17-
import { useMutation, useMutationState, useQuery, useQueryClient } from '@tanstack/react-query';
18+
import type { Guest } from '@/type/user.ts';
19+
import {
20+
useIsFetching,
21+
useMutation,
22+
useMutationState,
23+
useQuery,
24+
useQueryClient,
25+
} from '@tanstack/react-query';
1826
import { cx } from 'class-variance-authority';
1927

2028
const POPOVER_WIDTH = 460;
2129

2230
const RESERVATION_DELETE_MUTATION_KEY = ['reservation'];
31+
const GUEST_LOGIN_QUERY_KEY = ['guest'];
2332

2433
export default function Navbar() {
25-
const { isLogin, userId, logout } = useAuthContext();
34+
const { isLogin, userId, logout, login } = useAuthContext();
35+
const isGuestLoginPending = !!useIsFetching({ queryKey: GUEST_LOGIN_QUERY_KEY });
36+
const { confirm } = useConfirm();
2637
const navigate = useNavigate();
2738
const queryClient = useQueryClient();
2839
const { data: reservations } = useQuery<Reservation[], CustomError>({
@@ -31,6 +42,7 @@ export default function Navbar() {
3142
enabled: isLogin,
3243
staleTime: Infinity,
3344
});
45+
const sliced = userId?.slice(0, 12);
3446

3547
const { mutate: requestDeleteReservation } = useMutation({
3648
mutationKey: RESERVATION_DELETE_MUTATION_KEY,
@@ -44,6 +56,40 @@ export default function Navbar() {
4456
},
4557
});
4658

59+
const loginAsGuest = async () => {
60+
const isConfirm = await confirm({
61+
title: '게스트로 입장하기',
62+
description: '게스트 계정은 로그아웃하시면 다시 사용 할 수 없습니다.\n 그래도 입장하시겠습니까?',
63+
buttons: {
64+
ok: {
65+
title: '확인',
66+
color: 'success',
67+
},
68+
cancel: {
69+
title: '취소',
70+
},
71+
},
72+
});
73+
if (isConfirm) {
74+
await queryClient
75+
.fetchQuery<Guest>({
76+
queryKey: GUEST_LOGIN_QUERY_KEY,
77+
queryFn: getGuestLogin,
78+
})
79+
.then((data) => {
80+
const sliced = data.loginId.slice(0, 12);
81+
if (login) {
82+
login(sliced);
83+
toast.success('geust로 로그인 되었습니다');
84+
}
85+
})
86+
.catch(() => {
87+
toast.error('로그인에 실패했습니다\n잠시 후 다시 시도해주세요.');
88+
});
89+
return;
90+
}
91+
};
92+
4793
const deletingReservationIdList = useMutationState({
4894
filters: { mutationKey: RESERVATION_DELETE_MUTATION_KEY, status: 'pending' },
4995
select: (mutation) => mutation.state.variables,
@@ -78,7 +124,7 @@ export default function Navbar() {
78124
render={(togglePopover, triggerRef) => (
79125
<Button size="middle" intent={'ghost'} onClick={togglePopover} ref={triggerRef}>
80126
<Icon iconName="User" />
81-
<span className="text-label2 text-typo">{userId}</span>
127+
<span className="text-label2 text-typo">{`${sliced} 님`}</span>
82128
<Icon iconName="DownArrow" />
83129
</Button>
84130
)}
@@ -119,6 +165,21 @@ export default function Navbar() {
119165
</Popover.Root>
120166
) : (
121167
<nav className="flex gap-4">
168+
<Button
169+
size={'middle'}
170+
color={'primary'}
171+
intent={'outline'}
172+
onClick={loginAsGuest}
173+
disabled={isGuestLoginPending}>
174+
{isGuestLoginPending ? (
175+
<>
176+
<Icon iconName="Loading" className="animate-spin" />
177+
<span className="f text-label2 text-typo-disable">로그인중..</span>
178+
</>
179+
) : (
180+
<span className="text-label2 text-primary">게스트로 입장하기</span>
181+
)}
182+
</Button>
122183
<Button intent={'outline'} color={'primary'} size={'middle'} asChild>
123184
<Link to={'/signUp'}>
124185
<span className="text-label2 text-primary">회원가입</span>

front/src/components/common/Popover.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ const Content = ({ children }: IContent) => {
102102
}, [triggerRef]);
103103

104104
useEffect(() => {
105+
updatePosition();
105106
if (isOpen) {
106107
window.addEventListener('scroll', updatePosition);
107108
}

front/src/components/loaders/WithLogin.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { PropsWithChildren } from 'react';
2+
import { useEffect } from 'react';
23
import { Navigate } from 'react-router-dom';
34

45
import { useAuthContext } from '@/hooks/useAuthContext.tsx';
@@ -10,8 +11,11 @@ import { ROUTE_URL } from '@/constants/index.ts';
1011
//TODO toast 알림 추가 필ㄹ요
1112
export default function WithLogin({ children }: PropsWithChildren) {
1213
const { isLogin } = useAuthContext();
14+
useEffect(() => {
15+
if (!isLogin) toast.warning('로그인이 필요한 서비스입니다.\n로그인 후 이용해주세요.');
16+
}, [isLogin]);
17+
1318
if (!isLogin) {
14-
toast.warning('로그인이 필요한 서비스입니다.\n로그인 후 이용해주세요.');
1519
return <Navigate to={ROUTE_URL.USER.LOGIN} />;
1620
}
1721
return <>{children}</>;

front/src/components/loaders/WithoutLogin.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { PropsWithChildren } from 'react';
2+
import { useEffect } from 'react';
23
import { Navigate } from 'react-router-dom';
34

45
import { useAuthContext } from '@/hooks/useAuthContext.tsx';
@@ -9,8 +10,10 @@ import { ROUTE_URL } from '@/constants/index.ts';
910

1011
export default function WithoutLogin({ children }: PropsWithChildren) {
1112
const { isLogin } = useAuthContext();
13+
useEffect(() => {
14+
if (isLogin) toast.warning('로그인 후 접근할 수 없습니다.\n로그 아웃 후 이용해주세요.');
15+
}, [isLogin]);
1216
if (isLogin) {
13-
toast.warning('로그인 후 접근할 수 없습니다.\n로그 아웃 후 이용해주세요.');
1417
return <Navigate to={ROUTE_URL.PROGRAM.DEFAULT} />;
1518
}
1619
return <>{children}</>;

front/src/constants/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const API = {
1616
CHECK_ID: '/user/checkid',
1717
LOGOUT: '/user/logout',
1818
INFORMATION: '/user',
19+
LOGIN_AS_GUEST: '/user/guest',
1920
},
2021
EVENT: {
2122
GET_EVENT_DETAIL_MOCK: (id: number) => `/mock/events/${id}`,

0 commit comments

Comments
 (0)