Skip to content

Commit

Permalink
Merge pull request #340 from boostcampwm2023/feature/invite-page
Browse files Browse the repository at this point in the history
feat: 참여 요청 기능 구현, 초대 링크 변경 기능 구현
  • Loading branch information
surinkwon authored Oct 5, 2024
2 parents d0706f2 + 2e79fa2 commit f0f986c
Show file tree
Hide file tree
Showing 17 changed files with 237 additions and 79 deletions.
10 changes: 10 additions & 0 deletions frontend/src/apis/api/inviteAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { API_URL } from "../../constants/path";
import { authAPI } from "../utils/authAPI";

export const getInvitePreview = async (inviteLinkId: string) => {
const response = await authAPI.get(
`${API_URL.INVITE_PREVIEW}/${inviteLinkId}`
);

return response;
};
11 changes: 10 additions & 1 deletion frontend/src/components/landing/member/LandingMember.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ const LandingMember = ({ projectTitle }: LandingMemberProps) => {
});
}

const handleChangeInviteLinkClick = () => {
console.log("Asdfsdf");

socket.emit("inviteLink", { action: "update", content: {} });
};

return (
<div className="w-full px-6 py-6 overflow-y-scroll rounded-lg shadow-box bg-gradient-to-tr to-light-green-linear-from from-light-green scrollbar-thin scrollbar-thumb-light-green scrollbar-track-transparent scrollbar-thumb-rounded-full">
<div className="flex flex-col gap-3">
Expand Down Expand Up @@ -103,7 +109,10 @@ const LandingMember = ({ projectTitle }: LandingMemberProps) => {
초대링크 복사
</button>
<span>|</span>
<button className="text-xxs hover:underline" onClick={() => {}}>
<button
className="text-xxs hover:underline"
onClick={handleChangeInviteLinkClick}
>
링크 변경
</button>
</div>
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/landing/project/LandingProject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LandingProjectLink from "./LandingProjectLink";
import { useOutletContext } from "react-router-dom";

import useLandingProjectSocket from "../../../hooks/common/landing/useLandingProjectSocket";
import useMemberStore from "../../../stores/useMemberStore";

interface LandingProjectProps {
projectId: string;
Expand All @@ -12,6 +13,7 @@ interface LandingProjectProps {
const LandingProject = ({ projectId }: LandingProjectProps) => {
const { socket }: { socket: Socket } = useOutletContext();
const { project } = useLandingProjectSocket(socket);
const { role } = useMemberStore((state) => state.myInfo);

return (
<div className="flex flex-col justify-between w-full p-6 rounded-lg shadow-box">
Expand All @@ -22,10 +24,12 @@ const LandingProject = ({ projectId }: LandingProjectProps) => {
</p>
</div>
<div className="text-xs">{project.subject}</div>
<div className="flex justify-between">
<div className="flex justify-between gap-4">
<LandingProjectLink projectId={projectId} type="BACKLOG" />
<LandingProjectLink projectId={projectId} type="SPRINT" />
<LandingProjectLink projectId={projectId} type="SETTINGS" />
{role === "LEADER" && (
<LandingProjectLink projectId={projectId} type="SETTINGS" />
)}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const LandingProjectLink = ({ projectId, type }: LandingProjectLinkProps) => {
return (
<Link
to={LINK_URL.BACKLOG(projectId)}
className={`w-[8.75rem] h-[5rem] rounded-lg flex justify-center gap-2 items-center ${color} hover:shadow-button`}
className={`w-full h-[5rem] rounded-lg flex justify-center gap-2 items-center ${color} hover:shadow-button`}
>
<Icon height={36} width={36} fill="#FFFFFF" />
<div className="flex flex-col items-center gap-0 text-white text-[1rem] font-semibold">
Expand Down
63 changes: 35 additions & 28 deletions frontend/src/components/main/PageLinkIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,41 @@ import SprintIcon from "../../assets/icons/sprint.svg?react";
import SettingIcon from "../../assets/icons/settings.svg?react";
import { LINK_URL } from "../../constants/path";
import { ProjectSidebarProps } from "../../types/common/main";
import useMemberStore from "../../stores/useMemberStore";

const PageLinkIcons = ({ pathname, projectId }: ProjectSidebarProps) => (
<div className="flex flex-col pl-[0.9375rem] pt-[1.5625rem] w-[5.3125rem] gap-5">
<PageIcon
Icon={LandingIcon}
activated={pathname === LINK_URL.MAIN(projectId)}
to={LINK_URL.MAIN(projectId)}
pageName="메인페이지"
/>
<PageIcon
Icon={BacklogIcon}
activated={pathname.split("/").includes("backlog")}
to={LINK_URL.BACKLOG(projectId)}
pageName="백로그"
/>
<PageIcon
Icon={SprintIcon}
activated={pathname === LINK_URL.SPRINT(projectId)}
to={LINK_URL.SPRINT(projectId)}
pageName="스프린트"
/>
<PageIcon
Icon={SettingIcon}
activated={pathname === LINK_URL.SETTINGS(projectId)}
to={LINK_URL.SETTINGS(projectId)}
pageName="프로젝트 설정"
/>
</div>
);
const PageLinkIcons = ({ pathname, projectId }: ProjectSidebarProps) => {
const { role } = useMemberStore((state) => state.myInfo);

return (
<div className="flex flex-col pl-[0.9375rem] pt-[1.5625rem] w-[5.3125rem] gap-5">
<PageIcon
Icon={LandingIcon}
activated={pathname === LINK_URL.MAIN(projectId)}
to={LINK_URL.MAIN(projectId)}
pageName="메인페이지"
/>
<PageIcon
Icon={BacklogIcon}
activated={pathname.split("/").includes("backlog")}
to={LINK_URL.BACKLOG(projectId)}
pageName="백로그"
/>
<PageIcon
Icon={SprintIcon}
activated={pathname === LINK_URL.SPRINT(projectId)}
to={LINK_URL.SPRINT(projectId)}
pageName="스프린트"
/>
{role === "LEADER" && (
<PageIcon
Icon={SettingIcon}
activated={pathname === LINK_URL.SETTINGS(projectId)}
to={LINK_URL.SETTINGS(projectId)}
pageName="프로젝트 설정"
/>
)}
</div>
);
};

export default PageLinkIcons;
38 changes: 38 additions & 0 deletions frontend/src/components/setting/JoinRequestBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import useMemberStore from "../../stores/useMemberStore";
import { SettingJoinRequestDTO } from "../../types/DTO/settingDTO";

interface JoinRequestBlockProps extends SettingJoinRequestDTO {}

const JoinRequestBlock = ({ username, imageUrl }: JoinRequestBlockProps) => {
const myRole = useMemberStore((state) => state.myInfo.role);

return (
<div className="flex w-full gap-3">
<div className="w-[16.25rem] flex gap-3 items-center">
<img className="w-8 h-8 rounded-full" src={imageUrl} alt={username} />
<p className="">{username}</p>
</div>
<div className="w-[18.75rem]"></div>
<div className="w-[30rem]">
{myRole === "LEADER" && (
<>
<button
className="px-2 py-1 mr-3 text-white rounded w-fit text-xxs bg-middle-green"
type="button"
>
참여 수락
</button>
<button
className="px-2 py-1 text-white rounded w-fit bg-error-red text-xxs"
type="button"
>
참여 거절
</button>
</>
)}
</div>
</div>
);
};

export default JoinRequestBlock;
7 changes: 4 additions & 3 deletions frontend/src/components/setting/MemberBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import useMemberStore from "../../stores/useMemberStore";
import { LandingMemberDTO } from "../../types/DTO/landingDTO";
import { SettingMemberDTO } from "../../types/DTO/settingDTO";

interface MemberBlockProps extends LandingMemberDTO {}
interface MemberBlockProps extends SettingMemberDTO {}

const MemberBlock = ({ username, imageUrl, role }: MemberBlockProps) => {
const myRole = useMemberStore((state) => state.myInfo.role);
const myUserName = useMemberStore((state) => state.myInfo.username);

return (
<div className="flex w-full gap-3">
Expand All @@ -16,7 +17,7 @@ const MemberBlock = ({ username, imageUrl, role }: MemberBlockProps) => {
<p className="">{role}</p>
</div>
<div className="w-[30rem]">
{myRole === "LEADER" && (
{myRole === "LEADER" && myUserName !== username && (
<button
className="px-2 py-1 text-white rounded w-fit bg-error-red text-xxs"
type="button"
Expand Down
15 changes: 12 additions & 3 deletions frontend/src/components/setting/MemberSettingSection.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { LandingMemberDTO } from "../../types/DTO/landingDTO";
import JoinRequestBlock from "./JoinRequestBlock";
import MemberBlock from "./MemberBlock";
import {
SettingJoinRequestDTO,
SettingMemberDTO,
} from "../../types/DTO/settingDTO";

interface MemberSettingSectionProps {
memberList: LandingMemberDTO[];
memberList: SettingMemberDTO[];
joinRequestList: SettingJoinRequestDTO[];
}

const MemberSettingSection = ({ memberList }: MemberSettingSectionProps) => (
const MemberSettingSection = ({
memberList,
joinRequestList,
}: MemberSettingSectionProps) => (
<div className="mb-5">
<div className="mb-2">
<p className="font-bold text-m text-middle-green">멤버 관리</p>
Expand All @@ -18,6 +26,7 @@ const MemberSettingSection = ({ memberList }: MemberSettingSectionProps) => (
</div>
<div className="flex flex-col gap-3 overflow-y-auto scrollbar-thin">
{...memberList.map((member) => <MemberBlock {...member} />)}
{...joinRequestList.map((request) => <JoinRequestBlock {...request} />)}
</div>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/constants/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const API_URL = {
NICKNAME_AVAILABLILITY: "/member/availability",
GITHUB_USERNAME: "/auth/github/username",
PROJECT: "/project",
PROJECT_JOIN: "/project/join",
PROJECT_JOIN: "/project/join-request",
INVITE_PREVIEW: "/project/invite-preview",
};

export const ROUTER_URL = {
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/hooks/common/member/useUpdateUserStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Socket } from "socket.io-client";
import {
LandingSocketData,
LandingSocketDomain,
LandingSocketInviteLinkAction,
LandingSocketMemberAction,
} from "../../../types/common/landing";
import { LandingDTO, LandingMemberDTO } from "../../../types/DTO/landingDTO";
Expand Down Expand Up @@ -76,6 +77,16 @@ const useUpdateUserStatus = (
}
};

const handleInviteLinkEvent = (
action: LandingSocketInviteLinkAction,
content: { inviteLinkId: string }
) => {
if (action === "update") {
alert("초대링크가 변경되었습니다.");
inviteLinkIdRef.current = content.inviteLinkId;
}
};

const handleOnLanding = ({ domain, action, content }: LandingSocketData) => {
switch (domain) {
case LandingSocketDomain.INIT:
Expand All @@ -84,6 +95,9 @@ const useUpdateUserStatus = (
case LandingSocketDomain.MEMBER:
handleMemberEvent(action, content);
break;
case LandingSocketDomain.INVITE_LINK:
handleInviteLinkEvent(action, content);
break;
}
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/common/socket/useSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const useSocket = (projectId: string) => {
alert("프로젝트가 삭제되었습니다.");
setTimeout(() => {
navigate(ROUTER_URL.PROJECTS);
}, 1000);
}, 500);
}
};

Expand Down
17 changes: 14 additions & 3 deletions frontend/src/hooks/pages/setting/useSettingSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@ import {
SettingSocketDomain,
SettingSocketProjectInfoAction,
} from "../../../types/common/setting";
import { SettingDTO, SettingProjectDTO } from "../../../types/DTO/settingDTO";
import {
SettingDTO,
SettingJoinRequestDTO,
SettingMemberDTO,
SettingProjectDTO,
} from "../../../types/DTO/settingDTO";

const useSettingSocket = (socket: Socket) => {
const [projectInfo, setProjectInfo] = useState<SettingProjectDTO>({
title: "",
subject: "",
});
const [memberList, setMemberList] = useState<SettingMemberDTO[]>([]);
const [joinRequestList, setJoinRequestList] = useState<
SettingJoinRequestDTO[]
>([]);

const handleInitEvent = (content: SettingDTO) => {
const { project } = content;
const { project, member, joinRequestList } = content;
setProjectInfo(project);
setMemberList(member);
setJoinRequestList(joinRequestList ? joinRequestList : []);
};

const handleProjectInfoEvent = (
Expand Down Expand Up @@ -53,7 +64,7 @@ const useSettingSocket = (socket: Socket) => {
};
}, []);

return { projectInfo };
return { projectInfo, memberList, joinRequestList };
};

export default useSettingSocket;
Loading

0 comments on commit f0f986c

Please sign in to comment.