Skip to content

Commit

Permalink
UI: #72 유저 정보 설정 페이지 팀 설정 UI 구현 (#74)
Browse files Browse the repository at this point in the history
* UI: #72 유저 정보 설정 페이지 팀 설정 UI 구현

* Feat: #72 팀 초대 및 가입 상태를 처리하는 API Handler 추가 & 팀 설정 페이지 UI변경

* UI: #72 팀 head UI 추가 & style 변경

* Formatting: #72 TeamSettingPage 시맨틱 태그로 변경
  • Loading branch information
ice-bear98 authored Aug 23, 2024
1 parent 1f6c489 commit 68871f9
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 29 deletions.
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]);

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

0 comments on commit 68871f9

Please sign in to comment.