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

UI: #72 유저 정보 설정 페이지 팀 설정 UI 구현 #74

Merged
merged 6 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/sidebar/ListSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function ListSidebar({ label, title, children, showButton, text,
};

return (
<aside className="mr-10 flex w-1/3 flex-col border border-list bg-contents-box">
<aside className="mr-10 flex w-1/3 min-w-125 max-w-220 shrink-0 flex-col border border-list bg-contents-box">
<div className="flex min-h-30 items-center justify-between bg-sub px-10">
<div>
{label && <small className="mr-5 font-bold text-main">{label}</small>}
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/page/ProjectLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function ProjectLayout() {
<ListSidebar label="team" title="팀 이름...">
<ListProject data={PROJECT_DUMMY} targetId={projectId} />
</ListSidebar>
<section className="flex w-2/3 flex-col border border-list bg-contents-box">
<section className="flex grow flex-col border border-list bg-contents-box">
<header className="flex h-30 items-center justify-between border-b p-10">
{/* ToDo: LabelTitle 공통 컴포넌트로 추출할 것 */}
<div>
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/page/SettingLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function SettingLayout() {
<ListSidebar title={`${USER_INFO_DUMMY.nickname} 님의 정보`}>
<ListSetting navList={navList} />
</ListSidebar>
<section className="flex w-2/3 flex-col border border-list bg-contents-box">
<section className="flex grow flex-col border border-list bg-contents-box">
<header className="flex h-30 items-center justify-between border-b p-10">
<div>
<small className="font-bold text-category">{getTitle()}</small>
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/page/TeamLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function TeamLayout() {
<ListSidebar title="팀 목록" showButton text="팀 생성" onClick={openTeamModal}>
<ListTeam data={TEAM_DUMMY} targetId={teamId} />
</ListSidebar>
<section className="flex h-full w-2/3 flex-col border border-list bg-contents-box">
<section className="flex grow flex-col border border-list bg-contents-box">
{teamData.length === 0 ? (
<div className="flex h-full items-center justify-center text-center">
소속된 팀이 없습니다! <br />
Expand Down
50 changes: 26 additions & 24 deletions src/mocks/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type TeamUser = {
teamId: number;
userId: number;
roleId: number;
regStatus: boolean;
isPendingApproval: boolean;
};

type ProjectUser = {
Expand All @@ -18,8 +18,7 @@ type ProjectUser = {
roleId: number;
};

export const JWT_TOKEN_DUMMY =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
export const JWT_TOKEN_DUMMY = 'mocked-header.mocked-payload-4.mocked-signature';

export const USER_INFO_DUMMY = {
provider: 'LOCAL',
Expand Down Expand Up @@ -226,117 +225,117 @@ export const TEAM_USER_DUMMY: TeamUser[] = [
teamId: 1,
userId: 1,
roleId: 1,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 1,
userId: 3,
roleId: 1,
regStatus: true,
roleId: 2,
isPendingApproval: true,
},
{
teamId: 1,
userId: 4,
roleId: 3,
regStatus: false,
isPendingApproval: false,
},
{
teamId: 1,
userId: 8,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 1,
userId: 9,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 1,
userId: 11,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 1,
userId: 12,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 1,
userId: 13,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 1,
userId: 14,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 1,
userId: 15,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
// 팀2 소속 유저 정보
{
teamId: 2,
userId: 3,
roleId: 2,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 2,
userId: 4,
roleId: 1,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 2,
userId: 5,
roleId: 3,
regStatus: false,
isPendingApproval: false,
},
{
teamId: 2,
userId: 7,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 2,
userId: 10,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
// 팀3 소속 유저 정보
{
teamId: 3,
userId: 1,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 3,
userId: 2,
roleId: 3,
regStatus: true,
isPendingApproval: true,
},
{
teamId: 3,
userId: 5,
roleId: 2,
regStatus: false,
isPendingApproval: false,
},
{
teamId: 3,
userId: 6,
roleId: 1,
regStatus: true,
isPendingApproval: true,
},
] as const;

Expand All @@ -345,17 +344,20 @@ export const TEAM_DUMMY: Team[] = [
{
teamId: 1,
name: 'GU99',
content: '사이드 프로젝트 팀원 모집 / 프로젝트 관리 서비스 등을 만드는 팀',
content: '사이드 프로젝트 팀원 모집 / 프로젝트 관리 서비스 등을 만드는 팀 ',
creatorId: 1,
},
{
teamId: 2,
name: '오늘볼래',
content: '모임/이벤트/소개팅 등 사람과 사람을 이어주는 서비스를 만드는 팀',
creatorId: 4,
},
{
teamId: 3,
name: '고인물',
content: '게임 리뷰/정보공유/모임 등을 위한 서바스를 개발하고 있는 팀',
creatorId: 6,
},
] as const;

Expand Down
48 changes: 48 additions & 0 deletions src/mocks/services/userServiceHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { http, HttpResponse } from 'msw';
import { ROLE_DUMMY, TEAM_DUMMY, TEAM_USER_DUMMY, USER_DUMMY } from '@mocks/mockData';
import type { Team } from '@/types/TeamType';
import type { Role } from '@/types/RoleType';
import type { User } from '@/types/UserType';

const BASE_URL = import.meta.env.VITE_BASE_URL;

// ToDo: Dummy 데이터 Hash화 한 곳으로 모으기
const userServiceHandler = [
// 유저 검색 API
// ToDo: 내부 구현 사항 채울 것
Expand All @@ -14,6 +19,49 @@ const userServiceHandler = [

return HttpResponse.json([]);
}),
// 가입한 팀 목록 조회 API
http.get(`${BASE_URL}/user/team`, ({ request }) => {
const accessToken = request.headers.get('Authorization');

if (!accessToken) return new HttpResponse(null, { status: 401 });

const [header, payload, signature] = accessToken.split('.');
const userId = payload.replace('mocked-payload-', '');

// 유저가 속한 모든 팀 목록 추출
const teamUserList = TEAM_USER_DUMMY.filter((row) => row.userId === Number(userId));
// 유저 정보 Hash 형태로 추출
const USERS: { [key: string | number]: User } = {};
USER_DUMMY.forEach((user) => (USERS[user.userId] = user));

// 역할 정보 Hash 형태로 추출
const ROLES: { [key: string | number]: Role } = {};
ROLE_DUMMY.forEach((role) => (ROLES[role.roleId] = role));

// 팀 정보 Hash 형태로 추출
const TEAMS: { [key: string | number]: Team } = {};
TEAM_DUMMY.forEach((team) => (TEAMS[team.teamId] = team));

const teamJoinStatusList = teamUserList.map((teamUser) => {
const user = USERS[teamUser.userId];
const role = ROLES[teamUser.roleId];
const team = TEAMS[teamUser.teamId];

const creatorUser = USERS[team.creatorId];
const creatorNickname = creatorUser ? creatorUser.nickname : 'Unknown';

return {
teamId: team.teamId,
name: team.name,
content: team.content,
creator: creatorNickname,
isPendingApproval: teamUser.isPendingApproval,
roleName: role.roleName,
};
});

return HttpResponse.json(teamJoinStatusList);
}),
];

export default userServiceHandler;
117 changes: 116 additions & 1 deletion src/pages/setting/TeamSettingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,118 @@
import { useEffect, useState } from 'react';
import useAxios from '@/hooks/useAxios';
import { findTeamList } from '@/services/userService';

enum TeamStatus {
JOINED,
INVITED,
}

export default function TeamSettingPage() {
return <div>TeamSettingPage</div>;
const [view, setView] = useState<TeamStatus>(TeamStatus.JOINED);

// ToDo: useAxios 부분을 react query로 변경할 것
const { data, loading, fetchData } = useAxios(findTeamList);
const joinedTeamList = data?.filter((data) => data.isPendingApproval === true);
const invitedTeamList = data?.filter((data) => data.isPendingApproval === false);
useEffect(() => {
fetchData();
}, [fetchData]);
Seok93 marked this conversation as resolved.
Show resolved Hide resolved

return (
<main>
<section className="mt-10 space-x-4 border-b-2 px-10">
<button
type="button"
onClick={() => setView(TeamStatus.JOINED)}
className={view === TeamStatus.JOINED ? 'text-main' : 'text-emphasis'}
>
가입현황
</button>
<button
type="button"
onClick={() => setView(TeamStatus.INVITED)}
className={view === TeamStatus.INVITED ? 'text-main' : 'text-emphasis'}
>
대기현황
</button>
</section>

<section className="mt-6 px-10">
{/* 가입현황 레이아웃 */}
{view === TeamStatus.JOINED && (
<article>
<ul className="min-w-300 space-y-2 text-sm">
{joinedTeamList?.map((team) => (
<li key={team.teamId} className="flex h-50 items-center gap-4 border p-8">
<div className="flex max-h-full gap-4">
<div className="max-h-full w-45 shrink-0">
<small className="text-xs font-bold text-main">team</small>
<p className="break-all">{team.name}</p>
</div>

<div className="flex max-h-full w-45 shrink-0 flex-col">
<small className="h-10 text-xs font-bold text-main">head</small>
<p className="grow overflow-y-auto break-all scrollbar-hide">{team.creator}</p>
</div>
</div>

<div className="flex h-full flex-grow items-center overflow-y-auto break-all px-4 scrollbar-hide">
<p className="max-h-full">{team.content}</p>
</div>
<div className="w-45 shrink-0">
<button
type="button"
className="rounded-md bg-red-500 px-5 py-2 text-sm text-white hover:brightness-90"
>
탈퇴하기
</button>
</div>
</li>
))}
</ul>
</article>
)}
{/* 대기현황 레이아웃 */}
{view === TeamStatus.INVITED && (
<article>
<ul className="min-w-300 space-y-2 text-sm">
{invitedTeamList?.map((invite) => (
<li key={invite.teamId} className="flex h-50 items-center gap-4 border p-8">
<div className="flex max-h-full gap-4">
<div className="max-h-full w-45 shrink-0">
<small className="text-xs font-bold text-main">team</small>
<p className="break-all">{invite.name}</p>
</div>

<div className="flex max-h-full w-45 shrink-0 flex-col">
<small className="h-10 text-xs font-bold text-main">head</small>
<p className="grow overflow-y-auto break-all scrollbar-hide">{invite.creator}</p>
</div>
</div>

<div className="flex h-full flex-grow items-center overflow-y-auto break-all px-4 scrollbar-hide">
<p className="max-h-full">{invite.content}</p>
</div>
<div className="flex shrink-0 flex-col gap-4">
<button
type="button"
className="rounded-md bg-main px-5 py-2 text-sm text-white hover:brightness-90"
>
수락하기
</button>
<button
type="button"
className="rounded-md bg-red-500 px-5 py-2 text-sm text-white hover:brightness-90"
>
거부하기
</button>
</div>
</li>
))}
</ul>
</article>
)}
</section>
</main>
);
}
Loading