Skip to content

Commit

Permalink
[FE] - 에러 바운더리, 프로그레스바 추가 (#165)
Browse files Browse the repository at this point in the history
* feat: 에러 바운더리 설치 및 추가

* feat: 프로그래스바 UI 추가

* fix: 에러 바운더리에 잘못 입력한 값 삭제

* style: 퀴즈 세션 페이지 스타일 변경
  • Loading branch information
dooohun authored Dec 4, 2024
1 parent 331fe1a commit 59489ed
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 41 deletions.
24 changes: 24 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.
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"qrcode.react": "^4.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.1.2",
"react-router-dom": "^6.27.0",
"recharts": "^2.13.3",
"socket.io-client": "^4.8.1"
Expand Down
35 changes: 17 additions & 18 deletions packages/client/src/pages/quiz-session/index.lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export default function QuizSessionLazyPage() {
quizOrder: parseInt(id as string),
});

console.log(quiz.currentQuizData.timeLimit);
console.log(quiz.currentQuizData.position);

useEffect(() => {
const prevCurrentOrder = localStorage.getItem('currentOrder');
if (prevCurrentOrder !== null && parseInt(prevCurrentOrder) !== quiz.currentQuizData.position) {
Expand All @@ -45,21 +42,23 @@ export default function QuizSessionLazyPage() {
return (
<>
{!isQuizEnd && (
<div className="relative w-full">
<QuizHeader
startTime={quiz.startTime}
timeLimit={quiz.currentQuizData.timeLimit}
setQuizEnd={setIsQuizEnd}
totalParticipants={quiz.participantLength}
pinCode={pinCode as string}
/>
<QuizBox
quiz={quiz.currentQuizData}
startTime={quiz.startTime}
quizMaxNum={quiz.quizMaxNum}
initializeStates={initializeStates}
setInitializeStates={setInitializeStates}
/>
<div className="flex justify-center items-center h-screen w-screen">
<div className="relative w-full">
<QuizHeader
startTime={quiz.startTime}
timeLimit={quiz.currentQuizData.timeLimit}
setQuizEnd={setIsQuizEnd}
totalParticipants={quiz.participantLength}
pinCode={pinCode as string}
/>
<QuizBox
quiz={quiz.currentQuizData}
startTime={quiz.startTime}
quizMaxNum={quiz.quizMaxNum}
initializeStates={initializeStates}
setInitializeStates={setInitializeStates}
/>
</div>
</div>
)}
{isQuizEnd && (
Expand Down
42 changes: 28 additions & 14 deletions packages/client/src/pages/quiz-session/ui/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useEffect, useRef } from 'react';

interface ProgressBarProps {
/** 전체 시간 설정 */
time: number;
Expand All @@ -10,7 +12,7 @@ interface ProgressBarProps {
/** 애니메이션 종료 시 콜백 함수 */
handleAnimationEnd?: () => void;
/** 현재 진행 시간 */
currentTime?: number;
remainingTime: number;
}

const progressBarColors = {
Expand All @@ -22,27 +24,42 @@ const progressBarColors = {
};

const progressBarShapes = {
rounded: 'rounded-r-base',
rounded: 'rounded-base',
square: 'rounded-none',
};

const ProgressBar = ({
time = 5,
time,
type = 'success',
barShape = 'rounded',
pauseOnHover,
handleAnimationEnd,
currentTime = 0,
remainingTime,
}: ProgressBarProps) => {
const progressBarColor = progressBarColors[type];
const progressBarShape = progressBarShapes[barShape];
const progressBarRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const startTime = Date.now();
const endTime = startTime + remainingTime * 1000;

const updateWidth = () => {
const now = Date.now();
const remaining = Math.max(endTime - now, 0);
const progress = (remaining / (time * 1000)) * 100;

if (progressBarRef.current) {
progressBarRef.current.style.width = `${progress}%`;
}

if (remaining > 0) {
requestAnimationFrame(updateWidth);
}
};

// 현재 시간에 따른 진행률 계산
const progress = Math.min(Math.max((currentTime / time) * 100, 0), 100);
// 남은 시간 비율 계산
const remainingTimeRatio = 1 - currentTime / time;
// 애니메이션 지속 시간 계산 (초 단위)
const animationDuration = time * remainingTimeRatio;
updateWidth();
}, [remainingTime, time]);

return (
<section className="group" onAnimationEnd={handleAnimationEnd}>
Expand All @@ -51,10 +68,7 @@ const ProgressBar = ({
className={`h-[6px] ${progressBarColor} ${progressBarShape} ${
pauseOnHover && 'group-hover:[animation-play-state:paused]'
}`}
style={{
width: `${progress}%`,
animation: `progress ${animationDuration}s linear forwards`,
}}
ref={progressBarRef}
/>
</div>
</section>
Expand Down
16 changes: 8 additions & 8 deletions packages/client/src/pages/quiz-session/ui/QuizHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import HamsterImage from '@/shared/assets/characters/햄스터.png';
import { Calendar, Clock, Users } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';

// import ProgressBar from './ProgressBar';
import ProgressBar from './ProgressBar';

interface QuizHeaderProps {
startTime: number;
Expand Down Expand Up @@ -72,25 +72,25 @@ export default function QuizHeader({
<div className="relative z-10 p-6 max-w-4xl mx-auto pt-8 ">
<div className="flex-col justify-between items-center bg-white backdrop-blur-sm rounded-2xl shadow-lg pb-4 px-4">
<div className="flex justify-between">
<div className="flex flex-col p-2">
<div className="flex flex-col justify-center items-center p-2">
<img
src={characters[character]}
alt={`${characterNames[character]}character`}
className="w-16 h-16 rounded-full"
/>
<h3 className="font-lg text-gray-500">{nickname}</h3>
</div>
<div className="flex flex-col">
<div className="flex items-center space-x-2 pt-6">
<div className="flex flex-col gap-3 w-52">
<div className="flex items-center justify-end space-x-2 pt-6">
<Clock className="w-4 h-4 text-gray-500" />
<span className="text-gray-600">{remainingTime}초 남음</span>
</div>
{/* <ProgressBar
<ProgressBar
type="gradient"
time={timeLimit as 5}
currentTime={remainingTime}
time={timeLimit}
remainingTime={remainingTime}
pauseOnHover={false}
/> */}
/>
</div>
</div>
<div className="border-t-2 pt-2">
Expand Down
7 changes: 6 additions & 1 deletion packages/client/src/shared/boundary/AsyncBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Suspense } from 'react';
import QuizLoading from '@/pages/quiz-session/ui/QuizLoading';
import { ErrorBoundary } from 'react-error-boundary';

export default function AsyncBoundary({ children }: { children: React.ReactNode }) {
return <Suspense fallback={<QuizLoading />}>{children}</Suspense>;
return (
<ErrorBoundary fallback={<div>안녕하세요?</div>}>
<Suspense fallback={<QuizLoading />}>{children}</Suspense>
</ErrorBoundary>
);
}
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4553,6 +4553,7 @@ __metadata:
qrcode.react: "npm:^4.1.0"
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
react-error-boundary: "npm:^4.1.2"
react-router-dom: "npm:^6.27.0"
recharts: "npm:^2.13.3"
socket.io-client: "npm:^4.8.1"
Expand Down Expand Up @@ -9212,6 +9213,17 @@ __metadata:
languageName: node
linkType: hard

"react-error-boundary@npm:^4.1.2":
version: 4.1.2
resolution: "react-error-boundary@npm:4.1.2"
dependencies:
"@babel/runtime": "npm:^7.12.5"
peerDependencies:
react: ">=16.13.1"
checksum: 10c0/0737e5259bed40ce14eb0823b3c7b152171921f2179e604f48f3913490cdc594d6c22d43d7abb4ffb1512c832850228db07aa69d3b941db324953a5e393cb399
languageName: node
linkType: hard

"react-is@npm:^16.13.1":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
Expand Down

0 comments on commit 59489ed

Please sign in to comment.