Skip to content

Commit

Permalink
Merge pull request #250 from boostcampwm-2024/dev-fe
Browse files Browse the repository at this point in the history
Dev fe
  • Loading branch information
ijun17 authored Nov 21, 2024
2 parents ae9482d + d45b1dc commit 968cfa7
Show file tree
Hide file tree
Showing 23 changed files with 749 additions and 265 deletions.
12 changes: 6 additions & 6 deletions FE/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions FE/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { QuizSetupPage } from './pages/QuizSetupPage';
import { GameLobbyPage } from './pages/GameLobbyPage';
import { LoginPage } from './pages/LoginPage';
import { MyPage } from './pages/MyPage';
import { PinPage } from './pages/PinPage';

function App() {
return (
Expand All @@ -17,6 +18,7 @@ function App() {
<Route path="/game/lobby" element={<GameLobbyPage />} />
<Route path="/quiz/setup" element={<QuizSetupPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/pin" element={<PinPage />} />
<Route path="/mypage" element={<MyPage />} />
<Route path="*" element={<div>not found</div>} />
</Routes>
Expand Down
27 changes: 12 additions & 15 deletions FE/src/api/rest/authApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios';
import axiosInstance from './instance';

type LoginResponse = {
result: string;
acess_token: string;
};

export async function login(email: string, password: string): Promise<LoginResponse | null> {
Expand All @@ -14,13 +14,7 @@ export async function login(email: string, password: string): Promise<LoginRespo
console.log('Login Successful:', response.data);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Login Error:', error.response?.data || error.message);
} else {
console.error('Unexpected Error:', error);
}

return null;
return handleAxiosError(error);
}
}

Expand All @@ -41,12 +35,15 @@ export async function signUp(data: SignupPayload): Promise<SignupResponse | null

return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Signup Error:', error.response?.data || error.message);
} else {
console.error('Unexpected Error:', error);
}

return null;
return handleAxiosError(error);
}
}

