Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat / style : 게시판 생성 및 참여자 초대 api 연결 #112

Merged
merged 7 commits into from
Feb 2, 2025
Merged
24 changes: 24 additions & 0 deletions src/apis/Board/createBoardApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CreateBoardRequest, CreateBoardResponse } from '../../models/Modal';
import { http } from '../../apis/httpClient';

export const createBoardApi = async ({
title,
}: CreateBoardRequest): Promise<CreateBoardResponse> => {
const token = localStorage.getItem('accessToken'); // 토큰 가져오기
if (!token) {
throw new Error('토큰이 없습니다. 로그인 상태를 확인해주세요.');
}

const response = await http.post<CreateBoardResponse>(
'/api/boards',
{ title },
{
headers: {
Authorization: `Bearer ${token}`, // 헤더에 토큰 추가
'Content-Type': 'application/json',
},
}
);

return response;
};
13 changes: 13 additions & 0 deletions src/apis/Board/fetchBoardsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// apis/Board/fetchBoardsApi.ts
import { http } from '../../apis/httpClient';
import { BoardResponse } from '../../models/Board';

export const fetchBoardsApi = async (): Promise<BoardResponse[]> => {
try {
const response = await http.get<BoardResponse[]>('/api/boards/my');
return response;
} catch (error) {
console.error('API 호출 중 에러 발생:', error);
throw error; // 에러를 상위로 전달
}
};
16 changes: 16 additions & 0 deletions src/apis/Board/fetchPostsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { http } from '../../apis/httpClient';
import { PostResponse } from '../../models/Post';

export const fetchPostsApi = async (
boardId: number
): Promise<PostResponse[]> => {
try {
console.log(`게시글 데이터 요청 시작 (boardId: ${boardId})`);
const response = await http.get<PostResponse[]>(`/api/posts/${boardId}`);
console.log(`게시글 데이터 요청 성공:`, response);
return response;
} catch (error) {
console.error('게시글 데이터 요청 실패:', error);
throw error; // 에러를 다시 던져서 `useQuery`에서 처리
}
};
24 changes: 24 additions & 0 deletions src/apis/Board/inviteApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { InviteRequest, InviteResponse } from '../../models/Modal';
import { http } from '../../apis/httpClient';

export const inviteApi = async ({
boardId,
loginId,
}: InviteRequest): Promise<InviteResponse> => {
const token = localStorage.getItem('accessToken'); // 토큰 가져오기
if (!token) {
throw new Error('토큰이 없습니다. 로그인 상태를 확인해주세요.');
}

const response = await http.post<InviteResponse>(
`/api/boards/${boardId}/invite`,
{ loginId },
{
headers: {
Authorization: `Bearer ${token}`, // 헤더에 토큰 추가
'Content-Type': 'application/json', // 필요에 따라 추가
},
}
);
return response;
};
25 changes: 17 additions & 8 deletions src/components/layout/sideBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import React, { useState } from 'react';
import { SideBar, AddButton, ProfileButton } from './style';
import { SideBar, AddButton, ProfileButton, BoardItem } from './style';
import { IoIosAdd } from 'react-icons/io';
import { BsFillPersonFill } from 'react-icons/bs';
import AddBoardModal from '../../modals/AddBoardModal';

const Sidebar: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [boards, setBoards] = useState<string[]>([]);

const handleAddClick = () => {
setIsModalOpen(true);
};

const handleCloseModal = () => {
console.log('모달 닫기 클릭됨!');

setIsModalOpen(false);
};

const handleAddBoard = (boardName: string) => {
const truncatedName =
boardName.length > 5 ? `${boardName.slice(0, 5)}...` : boardName;
setBoards([truncatedName, ...boards]); // 새로운 게시판을 맨 위에 추가
};

const handleProfileClick = () => {};

