Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
9 commits
Select commit Hold shift + click to select a range
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
3 changes: 2 additions & 1 deletion src/apis/external/useGetExternalName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ const getExternalName = async (teamId: number): Promise<string> => {
}
};

export const useGetExternalName = (teamId: number) => {
export const useGetExternalName = (teamId: number, opts?: { enabled?: boolean }) => {
return useQuery({
queryKey: [queryKey.EXTERNAL_NAME, teamId],
queryFn: () => getExternalName(teamId),
enabled: (opts?.enabled ?? true) && Number.isFinite(teamId) && teamId > 0,
});
};
3 changes: 2 additions & 1 deletion src/apis/goal/useGetGoalName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ const getGoalName = async (teamId: number): Promise<string> => {
}
};

export const useGetGoalName = (teamId: number) => {
export const useGetGoalName = (teamId: number, opts?: { enabled?: boolean }) => {
return useQuery({
queryKey: [queryKey.GOAL_NAME, teamId],
queryFn: () => getGoalName(teamId),
enabled: (opts?.enabled ?? true) && Number.isFinite(teamId) && teamId > 0,
});
};
3 changes: 2 additions & 1 deletion src/apis/issue/useGetIssueName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ const getIssueName = async (teamId: number): Promise<string> => {
}
};

export const useGetIssueName = (teamId: number) => {
export const useGetIssueName = (teamId: number, opts?: { enabled?: boolean }) => {
return useQuery({
queryKey: [queryKey.ISSUE_NAME, teamId],
queryFn: () => getIssueName(teamId),
enabled: (opts?.enabled ?? true) && Number.isFinite(teamId) && teamId > 0,
});
};
21 changes: 9 additions & 12 deletions src/components/DetailView/DetailHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import TeamIcon from '../ListView/TeamIcon';
import { useGetGoalName } from '../../apis/goal/useGetGoalName.ts';
import { useGetExternalName } from '../../apis/external/useGetExternalName.ts';
import { useGetIssueName } from '../../apis/issue/useGetIssueName.ts';
import { useNavigate, useParams } from 'react-router-dom';
import { useGetWorkspaceTeams } from '../../apis/setting/useGetWorkspaceTeams.ts';
import { useMemo } from 'react';
Expand All @@ -10,35 +7,35 @@ interface DetailHeaderProps {
type: 'goal' | 'issue' | 'external';
defaultTitle: string; // 상세페이지 제목에 아무것도 입력되지 않았을 때 기본 타이틀
title: string; // 상세페이지 제목으로부터 전달받아올 타이틀
detailId?: string; // 상세페이지 id (부모에서 받아올 예정)
}

const DetailHeader = ({ type, defaultTitle, title }: DetailHeaderProps) => {
const DetailHeader = ({ type, defaultTitle, title, detailId }: DetailHeaderProps) => {
const navigate = useNavigate();
const teamId = Number(useParams<{ teamId: string }>().teamId);
const { data: detailId } =
type === 'goal'
? useGetGoalName(teamId)
: type === 'issue'
? useGetIssueName(teamId)
: useGetExternalName(teamId);

// 팀 정보 불러오기
const { data: teamData } = useGetWorkspaceTeams();
const currentTeam = useMemo(() => {
return teamData?.pages[0].teamList.find((team) => team.teamId === Number(teamId));
}, [teamData, teamId]);

const canNavigate = Boolean(currentTeam?.teamId);

return (
<div className="flex gap-[3.2rem] flex-nowrap">
{/* 팀 아이콘, 팀명, props로 요소 전달 가능 */}
<TeamIcon
teamName={currentTeam?.teamName}
teamImgUrl={currentTeam?.teamImageUrl}
onClick={() => navigate(`/workspace/team/${currentTeam?.teamId}/${type}`)}
onClick={() => {
if (!canNavigate) return;
navigate(`/workspace/team/${currentTeam?.teamId}/${type}`);
}}
/>
<div className="flex gap-[1.6rem] items-center overflow-hidden">
{/* 상세페이지 ID */}
<div className="flex whitespace-nowrap font-body-b text-gray-600">{detailId}</div>
<div className="flex whitespace-nowrap font-body-b text-gray-600">{detailId ?? ''}</div>
{/**
* 상세페이지 이름
* - 상세페이지 제목에 아무것도 입력되지 않았을 때는 defaultTitle(placeholder명과 동일) 렌더링
Expand Down
26 changes: 14 additions & 12 deletions src/components/DetailView/WorkspaceDetailHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,46 @@
import WorkspaceIcon from '../ListView/WorkspaceIcon';
import { useNavigate, useParams } from 'react-router-dom';
import { useGetGoalName } from '../../apis/goal/useGetGoalName.ts';
import { useGetIssueName } from '../../apis/issue/useGetIssueName.ts';
import { useGetExternalName } from '../../apis/external/useGetExternalName.ts';
import { useGetWorkspaceTeams } from '../../apis/setting/useGetWorkspaceTeams.ts';
import { useMemo } from 'react';

interface WorkspaceDetailHeaderProps {
type: 'goal' | 'issue' | 'external';
defaultTitle: string; // 상세페이지 제목에 아무것도 입력되지 않았을 때 기본 타이틀
title: string; // 상세페이지 제목으로부터 전달받아올 타이틀
detailId?: string;
}

const WorkspaceDetailHeader = ({ type, defaultTitle, title }: WorkspaceDetailHeaderProps) => {
const WorkspaceDetailHeader = ({
type,
defaultTitle,
title,
detailId,
}: WorkspaceDetailHeaderProps) => {
const navigate = useNavigate();
const teamId = Number(useParams<{ teamId: string }>().teamId);
const { data: detailId } =
type === 'goal'
? useGetGoalName(teamId)
: type === 'issue'
? useGetIssueName(teamId)
: useGetExternalName(teamId);

// 팀 정보 불러오기
const { data: teamData } = useGetWorkspaceTeams();
const currentTeam = useMemo(() => {
return teamData?.pages[0].teamList.find((team) => team.teamId === Number(teamId));
}, [teamData, teamId]);

const canNavigate = Boolean(currentTeam?.teamId);

return (
<div className="flex gap-[3.2rem] flex-nowrap">
{/* 워크스페이스 아이콘, 워크스페이스명, props로 요소 전달 가능 */}
<WorkspaceIcon
workspaceName={currentTeam?.teamName}
workspaceImgUrl={currentTeam?.teamImageUrl}
onClick={() => navigate(`/workspace/default/team/${currentTeam?.teamId}/${type}`)}
onClick={() => {
if (!canNavigate) return;
navigate(`/workspace/default/team/${currentTeam?.teamId}/${type}`);
}}
/>
<div className="flex gap-[1.6rem] items-center overflow-hidden">
{/* 상세페이지 ID */}
<div className="flex whitespace-nowrap font-body-b text-gray-600">{detailId}</div>
<div className="flex whitespace-nowrap font-body-b text-gray-600">{detailId ?? ''}</div>
{/**
* 상세페이지 이름
* - 상세페이지 제목에 아무것도 입력되지 않았을 때는 defaultTitle(placeholder명과 동일) 렌더링
Expand Down
25 changes: 20 additions & 5 deletions src/pages/external/ExternalDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import {
import { useToast } from '../../components/Toast/ToastProvider.tsx';
import { useModalActions, useModalInfo } from '../../hooks/useModal.ts';
import Modal from '../../components/Modal/Modal.tsx';
import { useGetExternalName } from '../../apis/external/useGetExternalName.ts';

/** 상세페이지 모드 구분
* (1) create - 생성 모드: 처음에 생성하여 작성 완료하기 전
Expand Down Expand Up @@ -162,6 +163,15 @@ const ExternalDetail = ({ initialMode }: ExternalDetailProps) => {
]
: [];

// 생성 모드에서만 "다음 생성될 이름"을 조회
const isCreate = mode === 'create';
const { data: nextGeneratedName } = useGetExternalName(teamId, {
enabled: isCreate,
});

// 헤더표시용 이름: 생성 모드면 nextGeneratedName, 그 외엔 조회 데이터의 name
const headerDetailName = isCreate ? nextGeneratedName : externalDetail?.name;

// 단일 선택 라벨
const selectedStatusLabel = STATUS_LABELS[state];
const selectedPriorityLabel = PRIORITY_LABELS[priority];
Expand Down Expand Up @@ -476,14 +486,19 @@ const ExternalDetail = ({ initialMode }: ExternalDetailProps) => {
};

return (
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem]">
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem] overflow-x-auto basic-scroll">
{/* 상세페이지 헤더 */}
<DetailHeader type={'external'} defaultTitle="제목을 작성해보세요" title={title} />
<DetailHeader
type={'external'}
defaultTitle="제목을 작성해보세요"
title={title}
detailId={headerDetailName}
/>

{/* 상세페이지 메인 */}
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-h-max">
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-w-max min-h-max">
{/* 상세페이지 좌측 영역 - 제목 & 상세설명 & 댓글 */}
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-h-max">
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-w-[40rem] min-h-max">
{/* 상세페이지 제목 */}
<DetailTitle
defaultTitle="제목을 작성해보세요"
Expand Down Expand Up @@ -514,7 +529,7 @@ const ExternalDetail = ({ initialMode }: ExternalDetailProps) => {
</div>

{/* 상세페이지 우측 영역 - 속성 탭 & 하단의 작성 완료 버튼 */}
<div className="w-[33rem] flex flex-col min-h-max">
<div className="min-w-[33rem] flex flex-col min-h-max">
{/* 속성 탭 */}
<div className="w-full h-full flex flex-col gap-[1.6rem] ">
<div className="w-full font-title-sub-r text-gray-600">속성</div>
Expand Down
25 changes: 20 additions & 5 deletions src/pages/goal/GoalDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { queryKey } from '../../constants/queryKey';
import queryClient from '../../utils/queryClient';
import { useModalActions, useModalInfo } from '../../hooks/useModal';
import Modal from '../../components/Modal/Modal';
import { useGetGoalName } from '../../apis/goal/useGetGoalName';

/** 상세페이지 모드 구분
* (1) create - 생성 모드: 처음에 생성하여 작성 완료하기 전
Expand Down Expand Up @@ -103,6 +104,15 @@ const GoalDetail = ({ initialMode }: GoalDetailProps) => {
const canPatch = Number.isFinite(numericGoalId); // PATCH 가능 조건
const blocker = useBlocker(isEditable); // 편집하고 있는 상황에 화면 이동을 블로킹

// 생성 모드에서만 "다음 생성될 이름"을 조회
const isCreate = mode === 'create';
const { data: nextGeneratedName } = useGetGoalName(teamId, {
enabled: isCreate,
});

// 헤더표시용 이름: 생성 모드면 nextGeneratedName, 그 외엔 조회 데이터의 name
const headerDetailName = isCreate ? nextGeneratedName : goalDetail?.name;

// 단일 선택 라벨
const selectedStatusLabel = STATUS_LABELS[state];
const selectedPriorityLabel = PRIORITY_LABELS[priority];
Expand Down Expand Up @@ -340,14 +350,19 @@ const GoalDetail = ({ initialMode }: GoalDetailProps) => {
};

return (
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem]">
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem] overflow-x-auto basic-scroll">
{/* 상세페이지 헤더 */}
<DetailHeader type={'goal'} defaultTitle="목표를 생성하세요" title={title} />
<DetailHeader
type={'goal'}
defaultTitle="목표를 생성하세요"
title={title}
detailId={headerDetailName}
/>

{/* 상세페이지 메인 */}
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-h-max">
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-w-max min-h-max">
{/* 상세페이지 좌측 영역 - 제목 & 상세설명 & 댓글 */}
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-h-max">
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-w-[40rem] min-h-max">
{/* 상세페이지 제목 */}
<DetailTitle
defaultTitle="목표를 생성하세요"
Expand Down Expand Up @@ -378,7 +393,7 @@ const GoalDetail = ({ initialMode }: GoalDetailProps) => {
</div>

{/* 상세페이지 우측 영역 - 속성 탭 & 하단의 작성 완료 버튼 */}
<div className="w-[33rem] flex flex-col min-h-max">
<div className="min-w-[33rem] flex flex-col min-h-max">
{/* 속성 탭 */}
<div className="w-full h-full flex flex-col gap-[1.6rem] ">
<div className="w-full font-title-sub-r text-gray-600">속성</div>
Expand Down
25 changes: 20 additions & 5 deletions src/pages/issue/IssueDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { queryKey } from '../../constants/queryKey.ts';
import { useHydrateIssueDetail } from '../../hooks/useHydrateIssueDetail.ts';
import { useModalActions, useModalInfo } from '../../hooks/useModal.ts';
import Modal from '../../components/Modal/Modal.tsx';
import { useGetIssueName } from '../../apis/issue/useGetIssueName.ts';

/** 상세페이지 모드 구분
* (1) create - 생성 모드: 처음에 생성하여 작성 완료하기 전
Expand Down Expand Up @@ -107,6 +108,15 @@ const IssueDetail = ({ initialMode }: IssueDetailProps) => {
const canPatch = Number.isFinite(numericIssueId); // PATCH 가능 조건
const blocker = useBlocker(isEditable); // 편집하고 있는 상황에 화면 이동을 블로킹

// 생성 모드에서만 "다음 생성될 이름"을 조회
const isCreate = mode === 'create';
const { data: nextGeneratedName } = useGetIssueName(teamId, {
enabled: isCreate,
});

// 헤더표시용 이름: 생성 모드면 nextGeneratedName, 그 외엔 조회 데이터의 name
const headerDetailName = isCreate ? nextGeneratedName : issueDetail?.name;

// 단일 선택 라벨
const selectedStatusLabel = STATUS_LABELS[state];
const selectedPriorityLabel = PRIORITY_LABELS[priority];
Expand Down Expand Up @@ -355,14 +365,19 @@ const IssueDetail = ({ initialMode }: IssueDetailProps) => {
};

return (
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem]">
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem] overflow-x-auto basic-scroll">
{/* 상세페이지 헤더 */}
<DetailHeader type={'issue'} defaultTitle="이슈를 생성하세요" title={title} />
<DetailHeader
type={'issue'}
defaultTitle="이슈를 생성하세요"
title={title}
detailId={headerDetailName}
/>

{/* 상세페이지 메인 */}
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-h-max">
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-w-max min-h-max">
{/* 상세페이지 좌측 영역 - 제목 & 상세설명 & 댓글 */}
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-h-max">
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-w-[40rem] min-h-max">
{/* 상세페이지 제목 */}
<DetailTitle
defaultTitle="이슈를 생성하세요"
Expand Down Expand Up @@ -393,7 +408,7 @@ const IssueDetail = ({ initialMode }: IssueDetailProps) => {
</div>

{/* 상세페이지 우측 영역 - 속성 탭 & 하단의 작성 완료 버튼 */}
<div className="w-[33rem] flex flex-col min-h-max">
<div className="min-w-[33rem] flex flex-col min-h-max">
{/* 속성 탭 */}
<div className="w-full h-full flex flex-col gap-[1.6rem] ">
<div className="w-full font-title-sub-r text-gray-600">속성</div>
Expand Down
25 changes: 20 additions & 5 deletions src/pages/workspace/WorkspaceExternalDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { useHydrateExternalDetail } from '../../hooks/useHydrateExternalDetail.t
import { useModalActions, useModalInfo } from '../../hooks/useModal.ts';
import { useToast } from '../../components/Toast/ToastProvider.tsx';
import Modal from '../../components/Modal/Modal.tsx';
import { useGetExternalName } from '../../apis/external/useGetExternalName.ts';

/** 상세페이지 모드 구분
* (1) create - 생성 모드: 처음에 생성하여 작성 완료하기 전
Expand Down Expand Up @@ -162,6 +163,15 @@ const WorkspaceExternalDetail = ({ initialMode }: WorkspaceExternalDetailProps)
]
: [];

// 생성 모드에서만 "다음 생성될 이름"을 조회
const isCreate = mode === 'create';
const { data: nextGeneratedName } = useGetExternalName(teamId, {
enabled: isCreate,
});

// 헤더표시용 이름: 생성 모드면 nextGeneratedName, 그 외엔 조회 데이터의 name
const headerDetailName = isCreate ? nextGeneratedName : externalDetail?.name;

// 단일 선택 라벨
const selectedStatusLabel = STATUS_LABELS[state];
const selectedPriorityLabel = PRIORITY_LABELS[priority];
Expand Down Expand Up @@ -477,14 +487,19 @@ const WorkspaceExternalDetail = ({ initialMode }: WorkspaceExternalDetailProps)
};

return (
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem]">
<div className="flex flex-1 flex-col min-h-max gap-[5.7rem] w-full px-[3.2rem] pt-[3.2rem] overflow-x-auto basic-scroll">
{/* 상세페이지 헤더 */}
<WorkspaceDetailHeader type={'external'} defaultTitle="제목을 작성해보세요" title={title} />
<WorkspaceDetailHeader
type={'external'}
defaultTitle="제목을 작성해보세요"
title={title}
detailId={headerDetailName}
/>

{/* 상세페이지 메인 */}
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-h-max">
<div className="flex px-[3.2rem] gap-[8.8rem] w-full min-w-max min-h-max">
{/* 상세페이지 좌측 영역 - 제목 & 상세설명 & 댓글 */}
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-h-max">
<div className="flex flex-col flex-1 gap-[3.2rem] w-[calc(100%-33rem)] min-w-[40rem] min-h-max">
{/* 상세페이지 제목 */}
<DetailTitle
defaultTitle="제목을 작성해보세요"
Expand Down Expand Up @@ -515,7 +530,7 @@ const WorkspaceExternalDetail = ({ initialMode }: WorkspaceExternalDetailProps)
</div>

{/* 상세페이지 우측 영역 - 속성 탭 & 하단의 작성 완료 버튼 */}
<div className="w-[33rem] flex flex-col min-h-max">
<div className="min-w-[33rem] flex flex-col min-h-max">
{/* 속성 탭 */}
<div className="w-full h-full flex flex-col gap-[1.6rem] ">
<div className="w-full font-title-sub-r text-gray-600">속성</div>
Expand Down
Loading