const handleAxiosError = (error: unknown): null => {
if (axios.isAxiosError(error)) {
console.error('Axios Error:', error.response?.data || error.message);
} else {
console.error('Unexpected Error:', error);
}
return null;
};
2 changes: 1 addition & 1 deletion FE/src/api/rest/quizTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type QuizChoiceInput = {
export type QuizInput = {
quiz: string;
limitTime: number;
choiceList: QuizChoiceInput[];
choices: QuizChoiceInput[];
};

export type CreateQuizSetPayload = {
Expand Down
20 changes: 16 additions & 4 deletions FE/src/api/socket/socket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ class SocketService {
private socket: SocketInterface;
private url: string;
private handlers: (() => void)[];
private handlerMap: Partial<
Record<SocketEvent, ((data: SocketDataMap[SocketEvent]['response']) => void)[]>
> = {};

constructor(url: string) {
this.socket = io() as SocketInterface;
Expand All @@ -46,17 +49,21 @@ class SocketService {
this.socket.on('connect', () => resolve());
this.socket.on('error', () => reject());
});
this.handlers.forEach((h) => h());
this.socket.onAny((eventName, ...args) => {
console.log(`SOCKET[${eventName}]`, ...args);
});
this.initHandler();
return;
}

async connectMock(gameId: keyof typeof mockMap) {
if (this.isActive()) return;
this.socket = new mockMap[gameId]() as SocketInterface;
this.initHandler();
}

initHandler() {
this.handlers.forEach((h) => h());
Object.entries(this.handlerMap).forEach(([event, handlers]) =>
handlers.forEach((h) => this.socket.on(event, h))
);
this.socket.onAny((eventName, ...args) => {
console.log(`SOCKET[${eventName}]`, ...args);
});
Expand All @@ -74,6 +81,7 @@ class SocketService {
return this.socket.id;
}

// deprecated
onPermanently<T extends SocketEvent>(
event: T,
callback: (data: SocketDataMap[T]['response']) => void
Expand All @@ -85,10 +93,14 @@ class SocketService {

on<T extends SocketEvent>(event: T, callback: (data: SocketDataMap[T]['response']) => void) {
if (this.isActive()) this.socket.on(event, callback);
if (!this.handlerMap[event]) this.handlerMap[event] = [];
this.handlerMap[event].push(callback);
}

off<T extends SocketEvent>(event: T, callback: (data: SocketDataMap[T]['response']) => void) {
if (!this.handlerMap[event]) return;
if (this.isActive()) this.socket.off(event, callback);
this.handlerMap[event] = this.handlerMap[event].filter((e) => e !== callback);
}

emit<T extends SocketEvent>(event: T, data: SocketDataMap[T]['request']) {
Expand Down
36 changes: 0 additions & 36 deletions FE/src/components/Input.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions FE/src/components/QuizOptionBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export const QuizOptionBoard = () => {
const handleClick: React.MouseEventHandler<HTMLDivElement> = (e) => {
const { pageX, pageY } = e;
const { width, height, top, left } = e.currentTarget.getBoundingClientRect();
const x = (pageX - left) / width;
const y = (pageY - top) / height;
const x = (pageX - left - window.scrollX) / width;
const y = (pageY - top - window.scrollY) / height;
if (x > 1 || y > 1) return;
socketService.emit('updatePosition', { gameId, newPosition: [y, x] });
const option = Math.round(x) + Math.floor(y * Math.ceil(choiceList.length / 2)) * 2;
Expand Down
23 changes: 10 additions & 13 deletions FE/src/components/QuizSetSearchList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useInfiniteQuery } from '@tanstack/react-query';
import { useCallback, useEffect, useRef, useState } from 'react';
import { QuizPreview } from './QuizPreview';
import { getQuizSetList } from '@/api/rest/quizApi';

// type Quiz = {
// id: string;
Expand All @@ -27,20 +28,16 @@ type Params = {
const SEARCH_COUNT = 10;

const QuizSetSearchList = ({ onClick, search }: Params) => {
const fetchPosts = async ({ pageParam = 1 }) => {
const res = await fetch(
'/api/quizset?' +
new URLSearchParams([
['search', search],
['offset', String(pageParam * SEARCH_COUNT)],
['size', String(SEARCH_COUNT)]
])
);
const data: { quizSetList: QuizSet[] } = await res.json();
// api둜 μˆ˜μ •μ‹œ
const fetchPosts = async ({ pageParam = '' }) => {
const data = await getQuizSetList('', pageParam, SEARCH_COUNT, search);
if (!data) {
throw new Error('Failed to fetch quiz set list');
}
return {
data: data.quizSetList,
nextPage: pageParam + 1,
hasMore: data.quizSetList.length > 0
nextPage: data.paging.nextCursor || '',
hasMore: data.paging.hasNextPage
};
};

Expand All @@ -49,7 +46,7 @@ const QuizSetSearchList = ({ onClick, search }: Params) => {
useInfiniteQuery({
queryKey: [search],
queryFn: fetchPosts,
initialPageParam: 0,
initialPageParam: '',
getNextPageParam: (lastPage) => (lastPage.hasMore ? lastPage.nextPage : undefined)
});

Expand Down
45 changes: 45 additions & 0 deletions FE/src/components/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { TextField } from '@mui/material';
import { ReactNode, useEffect, useRef } from 'react';

type InputProps = {
type?: string;
label: string;
value: string;
onChange: React.ChangeEventHandler<HTMLInputElement>;
error?: string;
className?: string;
children?: ReactNode;
};

export const TextInput = (props: InputProps) => {
const inputRef = useRef<HTMLInputElement | null>(null);

useEffect(() => {
if (props.error && inputRef.current) {
inputRef.current.querySelector('input')?.focus();
}
}, [props.error, inputRef]);

return (
<div className={'w-full ' + props.className}>
<div className="flex items-center">
<TextField
ref={inputRef}
label={props.label}
type={props.type || 'text'}
variant="outlined"
value={props.value}
onChange={props.onChange}
className="w-full flex-grow"
slotProps={{
inputLabel: {
style: { color: props.error ? 'red' : 'inherit', borderColor: 'red' }
}
}}
/>
{props.children}
</div>
<p className={'text-red-600 text-sm'}>{props.error}</p>
</div>
);
};
Loading

0 comments on commit 968cfa7

Please sign in to comment.