diff --git a/FE/src/App.tsx b/FE/src/App.tsx index 6acad7b..f5dd228 100644 --- a/FE/src/App.tsx +++ b/FE/src/App.tsx @@ -4,6 +4,8 @@ import { GameSetupPage } from './pages/GameSetupPage'; import { GamePage } from './pages/GamePage'; import { QuizSetupPage } from './pages/QuizSetupPage'; import { GameLobbyPage } from './pages/GameLobbyPage'; +import { LoginPage } from './pages/LoginPage'; +import { MyPage } from './pages/MyPage'; function App() { return ( @@ -14,6 +16,8 @@ function App() { } /> } /> } /> + } /> + } /> not found} /> diff --git a/FE/src/components/Chat.tsx b/FE/src/components/Chat.tsx index 4218622..4863a29 100644 --- a/FE/src/components/Chat.tsx +++ b/FE/src/components/Chat.tsx @@ -18,28 +18,38 @@ const Chat = () => { const [isAtBottom, setIsAtBottom] = useState(true); const [newMessage, setNewMessage] = useState(false); const [prevMessageCount, setPrevMessageCount] = useState(messages.length); - const prevScrollTopRef = useRef(0); const scrollToBottom = () => { if (chatBottomRef.current) { - chatBottomRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - setNewMessage(false); + setTimeout(() => { + chatBottomRef.current?.scrollIntoView({ + behavior: 'instant', + block: 'end' + }); + setNewMessage(false); + }, 0); } }; const handleScroll = () => { const container = chatContainerRef.current; - if (container) { - // const isBottom = container.scrollHeight - container.scrollTop === container.clientHeight; - const isBottom = prevScrollTopRef.current < container.scrollTop && isAtBottom; - prevScrollTopRef.current = container.scrollTop; - setIsAtBottom(isBottom); // 맨 아래에 있으면 true, 아니면 false - if (isBottom) { - setNewMessage(false); - } + if (!container) return; + + const isBottom = + Math.abs(container.scrollHeight - container.scrollTop - container.clientHeight) < 10; + + if (isBottom) { + setIsAtBottom(true); + } else { + setIsAtBottom(false); } }; + const handleScrollToBottomClick = () => { + scrollToBottom(); + setIsAtBottom(true); + }; + const handleInputChange = (e: React.ChangeEvent) => { setInputValue(e.target.value); }; @@ -78,8 +88,8 @@ const Chat = () => { setPrevMessageCount(messages.length); } - if (isAtBottom && chatBottomRef.current) { - scrollToBottom(); + if (isAtBottom) { + requestAnimationFrame(() => scrollToBottom()); } }, [messages, isAtBottom, prevMessageCount]); @@ -127,7 +137,7 @@ const Chat = () => { {`${messages[messages.length - 1].playerName}: ${ diff --git a/FE/src/pages/LoginPage.tsx b/FE/src/pages/LoginPage.tsx new file mode 100644 index 0000000..df3f747 --- /dev/null +++ b/FE/src/pages/LoginPage.tsx @@ -0,0 +1,121 @@ +import { HeaderBar } from '@/components/HeaderBar'; +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +export const LoginPage = () => { + const [isSignUp, setIsSignUp] = useState(false); + const navigate = useNavigate(); + + const handleLogin = () => { + navigate('/mypage'); + }; + + return ( + + + + + + + + setIsSignUp(false)} + > + 로그인 + + setIsSignUp(true)} + > + 회원가입 + + + setIsSignUp(!isSignUp)} + > + + + + + + {isSignUp ? : } + + + + + ); +}; + +type LoginFormProps = { + handleLogin: () => void; +}; + +const LoginForm: React.FC = ({ handleLogin }) => ( + + 로그인 + + + + + + + + 로그인 + + + + 비밀번호를 잊으셨나요? + + + +); + +const SignUpForm = () => ( + + 회원가입 + + + + + + + + + + + 회원가입 + + +); diff --git a/FE/src/pages/MainPage.tsx b/FE/src/pages/MainPage.tsx index 970b934..f580f2e 100644 --- a/FE/src/pages/MainPage.tsx +++ b/FE/src/pages/MainPage.tsx @@ -23,7 +23,9 @@ export const MainPage = () => { navigate('/quiz/setup')}> 퀴즈 생성 - 로그인 + navigate('/login')}> + 로그인 + diff --git a/FE/src/pages/MyPage.tsx b/FE/src/pages/MyPage.tsx new file mode 100644 index 0000000..b76b5d8 --- /dev/null +++ b/FE/src/pages/MyPage.tsx @@ -0,0 +1,104 @@ +import { HeaderBar } from '@/components/HeaderBar'; +import { useState } from 'react'; + +type QuizListProps = { + title: string; + quizzes: string[]; + activeTab: string; +}; + +export const MyPage: React.FC = () => { + const [activeTab, setActiveTab] = useState<'myQuizzes' | 'solvedQuizzes'>('myQuizzes'); + + const myQuizzes = ['퀴즈 1', '퀴즈 2', '퀴즈 3', '퀴즈 4']; // 예시 데이터 + const solvedQuizzes = ['퀴즈 A', '퀴즈 B', '퀴즈 C', '퀴즈 D']; // 예시 데이터 + + return ( + + + + + + + + 닉네임 + + + + + setActiveTab('myQuizzes')} + > + 내가 만든 퀴즈 + + setActiveTab('solvedQuizzes')} + > + 내가 풀이한 퀴즈 + + + + {/* 리스트 */} + + {activeTab === 'myQuizzes' ? ( + + ) : ( + + )} + + + + ); +}; + +// 퀴즈 리스트 컴포넌트 +const QuizList: React.FC = ({ title, quizzes, activeTab }) => { + const handleEdit = (index: number) => { + console.log(index + '수정 클릭'); + }; + const handleDelete = (index: number) => { + console.log(index + '삭제 클릭'); + }; + return ( + + {title} + + {quizzes.map((quiz, index) => ( + + {quiz} + + {activeTab === 'myQuizzes' && ( + handleEdit(index)} + className="px-2 py-1 bg-blue-500 text-white rounded hover:bg-blue-600" + > + 수정 + + )} + handleDelete(index)} + className="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600" + > + 삭제 + + + + ))} + + + ); +}; diff --git a/FE/src/pages/QuizSetupPage.tsx b/FE/src/pages/QuizSetupPage.tsx index ee4bdd5..fe608ba 100644 --- a/FE/src/pages/QuizSetupPage.tsx +++ b/FE/src/pages/QuizSetupPage.tsx @@ -8,6 +8,7 @@ import { Typography, SelectChangeEvent } from '@mui/material'; +import { HeaderBar } from '@/components/HeaderBar'; /* { title: string, // 퀴즈셋의 제목 @@ -101,97 +102,100 @@ export const QuizSetupPage: React.FC = () => { }; return ( - - - 퀴즈셋 생성하기 - + <> + + + + 퀴즈셋 생성하기 + - + - - - 카테고리 설정 - - 역사 - 컴퓨터 - 과학 - + + + 카테고리 설정 + + 역사 + 컴퓨터 + 과학 + - {quizSet.map((quiz, quizIndex) => ( - - - 퀴즈 {quizIndex + 1} - - handleQuizChange(quizIndex, e.target.value)} - /> - handleLimitTimeChange(quizIndex, e.target.value)} - /> - {quiz.choices.map((choice, choiceIndex) => ( - - - handleChoiceChange(quizIndex, choiceIndex, 'content', e.target.value) - } - /> - - handleChoiceChange(quizIndex, choiceIndex, 'isAnswer', e.target.value) - } - className="w-28" - > - 정답아님 - 정답 - - - ))} - addChoice(quizIndex)} - className="w-full mb-4" - > - 선택지 추가 - - - ))} + {quizSet.map((quiz, quizIndex) => ( + + + 퀴즈 {quizIndex + 1} + + handleQuizChange(quizIndex, e.target.value)} + /> + handleLimitTimeChange(quizIndex, e.target.value)} + /> + {quiz.choices.map((choice, choiceIndex) => ( + + + handleChoiceChange(quizIndex, choiceIndex, 'content', e.target.value) + } + /> + + handleChoiceChange(quizIndex, choiceIndex, 'isAnswer', e.target.value) + } + className="w-28" + > + 정답아님 + 정답 + + + ))} + addChoice(quizIndex)} + className="w-full mb-4" + > + 선택지 추가 + + + ))} - - 퀴즈 추가 - - - 퀴즈 데이터 제출 - - + + 퀴즈 추가 + + + 퀴즈 데이터 제출 + + + > ); };
+ + 비밀번호를 잊으셨나요? + +