Skip to content

Commit

Permalink
Merge pull request #167 from boostcampwm-2024/dev-fe
Browse files Browse the repository at this point in the history
[FE] 배포 ν…ŒμŠ€νŠΈ
  • Loading branch information
songbuild00 authored Nov 6, 2024
2 parents 90ed8c4 + a321df3 commit 37cd74f
Show file tree
Hide file tree
Showing 26 changed files with 1,295 additions and 121 deletions.
677 changes: 644 additions & 33 deletions FE/package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions FE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@mui/material": "^6.1.6",
"@tanstack/react-query": "^5.59.19",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0",
"socket.io-client": "^4.8.1",
"zustand": "^5.0.1"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/node": "^22.9.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
Expand Down
1 change: 0 additions & 1 deletion FE/public/vite.svg

This file was deleted.

42 changes: 0 additions & 42 deletions FE/src/App.css

This file was deleted.

44 changes: 14 additions & 30 deletions FE/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { MainPage } from './pages/MainPage';
import { GameSetupPage } from './pages/GameSetupPage';
import { GamePage } from './pages/GamePage';

function App() {
const [count, setCount] = useState(0)

return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
<Router>
<Routes>
<Route path="/" element={<MainPage />} />
<Route path="/game/setup" element={<GameSetupPage />} />
<Route path="/game/:id" element={<GamePage />} />
<Route path="*" element={<div>not found</div>} />
</Routes>
</Router>
);
}

export default App
export default App;
78 changes: 78 additions & 0 deletions FE/src/api/socket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { io, Socket } from 'socket.io-client';
import SocketEvents from '../constants/socketEvents';

// type SocketEvent = (typeof SocketEvents)[keyof typeof SocketEvents];

type ChatMessage = {
userId: string;
message: string;
};

type CreateRoomPayload = {
title: string;
maxPlayerCount: number;
gameMode: string;
isPublic: boolean;
};

// 이벀트의 데이터 νƒ€μž…μ„ μ •μ˜
// type SocketDataMap = {
// [SocketEvents.CHAT_MESSAGE]: ChatMessage;
// [SocketEvents.CREATE_ROOM]: CreateRoomPayload;
// // λ‹€λ₯Έ 이벀트의 데이터 νƒ€μž…μ„ μΆ”κ°€
// };

class SocketService {
private socket: Socket;
private url: string;

constructor(url: string) {
this.socket = io();
this.url = url;
}

connect() {
this.socket = io(this.url);
return new Promise((resolve, reject) => {
this.socket.on('connect', () => resolve(null));
this.socket.on('error', () => reject());
});
}

isActive() {
return this.socket && this.socket.active;
}

// 이벀트 μˆ˜μ‹  λ©”μ„œλ“œ
// on<T extends SocketEvent>(event: T, callback: (data: SocketDataMap[T]) => void) {
// this.socket.on(event, (data: SocketDataMap[T]) => {
// callback(data);
// });
// }

// λ©”μ‹œμ§€ 전솑 λ©”μ„œλ“œ
sendChatMessage(message: ChatMessage) {
this.socket.emit(SocketEvents.CHAT_MESSAGE, message);
}

// λ°© 생성 λ©”μ„œλ“œ
async createRoom(payload: CreateRoomPayload) {
await this.connect();
this.socket.emit(SocketEvents.CREATE_ROOM, payload);
}

// μ—°κ²° μ’…λ£Œ λ©”μ„œλ“œ
disconnect() {
this.socket.disconnect();
}

joinRoom(gameId: string, playerName: string) {
this.socket.send(SocketEvents.JOIN_ROOM, { gameId, playerName });
}

chatMessage(gameId: string, message: string) {
this.socket.send(SocketEvents.CHAT_MESSAGE, { gameId, message });
}
}

export const socketService = new SocketService('http://quizground.duckdns.org:3000/game');
1 change: 0 additions & 1 deletion FE/src/assets/react.svg

This file was deleted.

61 changes: 61 additions & 0 deletions FE/src/components/Chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { socketService } from '@/api/socket';
import { useEffect, useState } from 'react';

const sampleChat = Array(100)
.fill(null)
.map((_, i) => ({ name: 'user' + i, message: 'messagemessagemessagemessagemessagemessage' }));

const Chat = () => {
const [messages, setMessages] = useState<Array<{ name: string; message: string }>>([]);
const [inputValue, setInputValue] = useState('');

useEffect(() => {
setMessages(sampleChat); //TODO λ‚˜μ€‘μ— 고쳐야 함
// μ„œλ²„μ—μ„œ λ©”μ‹œμ§€λ₯Ό 받을 λ•Œ
// socket.on('chat message', (message) => {
// setMessages((prevMessages) => [...prevMessages, message]);
// });
// return () => {
// socket.off('chat message');
// };
}, []);

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (inputValue.trim()) {
socketService.chatMessage('1234', inputValue);
setInputValue('');
}
};
return (
<div className="component-default h-[100%]">
<div className="border-b border-default center h-[2.5rem]">λ©”μ‹œμ§€</div>
<div className="p-2 h-[calc(100%-6rem)] overflow-y-scroll">
{messages.map((e, i) => (
<div className="break-words leading-5 mt-3" key={i}>
<span className="font-bold mr-2">{e.name}</span>
<span>{e.message}</span>
</div>
))}
</div>
<div className="center border-t border-default h-[3.5rem] p-2">
<form onSubmit={handleSubmit} className="w-full h-full">
<input
className="bg-[#0001] w-[100%] h-[100%] rounded-m p-2"
type="text"
placeholder="λ©”μ‹œμ§€"
value={inputValue}
onChange={handleInputChange}
/>
</form>
</div>
</div>
);
};

