Skip to content

Commit

Permalink
Feat: 스카우트 모달 내 태그 API 연결 및 기타 버그 수정 (#345)
Browse files Browse the repository at this point in the history
* Feat: 유저 Tag List API 변경

* Feat: UserProfileTagList 구현

* Feat: 캐시 1분

* Feat: UserProfileModal 위치 변경 및 API 구현

* Refactor: emptyMessage 적용

* Refactor: isCollapsed -> isOpen으로 변경

* Feat: compliments 추출
  • Loading branch information
nakyoung98 authored Apr 4, 2024
1 parent adb0734 commit e6f2c35
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 38 deletions.
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;
}

0 comments on commit e6f2c35

Please sign in to comment.