Skip to content

Commit

Permalink
Merge pull request #61 from softeerbootcamp4th/TASK-185
Browse files Browse the repository at this point in the history
[Feature][Task-185] Global Error Boundary 추가
  • Loading branch information
nim-od authored Aug 16, 2024
2 parents ee6e4fe + 65a6b5b commit ed797dc
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 30 deletions.
7 changes: 5 additions & 2 deletions packages/user/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RouterProvider } from 'react-router-dom';
import ToasterStack from 'src/components/common/toast/ToasterStack.tsx';
import GlobalErrorBoundary from 'src/components/layout/ErrorBoundary.tsx';
import AppProviders from 'src/libs/index.tsx';
import router from 'src/routes/router.tsx';
import CasperCursor from './components/cursor/CasperCursor.tsx';
Expand All @@ -10,8 +11,10 @@ export default function App() {
return (
<>
<AppProviders>
{/* TODO: create global loading component */}
<RouterProvider router={router} fallbackElement={<>loading...</>} />
<GlobalErrorBoundary>
{/* TODO: create global loading component */}
<RouterProvider router={router} fallbackElement={<>loading...</>} />
</GlobalErrorBoundary>
<ToasterStack />
</AppProviders>
<CasperCursor />
Expand Down
4 changes: 2 additions & 2 deletions packages/user/src/components/common/toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport
ref={ref}
className={cn(
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-10 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[500px] gap-4',
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-4 p-10 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[500px]',
className,
)}
{...props}
Expand Down Expand Up @@ -86,5 +86,5 @@ export {
ToastProvider,
ToastViewport,
type ToastActionElement,
type ToastProps
type ToastProps,
};
28 changes: 15 additions & 13 deletions packages/user/src/components/event/chatting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@ import ChatInputArea from './inputArea/index.tsx';
/** 실시간 기대평 섹션 */

function RealTimeChatting({ onSendMessage, messages }: UseChatSocketReturnType) {
return <section className="container flex max-w-[1200px] snap-start flex-col items-center pb-[115px] pt-[50px]">
<h6 className="text-heading-10 mb-[25px] font-medium">기대평을 남겨보세요!</h6>
<ChatInputArea>
<ChatInput onSend={onSendMessage} />
</ChatInputArea>
<div className="h-[1000px] w-full overflow-y-auto rounded-[10px] bg-neutral-800 py-10">
<ChatList>
{messages.map((message) => (
<Chat key={message.id} {...message} />
))}
</ChatList>
</div>
</section>;
return (
<section className="container flex max-w-[1200px] snap-start flex-col items-center pb-[115px] pt-[50px]">
<h6 className="text-heading-10 mb-[25px] font-medium">기대평을 남겨보세요!</h6>
<ChatInputArea>
<ChatInput onSend={onSendMessage} />
</ChatInputArea>
<div className="h-[1000px] w-full overflow-y-auto rounded-[10px] bg-neutral-800 py-10">
<ChatList>
{messages.map((message) => (
<Chat key={message.id} {...message} />
))}
</ChatList>
</div>
</section>
);
}

export default RealTimeChatting;
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ interface ChargeButtonContentProps extends ChargeButtonData {
}

export default function ChargeButtonContent({
rank, vote, type, percentage }: ChargeButtonContentProps) {
rank,
vote,
type,
percentage,
}: ChargeButtonContentProps) {
const { shortTitle, title } = TEAM_DESCRIPTIONS[type];
const displayTitle = shortTitle ?? title;
const displayVoteStats = useMemo(() => `${percentage.toFixed(1)}% (${formatVoteCount(vote)})`, [percentage, vote]);
const displayVoteStats = useMemo(
() => `${percentage.toFixed(1)}% (${formatVoteCount(vote)})`,
[percentage, vote],
);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function ChargeButtonWrapper({
<GradientBorderWrapper className={styles.borderWrapper}>
<div className={styles.innerborderWrapper}>{children}</div>
</GradientBorderWrapper>
<img src={imageUrl} alt={`${type} 팀 캐스퍼 실물`} className={styles.image} />=
<img src={imageUrl} alt={`${type} 팀 캐스퍼 실물`} className={styles.image} />
</button>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ export interface ChargeButtonData {
}

export default function ControlButton({
onCharge, onFullyCharged, type, data }: ControlButtonProps) {
onCharge,
onFullyCharged,
type,
data,
}: ControlButtonProps) {
const { rank, percentage } = data;
const { progress, clickCount, handleClick } = useGaugeProgress({
percentage,
Expand Down Expand Up @@ -60,10 +64,13 @@ function useGaugeProgress({
const [progress, setProgress] = useState(percentage);
const [clickCount, setClickCount] = useState(0);

const updateProgress = useCallback((count: number) => {
const newProgress = calculateProgress(count);
setProgress(newProgress);
}, [progress]);
const updateProgress = useCallback(
(count: number) => {
const newProgress = calculateProgress(count);
setProgress(newProgress);
},
[progress],
);

const resetProgress = useCallback(() => {
setClickCount(0);
Expand Down
6 changes: 4 additions & 2 deletions packages/user/src/components/event/racing/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ interface RacingDashboardProps extends Pick<UseRacingSocketReturnType, 'ranks'>
}

export default function RacingDashboard({ ranks, chargedCar }: RacingDashboardProps) {
return <div className="relative h-[685px] w-full">
return (
<div className="relative h-[685px] w-full">
<HeaderSection />
<RacingCardSection />
{CATEGORIES.map((type) => (
Expand All @@ -25,7 +26,8 @@ export default function RacingDashboard({ ranks, chargedCar }: RacingDashboardPr
/>
))}
<Background />
</div>;
</div>
);
}

function HeaderSection() {
Expand Down
40 changes: 40 additions & 0 deletions packages/user/src/components/layout/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { PropsWithChildren, useMemo } from 'react';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
import ErrorContainer from 'src/components/layout/ErrorContainer.tsx';

export default function GlobalErrorBoundary({ children }: PropsWithChildren) {
return (
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={window.location.reload}>
{children}
</ErrorBoundary>
);
}

function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
const errorMessage = useMemo(() => {
switch (error.status) {
case 400:
return '잘못된 요청입니다. 입력된 정보를 확인해주세요.';
case 401:
return '로그인이 필요합니다. 다시 로그인해주세요.';
case 403:
return '접근 권한이 없습니다. 권한을 확인해주세요.';
case 404:
return '요청한 페이지를 찾을 수 없습니다.';
case 408:
return '요청 시간이 초과되었습니다. 다시 시도해주세요.';
case 500:
return '서버에 오류가 발생했습니다. 잠시 후 다시 시도해주세요.';
case 502:
return '잘못된 게이트웨이입니다. 서비스가 불안정할 수 있습니다.';
case 503:
return '서비스가 현재 이용 불가능합니다. 잠시 후 다시 시도해주세요.';
case 504:
return '게이트웨이 시간 초과입니다. 다시 시도해주세요.';
default:
return '무언가 문제가 발생했어요!';
}
}, [error]);

return <ErrorContainer errorMessage={errorMessage} reset={resetErrorBoundary} />;
}
21 changes: 21 additions & 0 deletions packages/user/src/components/layout/ErrorContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Button from 'src/components/common/Button.tsx';

interface ErrorFallbackProps {
errorMessage: string;
reset: () => void;
}

export default function ErrorContainer({ errorMessage, reset }: ErrorFallbackProps) {
return (
<div
role="alert"
className="w-screen flex h-screen flex-col items-center gap-15 justify-center"
>
<img src="/images/fcfs/result/wrong.png" alt="오류 발생 이미지" />
<div className="flex flex-col items-center gap-5">
<h4>{errorMessage}</h4>
<Button onClick={reset}>새로 고침 하기</Button>
</div>
</div>
);
}
7 changes: 6 additions & 1 deletion packages/user/src/components/layout/header/user/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import UserIcon from 'src/assets/icons/user.svg?react';
import withAuth from 'src/components/shared/withAuthHOC.tsx';
import useAuth from 'src/hooks/useAuth.tsx';
import SpeechBubble from './SpeechBubble.tsx';

const UserGreeting = withAuth(() => <p className="text-detail-1">김보민님 반갑습니다</p>);
const UserGreeting = withAuth(({ name }: { name: string }) => (
<p className="text-detail-1">{name}님 반갑습니다</p>
));

// TODO: 로그아웃
export default function User() {
const { user } = useAuth();
return (
<UserGreeting
name={user?.name ?? '캐스퍼'}
unauthenticatedDisplay={
<div className="flex items-center gap-2">
<SpeechBubble />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import useAuth from 'src/hooks/useAuth.tsx';
export default function ResultStep() {
const { user } = useAuth();

const type = useMemo(() => (user?.type as Category), [user]);
const type = useMemo(() => user?.type as Category, [user]);

const { title, shortTitle, details } = TEAM_DESCRIPTIONS[type];
const displayTitle = shortTitle ?? title;
Expand Down
6 changes: 5 additions & 1 deletion packages/user/src/hooks/socket/useRacingSocket.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { categoryToSocketCategory, RACING_SOCKET_ENDPOINTS, socketCategoryToCategory } from '@softeer/common/constants';
import {
categoryToSocketCategory,
RACING_SOCKET_ENDPOINTS,
socketCategoryToCategory,
} from '@softeer/common/constants';
import { Category } from '@softeer/common/types';
import type { SocketSubscribeCallbackType } from '@softeer/common/utils';
import { useCallback, useEffect, useMemo, useState } from 'react';
Expand Down
11 changes: 11 additions & 0 deletions packages/user/src/pages/ErrorPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useNavigate } from 'react-router-dom';
import ErrorContainer from 'src/components/layout/ErrorContainer.tsx';
import RoutePaths from 'src/constants/routePath.ts';

export default function ErrorPage() {
const navigate = useNavigate();

return (
<ErrorContainer errorMessage="문제가 발생했어요!" reset={() => navigate(RoutePaths.Home)} />
);
}
2 changes: 2 additions & 0 deletions packages/user/src/routes/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createBrowserRouter, RouteObject } from 'react-router-dom';
import Layout from 'src/components/layout/index.tsx';
import RoutePaths from 'src/constants/routePath.ts';
import AuthProvider from 'src/context/auth/index.tsx';
import ErrorPage from 'src/pages/ErrorPage.tsx';
import EventPage from 'src/pages/EventPage.tsx';
import HomePage from 'src/pages/HomePage.tsx';

Expand All @@ -13,6 +14,7 @@ const routes: RouteObject[] = [
<Layout />
</AuthProvider>
),
errorElement: <ErrorPage />,
children: [
{
index: true,
Expand Down

0 comments on commit ed797dc

Please sign in to comment.