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: 스카우트 모달 내 태그 API 연결 및 기타 버그 수정 #345

Merged
Merged
12 changes: 6 additions & 6 deletions co-kkiri/src/components/commons/CollapseSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import styled from "styled-components";
interface CollapseSectionProps {
title: string;
children: React.ReactNode;
isCollapsed: boolean;
isOpen: boolean;
onClick: () => void;
className?: string;
}

export default function CollapseSection({ title, children, isCollapsed, onClick, className }: CollapseSectionProps) {
export default function CollapseSection({ title, children, isOpen, onClick, className }: CollapseSectionProps) {
return (
<Container className={className}>
<Header onClick={onClick}>
<Icon src={ICONS.circleArrow.src} alt={ICONS.circleArrow.alt} $isCollapsed={isCollapsed} />
<Icon src={ICONS.circleArrow.src} alt={ICONS.circleArrow.alt} $isOpen={isOpen} />
<p>{title}</p>
</Header>
{!isCollapsed && <>{children}</>}
{isOpen && <>{children}</>}
</Container>
);
}
Expand Down Expand Up @@ -48,9 +48,9 @@ const Header = styled.div`
}
`;

const Icon = styled.img<{ $isCollapsed: boolean }>`
const Icon = styled.img<{ $isOpen: boolean }>`
width: 2.4rem;
height: 2.4rem;

${({ $isCollapsed }) => $isCollapsed && `transform: rotate(180deg);`}
${({ $isOpen }) => $isOpen && `transform: rotate(180deg);`}
`;
3 changes: 2 additions & 1 deletion co-kkiri/src/components/domains/myPage/TagList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import styled from "styled-components";
import NoResultText from "@/components/commons/NoResultText";
import EvaluationChip from "@/components/commons/Chips/EvaluationChip";
import { MyPageReviewApiResponseDto } from "@/lib/api/myPage/type";
import { emptyMessages } from "@/components/commons/UserProfileCard/constants";

interface TagListProps {
reviewList: MyPageReviewApiResponseDto;
Expand All @@ -19,7 +20,7 @@ export default function TagList({ reviewList }: TagListProps) {
<SectionTitle title="내가 받은 태그" lineLength="mypage" />
<TagListWrapper>
{noTags ? (
<NoResultText text="받은 태그가 없어요" padding={60} color="gray" />
<NoResultText text={emptyMessages.tags} padding={60} color="gray" />
) : (
<>
{compliments.map((tag, index) => (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import styled from "styled-components";
import EvaluationChip from "@/components/commons/Chips/EvaluationChip";
import { TagType } from "@/types/tagtypes";
import DESIGN_TOKEN from "@/styles/tokens";
import { emptyMessages } from "@/components/commons/UserProfileCard/constants";

interface UserProfileTagListProps {
reviewList: TagType[];
}

export default function UserProfileTagList({ reviewList }: UserProfileTagListProps) {
const noTags = reviewList.length === 0;
const compliments = reviewList.filter((tag) => tag.type === "COMPLIMENT");
return (
<Container>
{noTags ? (
<NoResultText>{emptyMessages.tags}</NoResultText>
) : (
<>
{compliments.map((tag, index) => (
<EvaluationChip
key={`${tag.content}-${index}`}
label={tag.content || ""}
evaluationWay={tag.type}
count={tag.count}
/>
))}
</>
)}
</Container>
);
}

const { typography, color } = DESIGN_TOKEN;

const Container = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 0.6rem;
`;

const NoResultText = styled.p`
margin: 4rem 0;

color: ${color.gray[1]};
${typography.font12Regular}
`;
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import DefaultModalLayout from "./ModalLayout";
import UserProfileCardLayout from "../commons/UserProfileCard/UserProfileCardLayout";
import DefaultModalLayout from "../ModalLayout";
import UserProfileCardLayout from "../../commons/UserProfileCard/UserProfileCardLayout";
import styled from "styled-components";
import Button from "../commons/Button";
import Button from "../../commons/Button";
import useOpenToggle from "@/hooks/useOpenToggle";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { getMemberProfile } from "@/lib/api/member";
import { MemberProfileApiResponseDto } from "@/lib/api/member/type";
import { getMemberProfile, getMemberReviewList } from "@/lib/api/member";
import { MemberProfileApiResponseDto, MemberReviewListApiResponseDto } from "@/lib/api/member/type";
import { memberProfile } from "@/constants/initialDatas";
import ScoutModal from "./ScoutModal";
import ScoutModal from "../ScoutModal";
import { useToast } from "@/hooks/useToast";
import { useEffect } from "react";
import DESIGN_TOKEN from "@/styles/tokens";
import Divider from "../../commons/Divider";
import CollapseSection from "../../commons/CollapseSection";
import UserProfileTagList from "./UserProfileTagList";
import { useToggle } from "usehooks-ts";

interface UserProfileModalProps {
userId: number;
Expand All @@ -20,9 +24,10 @@ interface UserProfileModalProps {
export default function UserProfileModal({ userId, onClose }: UserProfileModalProps) {
const pushToast = useToast();
const { isOpen: isInvitedModalOpen, setIsOpen: setIsInvitedModalOpen } = useOpenToggle();
const [isToggled, _, setIsToggled] = useToggle(false);
const queryClient = useQueryClient();

const { data, isError } = useQuery<MemberProfileApiResponseDto>({
const { data: MemberProfileData, isError: isMemberProfileError } = useQuery<MemberProfileApiResponseDto>({
queryKey: ["memberProfile", userId],
queryFn: ({ queryKey }) => getMemberProfile(queryKey[1] as number),
placeholderData: () => {
Expand All @@ -32,17 +37,27 @@ export default function UserProfileModal({ userId, onClose }: UserProfileModalPr
staleTime: 1000 * 60,
});

const { data: TagsData, isError: isTagsError } = useQuery<MemberReviewListApiResponseDto>({
queryKey: ["review", "list", userId],
queryFn: ({ queryKey }) => getMemberReviewList(queryKey[2] as number),
placeholderData: () => {
const data = queryClient.getQueryData<MemberReviewListApiResponseDto>(["review", "list", userId]);
return data || [];
},
staleTime: 1000 * 60,
});

const onScoutClickHandler = () => {
setIsInvitedModalOpen(true);
};

useEffect(() => {
if (isError) {
if (isMemberProfileError || isTagsError) {
pushToast("유저 정보를 불러오는 중 오류가 발생했습니다", "error");
}
}, [isError, pushToast]);
}, [isMemberProfileError, isTagsError, pushToast]);

if (!data) {
if (!MemberProfileData) {
// 스켈레톤 UI 추가
return null;
}
Expand All @@ -51,21 +66,12 @@ export default function UserProfileModal({ userId, onClose }: UserProfileModalPr
<>
{!isInvitedModalOpen && (
<ModalLayout desktopWidth={430} tabletWidth={430} mobileWidth={320} onClose={onClose} isCloseClickOutside>
<UserProfileCardLayout {...data} score={data.gauge} />
{/* <Divider /> */}
{/* <CollapseSection title="유저가 받은 태그" isCollapsed={isOpen} onClick={() => setIsOpen(!isOpen)}>
{isEmptyValue(data?.memberProfile.tags) ? (
<EmptyCollapseBody>{emptyMessages.tags}</EmptyCollapseBody>
) : (
<CollapseBody>
{Object.entries(data?.memberProfile.tags).map(([tag, count]) => (
<EvaluationChip key={tag} label={`${tag} ${count}`} evaluationWay="compliments" />
))}
</CollapseBody>
)}
</CollapseSection> */}
{/* //TODO: 내 프로필이어도 안보여야함 - 백엔드 추가 필요 */}
{data.isVisibleProfile && (
<UserProfileCardLayout {...MemberProfileData} score={MemberProfileData.gauge} />
<Divider />
<CollapseSection title="유저가 받은 태그" isOpen={isToggled} onClick={() => setIsToggled(!isToggled)}>
<UserProfileTagList reviewList={TagsData || []} />
</CollapseSection>
{!MemberProfileData.isMine && MemberProfileData.isVisibleProfile && (
<Button variant="primary" onClick={onScoutClickHandler}>
스카우트
</Button>
Expand All @@ -75,7 +81,7 @@ export default function UserProfileModal({ userId, onClose }: UserProfileModalPr

{isInvitedModalOpen && (
<ScoutModal
memberInfo={{ ...data }}
memberInfo={{ ...MemberProfileData }}
onClose={() => {
setIsInvitedModalOpen(false);
}}
Expand Down
2 changes: 2 additions & 0 deletions co-kkiri/src/lib/api/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ export const memberAddress = {
memberId: (memberId: number) => `/member/${memberId}`,
//get
search: "/member/search",
//get
reviewList: (memberId: number) => `review/review/list/${memberId}`,
};

export const scoutAddress = {
Expand Down
5 changes: 5 additions & 0 deletions co-kkiri/src/lib/api/member/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { memberAddress } from "../address";
import { apiRequest } from "../axios";
import {
MemberProfileApiResponseDto,
MemberReviewListApiResponseDto,
SearchedMemberProfileApiRequestDto,
SearchedMemberProfileApiResponseDto,
} from "./type";
Expand All @@ -17,3 +18,7 @@ export const getMemberProfile = (memberId: number): Promise<MemberProfileApiResp
export const getSearchedMemberProfile = (
qs: SearchedMemberProfileApiRequestDto,
): Promise<SearchedMemberProfileApiResponseDto> => apiRequest("get", memberAddress.search, null, qs);

/** 유저 리뷰 조회 */
export const getMemberReviewList = (memberId: number): Promise<MemberReviewListApiResponseDto> =>
apiRequest("get", memberAddress.reviewList(memberId));
4 changes: 4 additions & 0 deletions co-kkiri/src/lib/api/member/type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { TagType } from "@/types/tagtypes";
import { PageMeta, PaginationOptions } from "../pageMetaType";
import { ReviewType } from "../review/type";

// 유저 프로필 조회
export type MemberProfile = {
Expand Down Expand Up @@ -36,3 +38,5 @@ export type SearchedMemberProfileApiRequestDto = PaginationOptions & {
position?: string; // 포지션
stacks?: string[]; // 스택
};

export type MemberReviewListApiResponseDto = TagType[];
7 changes: 2 additions & 5 deletions co-kkiri/src/lib/api/myPage/type.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CategoryList } from "@/types/categoryAndFilterTypes";
import { PageMeta, PaginationOptions } from "../pageMetaType";
import { TagType } from "@/types/tagtypes";

// 마이 페이지 유저 정보
export type UserInfoApiResponseDto = {
Expand All @@ -15,11 +16,7 @@ export type UserInfoApiResponseDto = {
};

// 내가 받은 태그 정보
type MyPageReview = {
type: "COMPLIMENT" | "IMPROVEMENT";
content?: string;
count: number;
};
type MyPageReview = TagType;

export type MyPageReviewApiResponseDto = MyPageReview[];

Expand Down
1 change: 1 addition & 0 deletions co-kkiri/src/pages/MyPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default function MyPage() {
queryKey: ["my-page/review/list"],
queryFn: () => getReviewTagList(),
retry: false,
staleTime: 1000 * 60,
});

const {
Expand Down
5 changes: 5 additions & 0 deletions co-kkiri/src/types/tagtypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type TagType = {
type: 'COMPLIMENT' | 'IMPROVEMENT'
content: string;
count: number;
}