const hiddenPaths: string[] = ['/sign-in', '/sign-up'];
Expand All @@ -27,15 +32,19 @@ const Sidebar: React.FC = () => {

return (
<SideBar>
<AddButton onClick={handleAddClick}>
<IoIosAdd className="AddIcon" />
</AddButton>

<div>
{boards.map((board, index) => (
<BoardItem key={index}>{board}</BoardItem>
))}
<AddButton onClick={handleAddClick}>
<IoIosAdd className="AddIcon" />
</AddButton>
</div>
<ProfileButton onClick={handleProfileClick}>
<BsFillPersonFill className="ProfileIcon" />
</ProfileButton>
{isModalOpen && (
<AddBoardModal isOpen={isModalOpen} onClose={handleCloseModal} />
<AddBoardModal onClose={handleCloseModal} onAddBoard={handleAddBoard} />
)}
</SideBar>
);
Expand Down
20 changes: 18 additions & 2 deletions src/components/layout/sideBar/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export const SideBar = styled.div`
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 2rem 0rem;
`;

export const AddButton = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 2rem;
cursor: pointer;
transition: transform 0.2s;
background-color: var(--input);
Expand Down Expand Up @@ -45,7 +45,6 @@ export const AddButton = styled.div`
export const ProfileButton = styled.div`
display: flex;
justify-content: center;
margin-bottom: 2rem;
align-items: center;
cursor: pointer;
transition: transform 0.2s;
Expand All @@ -70,3 +69,20 @@ export const ProfileButton = styled.div`
color: var(--light-gray);
}
`;

export const BoardItem = styled.div`
width: 5rem;
height: 5rem;
margin-bottom: 2rem;
border-radius: 50%;
background: var(--input);
display: flex;
justify-content: center;
align-items: center;
color: var(--white);
font-family: 'Pretendard';
font-size: 1rem;
font-weight: 700;
text-align: center;
line-height: normal;
`;
64 changes: 48 additions & 16 deletions src/components/modals/AddBoardModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,46 @@ import {
SuccessMessage,
} from './style';
import { BsXLg } from 'react-icons/bs';
import { BoardModalProps } from '../../../models/Modal';
import { AddBoardModalProps } from '../../../models/Modal';
import { useBoardInvite } from '../../../hooks/Board/useBoardInvite';
import { useCreateBoard } from '../../../hooks/Board/useCreateBoard';

const AddBoardModal: React.FC<BoardModalProps> = ({ onClose }) => {
const AddBoardModal: React.FC<AddBoardModalProps> = ({
onClose,
onAddBoard,
}) => {
const [boardName, setBoardName] = useState('');
const [id, setId] = useState('');
const [idList, setIdList] = useState<string[]>([]);
const [idError, setIdError] = useState(false);
const [idSuccess, setIdSuccess] = useState(false);

const { mutate: inviteUser } = useBoardInvite();
const { mutate: createBoard } = useCreateBoard();

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
e.preventDefault(); // 기본 엔터 동작 방지
if (id.trim()) {
if (!idList.includes(id)) {
setIdList([...idList, id]);
setIdSuccess(true); // 성공 메시지 표시
setIdError(false); // 에러 메시지 제거
setId(''); // 입력 초기화
} else {
setIdError(true); // 중복된 ID 입력 시 에러 표시
setIdSuccess(false); // 성공 메시지 제거
}
e.preventDefault(); // 기본 Enter 동작 방지
if (id.trim() && id.trim() && !idList.includes(id)) {
// API 호출
inviteUser(
{ boardId: 1, loginId: id },
{
onSuccess: () => {
setIdList([...idList, id]);
setIdSuccess(true);
setIdError(false);
setId('');
},
onError: () => {
setIdError(true);
setIdSuccess(false);
},
}
);
} else {
setIdError(true); // 빈 입력 값에 대한 에러 표시
setIdSuccess(false); // 성공 메시지 제거
setIdError(true); // 빈 입력값 또는 중복 ID 에러
setIdSuccess(false);
}
}
};
Expand All @@ -47,7 +62,24 @@ const AddBoardModal: React.FC<BoardModalProps> = ({ onClose }) => {
};

const handleCreateBoard = () => {
onClose(); // 모달 닫기
if (!boardName.trim()) {
alert('교실 이름을 입력하세요.');
return;
}

createBoard(
{ title: boardName },
{
onSuccess: () => {
onAddBoard(boardName); // Sidebar에 교실 이름 전달
onClose(); // 모달 닫기
},
onError: (error) => {
console.error('교실 생성 실패:', error);
alert('교실 생성에 실패했습니다. 다시 시도해주세요.');
},
}
);
};

return (
Expand Down
7 changes: 5 additions & 2 deletions src/components/modals/InvitePersonModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ import {
Line,
} from './style';
import { BsXLg } from 'react-icons/bs';
import { BoardModalProps } from '../../../models/Modal';
import { InvitePersonModalProps } from '../../../models/Modal';
import UserInfo from '../../../components/board/UserInfo';

const ParticipantModal: React.FC<BoardModalProps> = ({ isOpen, onClose }) => {
const ParticipantModal: React.FC<InvitePersonModalProps> = ({
isOpen,
onClose,
}) => {
const [isAdding, setIsAdding] = useState(false); // 화면 전환 상태
const [participants, setParticipants] = useState([
{
Expand Down
15 changes: 15 additions & 0 deletions src/hooks/Board/useBoardInvite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useMutation } from '@tanstack/react-query';
import { InviteRequest, InviteResponse } from '../../models/Modal';
import { inviteApi } from '../../apis/Board/inviteApi';

export const useBoardInvite = () => {
return useMutation<InviteResponse, Error, InviteRequest>({
mutationFn: inviteApi,
onSuccess: (data) => {
console.log('초대 성공:', data.message);
},
onError: (error) => {
console.error('초대 실패:', error);
},
});
};
15 changes: 15 additions & 0 deletions src/hooks/Board/useCreateBoard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useMutation } from '@tanstack/react-query';
import { createBoardApi } from '../../apis/Board/createBoardApi';
import { CreateBoardRequest, CreateBoardResponse } from '../../models/Modal';

export const useCreateBoard = () => {
return useMutation<CreateBoardResponse, Error, CreateBoardRequest>({
mutationFn: createBoardApi,
onSuccess: (data) => {
console.log('교실 생성 성공:', data);
},
onError: (error) => {
console.error('교실 생성 실패:', error);
},
});
};
12 changes: 12 additions & 0 deletions src/hooks/Board/useFetchBoards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// hooks/Board/useFetchBoards.ts
import { useQuery } from '@tanstack/react-query';
import { fetchBoardsApi } from '../../apis/Board/fetchBoardsApi';
import { BoardResponse } from '../../models/Board';

export const useFetchBoards = () => {
return useQuery<BoardResponse[], Error>({
queryKey: ['boards'], // 캐싱 키
queryFn: fetchBoardsApi, // API 호출 함수
staleTime: 5 * 60 * 1000, // 5분 동안 캐싱
});
};
12 changes: 12 additions & 0 deletions src/hooks/Board/useFetchPosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useQuery } from '@tanstack/react-query';
import { fetchPostsApi } from '../../apis/Board/fetchPostsApi';
import { PostResponse } from '../../models/Post';

export const useFetchPosts = (boardId: number | null) => {
return useQuery<PostResponse[], Error>({
queryKey: ['posts', boardId],
queryFn: () => fetchPostsApi(boardId!),
enabled: !!boardId,
staleTime: 5 * 60 * 1000,
});
};
7 changes: 7 additions & 0 deletions src/models/Board.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// models/Board.ts
export interface BoardResponse {
id: number; // 게시판 ID
title: string; // 게시판 이름
createdAt: string; // 생성 날짜 (ISO 포맷)
updatedAt: string; // 수정 날짜 (ISO 포맷)
}
32 changes: 32 additions & 0 deletions src/models/Modal.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
export interface BoardModalProps {
onClose: () => void;
onAddBoard?: (boardName: string) => void; // 선택적 속성으로 변경
isOpen: boolean;
}

export interface AddBoardModalProps {
onClose: () => void;
onAddBoard: (boardName: string) => void;
}

export interface InvitePersonModalProps {
isOpen: boolean;
onClose: () => void;
}

export interface InviteRequest {
boardId: number;
loginId: string;
}

export interface InviteResponse {
status: number;
message: string;
}

export interface CreateBoardRequest {
title: string;
}

export interface CreateBoardResponse {
id: number;
title: string;
createdAt: string;
updatedAt: string;
}
9 changes: 9 additions & 0 deletions src/models/Post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface PostResponse {
id: number;
boardId: number;
name: string;
language: string;
filePath: string;
createdAt: string;
roomId: number;
}