export default Chat;
26 changes: 26 additions & 0 deletions FE/src/components/ClipboardCopy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Button } from '@mui/material';

type ClipboardCopyProps = {
valueToCopy: string;
message: string;
className?: string;
};

export const ClipboardCopy: React.FC<ClipboardCopyProps> = ({ valueToCopy, message }) => {
const handleCopyToClipboard = (): void => {
navigator.clipboard
.writeText(valueToCopy)
.then(() => {
alert('ν΄λ¦½λ³΄λ“œμ— λ³΅μ‚¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€: ');
})
.catch((err) => {
console.error('볡사 μ‹€νŒ¨:', err);
});
};

return (
<div>
<Button onClick={handleCopyToClipboard}>{message}</Button>
</div>
);
};
29 changes: 29 additions & 0 deletions FE/src/components/GameHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ClipboardCopy } from './ClipboardCopy';
import Card from '@mui/material/Card';
import { QuizPreview } from './QuizView';

export const GameHeader = () => {
// μž„μ‹œκ°’
const pinNum = '123456';
const linkURL = 'naver.com';
return (
<Card className="p-4 border border-blue-600 shadow-xl rounded-md h-[280px] w-[1000px] bg-gradient-to-b from-blue-500 to-blue-700 text-white">
<div className="flex justify-center mb-4">
<ClipboardCopy valueToCopy={pinNum} message={`PIN: ${pinNum} 볡사`} />
<ClipboardCopy valueToCopy={linkURL} message="곡유 링크 볡사" />
</div>
<div className="flex flex-col items-center justify-center text-center space-y-2">
<span className="text-xl font-semibold">ν€΄μ¦ˆμ΄λ¦„22</span>
</div>
<QuizPreview title="title" description="ν€΄μ¦ˆν€΄μ¦ˆν€΄γ…£μ¦ˆ" />
<div className="flex space-x-4 justify-center">
<button className="bg-yellow-400 text-black font-bold py-2 px-4 rounded-md shadow-lg transform hover:translate-y-[-2px] hover:shadow-xl active:translate-y-1 active:shadow-sm transition">
ν€΄μ¦ˆ μ„€μ •
</button>
<button className="bg-blue-500 text-white font-bold py-2 px-4 rounded-md shadow-lg transform hover:translate-y-[-2px] hover:shadow-xl active:translate-y-1 active:shadow-sm transition">
κ²Œμž„ μ‹œμž‘
</button>
</div>
</Card>
);
};
7 changes: 7 additions & 0 deletions FE/src/components/HeaderBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const HeaderBar = () => {
return (
<header className="h-[100px] text-l leading-[100px] pl-8 font-bold bg-main text-white">
QuizGround
</header>
);
};
62 changes: 62 additions & 0 deletions FE/src/components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState } from 'react';

type ModalProps = {
isOpen: boolean;
title: string;
placeholder?: string;
initialValue?: string;
onClose: () => void;
onSubmit: (value: string) => void;
};

export const Modal: React.FC<ModalProps> = ({
isOpen,
title,
placeholder,
initialValue = '',
onClose,
onSubmit
}) => {
const [inputValue, setInputValue] = useState(initialValue);

if (!isOpen) return null;

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
};

const handleSubmit = () => {
onSubmit(inputValue);
setInputValue(''); // μž…λ ₯κ°’ μ΄ˆκΈ°ν™”
};

return (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white rounded-lg shadow-lg p-6 max-w-sm w-full">
<h2 className="text-xl font-semibold mb-4">{title}</h2>
<input
type="text"
placeholder={placeholder}
value={inputValue}
onChange={handleInputChange}
className="border border-gray-300 rounded-md p-2 mb-4 w-full"
/>
<div className="flex justify-end">
<button
onClick={handleSubmit}
className="bg-blue-500 text-white rounded-md px-4 py-2 mr-2 hover:bg-blue-600"
>
등둝
</button>
{/* λ‹‰λ„€μž„ 등둝 λͺ¨λ‹¬μ—μ„œ μ·¨μ†Œλ²„νŠΌμ€ 없어야할것같은데 일단 λ„£μ–΄λ‘  */}
<button
onClick={onClose}
className="bg-gray-300 text-gray-700 rounded-md px-4 py-2 hover:bg-gray-400"
>
μ·¨μ†Œ
</button>
</div>
</div>
</div>
);
};
Loading

0 comments on commit 37cd74f

Please sign in to comment.