diff --git a/src/assets/data/keywords.ts b/src/assets/data/keywords.ts index fb82db3..567c340 100644 --- a/src/assets/data/keywords.ts +++ b/src/assets/data/keywords.ts @@ -1,84 +1,82 @@ export const basicKeywords = [ { - id: "프레젠테이션", + id: "018f99d1-cfd9-7991-8519-3c04e1ef6dab", name: "프레젠테이션", }, { - id: "문서화", + id: "018f99d1-0061-7785-b066-c56ccb8e8929", name: "문서화", }, { - id: "글쓰기", + id: "018f99d0-926b-7c84-99e8-f726538000e4", name: "글쓰기", }, { - id: "문제 발견", + id: "018f99d1-10d4-7fc7-afa3-20253ba5f1d3", name: "문제 발견", }, { - id: "탐구력", + id: "018f99d1-a0bb-76e3-a96c-4b25a30aec5b", name: "탐구력", }, { - id: "끈기", + id: "018f99d0-b6ef-7dcc-b149-146544946f03", name: "끈기", }, { - id: "문제해결", + id: "018f99d1-2134-7c2b-9bb1-ec87362cee1b", name: "문제해결", }, { - id: "분석력", + id: "018f99d1-3178-70c0-9105-ec3a62418bb4", name: "분석력", }, { - id: "계획력", + id: "018f99d0-7434-75d1-96af-66da7799a7e2", name: "계획력", }, { - id: "통찰력", + id: "018f99d1-af15-7eb9-9a8e-f480e5d51294", name: "통찰력", }, { - id: "논리력", + id: "018f99d0-d52e-718e-ad09-018eefc42532", name: "논리력", }, { - id: "협업", + id: "018f99d1-e00c-7ef2-88c0-c8bd4e923ce0", name: "협업", }, { - id: "커뮤니케이션", + id: "018f99d1-9175-702f-854b-df51010040c2", name: "커뮤니케이션", }, { - id: "아이디어", + id: "018f99d1-44b5-74ff-b0e3-d5ec54106584", name: "아이디어", }, { - id: "리더십", + id: "018f99d0-effc-7252-9fd3-86844df0da46", name: "리더십", }, { - id: "프로젝트 관리", + id: "018f99d1-c131-7ac2-92f7-2c95dbdce1d1", name: "프로젝트 관리", }, { - id: "인력 관리", + id: "018f99d1-56e2-7e15-9cb2-47b256a01def", name: "인력 관리", }, - { - id: "주도성", + id: "018f99d1-67aa-742f-837a-b3fa82e781b0", name: "주도성", }, - { - id: "책임감", + id: "018f99d1-79cd-7a61-bb86-596239427394", name: "책임감", }, { - id: "꼼꼼함", + id: "018f99d0-a5b0-7770-bb6c-690b8360f729", name: "꼼꼼함", }, ]; diff --git a/src/components/Experience/KeywordTab.tsx b/src/components/Experience/KeywordTab.tsx index 609ccb5..fa93bda 100644 --- a/src/components/Experience/KeywordTab.tsx +++ b/src/components/Experience/KeywordTab.tsx @@ -3,7 +3,9 @@ import { useRecoilState } from "recoil"; import styled, { useTheme } from "styled-components"; import { deleteState, + deleteTagState, primeTagState, + subTagState, yearState, } from "../../store/selectedStore"; import { questions } from "../../assets/data/questions"; @@ -30,18 +32,18 @@ import { basicKeywords } from "../../assets/data/keywords"; import Experience from "../JD/Experience"; import ExpData from "../../services/JD/ExpData"; import editIcon from "../../assets/images/editIcon.png"; -import { myKeywords } from "../../services/Experience/myKeywords"; import { useNavigate } from "react-router-dom"; import { + deleteTag, getPrimeTagSubTags, getPrimeTagYears, } from "../../services/Experience/tagApi"; import { getCookie } from "../../services/cookie"; -import { TagType } from "../../types/type"; import { ExperienceDetailType, KeywordType, TagMenuType, + TagType, } from "../../types/experience"; import { getExperienceList } from "../../services/Experience/experienceApi"; import { getKeywords } from "../../services/Experience/keywordApi"; @@ -57,7 +59,9 @@ const KeywordTab = ({ openDeleteModal }: KeywordTabProp) => { const navigate = useNavigate(); const [selectedYear, setSelectedYear] = useRecoilState(yearState); const [isDelete, setIsDelete] = useRecoilState(deleteState); + const [selectedDeleteTag, setSelectedDeleteTag] = useRecoilState(deleteTagState); const [selectedPrimeTag, setSelectedPrimeTag] = useRecoilState(primeTagState); + const [selectedSubTag, setSelectedSubTag] = useRecoilState(subTagState); const [selectedQ, setSelectedQ] = React.useState(0); const [expanded, setExpanded] = React.useState(false); // 질문 아코디언 관리 const [keywordTabOption, setKeywordTabOption] = @@ -66,7 +70,6 @@ const KeywordTab = ({ openDeleteModal }: KeywordTabProp) => { const [primeTagYears, setPrimeTagYears] = React.useState(); const [totalExpCount, setTotalExpCount] = React.useState(0); const [subTagMenus, setSubTagMenus] = React.useState([]); - const [selectedSubTag, setSelectedSubTag] = React.useState(); const [experiences, setExperiences] = React.useState( [] ); @@ -152,11 +155,16 @@ const KeywordTab = ({ openDeleteModal }: KeywordTabProp) => { setExpanded(!expanded); }; const handleDelete = () => { + setSelectedDeleteTag(null); setIsDelete(!isDelete); }; + const handleDeleteSubTag = (item: TagType) => { + setSelectedDeleteTag(item); + openDeleteModal(); + }; + // My 역량 키워드 조회 React.useEffect(() => { - // My 역량 키워드 조회 if (user?.token) { getKeywords(user?.token) .then((res) => setMyKeywordList(res.data.strongPoints)) @@ -186,6 +194,7 @@ const KeywordTab = ({ openDeleteModal }: KeywordTabProp) => { const { totalExperienceCount, tagInfos } = res.data; setTotalExpCount(totalExperienceCount); setSubTagMenus(tagInfos); + setSelectedSubTag(null); } ); } @@ -227,7 +236,7 @@ const KeywordTab = ({ openDeleteModal }: KeywordTabProp) => { setSelectedSubTag(undefined)} + onClick={() => setSelectedSubTag(null)} >
전체
{totalExpCount}
@@ -240,11 +249,13 @@ const KeywordTab = ({ openDeleteModal }: KeywordTabProp) => { >
{item.name}
{item.experienceCount}
- {isDelete && index !== 0 ? ( + {isDelete ? ( + handleDeleteSubTag({ id: item.id, name: item.name }) + } /> ) : null}
diff --git a/src/components/Experience/MoreTab.tsx b/src/components/Experience/MoreTab.tsx index be7a12e..f462088 100644 --- a/src/components/Experience/MoreTab.tsx +++ b/src/components/Experience/MoreTab.tsx @@ -4,13 +4,21 @@ import { ArrowRight } from "../../assets"; import { useRecoilState, useSetRecoilState } from "recoil"; import { primeTagState, yearState } from "../../store/selectedStore"; import PrimeTagCard from "./PrimeTagCard"; -import { moreData } from "../../services/Experience/moreData"; +import { getYearAllPrimeTags } from "../../services/Experience/tagApi"; +import { PrimeTagData } from "../../types/experience"; +import { getCookie } from "../../services/cookie"; const MoreTab = () => { + const user = getCookie("user"); const [selectedYear, setSelectedYear] = useRecoilState(yearState); const setSelectedPrimeTag = useSetRecoilState(primeTagState); + const [primeTagList, setPrimeTagList] = React.useState([]); - const totalNum = moreData.length; + + const handlePrimeTagClick = (item: PrimeTagData) => { + const selectedPrimeTag = { id: item.id, name: item.name }; + setSelectedPrimeTag(selectedPrimeTag); + }; /** * 사이드 메뉴 컨테이너 */ @@ -31,7 +39,7 @@ const MoreTab = () => { {selectedYear} 활동
-
{totalNum}개
+
{primeTagList.length}개
); @@ -44,11 +52,13 @@ const MoreTab = () => { return ( - {moreData.map((item) => ( + {primeTagList.map((item) => ( handlePrimeTagClick(item)} /> ))} @@ -56,6 +66,14 @@ const MoreTab = () => { ); }; + React.useEffect(() => { + if (selectedYear) { + getYearAllPrimeTags(selectedYear, user?.token).then((res) => + setPrimeTagList(res.data.tagInfos) + ); + } + }, [selectedYear]); + // // // diff --git a/src/components/Experience/PrimeTagCard.tsx b/src/components/Experience/PrimeTagCard.tsx index 57f26f1..b85f929 100644 --- a/src/components/Experience/PrimeTagCard.tsx +++ b/src/components/Experience/PrimeTagCard.tsx @@ -3,14 +3,16 @@ import styled from "styled-components"; import { ArrowRight } from "../../assets"; interface PrimeTagCardProp { + key: string; title: string; tagNum: number; expNum: number; + onClick: () => void; } -const PrimeTagCard = ({ title, tagNum, expNum }: PrimeTagCardProp) => { +const PrimeTagCard = ({ key, title, tagNum, expNum, onClick}: PrimeTagCardProp) => { return ( - +
{title}
diff --git a/src/components/Experience/YearCircle.tsx b/src/components/Experience/YearCircle.tsx index e9bd2d0..c0ecee7 100644 --- a/src/components/Experience/YearCircle.tsx +++ b/src/components/Experience/YearCircle.tsx @@ -1,13 +1,16 @@ -import React, { useEffect } from "react"; +import React from "react"; import { motion } from "framer-motion"; import styled from "styled-components"; import yearCircle from "../../assets/images/yearActiveCircle.png"; import { useRecoilState } from "recoil"; -import { deleteState, yearState } from "../../store/selectedStore"; +import { + deleteState, + deleteTagState, + yearState, +} from "../../store/selectedStore"; import { primeTagState } from "../../store/selectedStore"; import { DeleteIcon } from "../../assets"; import { TagType } from "../../types/experience"; -import { getYearPrimeTags } from "../../services/Experience/tagApi"; interface YearCircleProps { year: number; @@ -28,11 +31,11 @@ const YearCircle: React.FC = ({ const [selectedPrimeTag, setSelectedPrimeTag] = useRecoilState(primeTagState); const [isDelete, setIsDelete] = useRecoilState(deleteState); + const [selectedDeleteTag, setSelectedDeleteTag] = useRecoilState(deleteTagState); const isSelectedYear = selectedYear === year; const isHoveredYear = hoveredYear === year; - const radius = 20; const centralWidth = 84; const surroundWidth = 88; @@ -70,13 +73,18 @@ const YearCircle: React.FC = ({ exit: { scale: 0, opacity: 0, transition: { duration: 0.3 } }, }; - // 주변 원(키워드 원) 클릭 함수 + // 주변 원(상위 태그 원) 클릭 함수 const handleTagClick = (e: any, year: number, primeTag: TagType) => { e.stopPropagation(); setSelectedYear(year); setSelectedPrimeTag(primeTag); }; + const handleTagDelete = (item: TagType) => { + setSelectedDeleteTag(item); + openDeleteModal(); + }; + // // // @@ -111,7 +119,7 @@ const YearCircle: React.FC = ({ {selectedPrimeTag && isDelete && index !== 5 ? ( handleTagDelete(tag)} /> ) : null} diff --git a/src/components/Experience/YearList.tsx b/src/components/Experience/YearList.tsx index 2214305..b36760c 100644 --- a/src/components/Experience/YearList.tsx +++ b/src/components/Experience/YearList.tsx @@ -10,7 +10,6 @@ import { } from "../../store/selectedStore"; import { getExperienceYears } from "../../services/Experience/experienceApi"; import { getCookie } from "../../services/cookie"; -import { getYearPrimeTags } from "../../services/Experience/tagApi"; import { TagType, YearData } from "../../types/experience"; interface YearListProps { @@ -20,7 +19,6 @@ interface YearListProps { const YearList = ({ width, openDeleteModal }: YearListProps) => { const user = getCookie("user"); - const [primeTags, setPrimeTags] = React.useState([]); const [selectedYear, setSelectedYear] = useRecoilState( yearState ); @@ -31,9 +29,6 @@ const YearList = ({ width, openDeleteModal }: YearListProps) => { const [hoveredYear, setHoveredYear] = useState(null); - // 임시 데이터 - const keywords = ["큐시즘", "밋업", "밤양갱", "화이팅", "승효", "더보기"]; - // 클릭한 year 객체로 스크롤 이동하기 위한 객체 참조 값 const yearRefs = useRef<{ [key: number]: HTMLDivElement | null }>({}); years.forEach((year) => { @@ -144,7 +139,11 @@ const YearList = ({ width, openDeleteModal }: YearListProps) => { > 5 + ? [...data.tags.slice(0, 5), { id: "더보기", name: "더보기" }] + : data.tags + } hoveredYear={hoveredYear} openDeleteModal={openDeleteModal} /> diff --git a/src/pages/ExperiencePage.tsx b/src/pages/ExperiencePage.tsx index e0edda0..9979a5f 100644 --- a/src/pages/ExperiencePage.tsx +++ b/src/pages/ExperiencePage.tsx @@ -3,7 +3,12 @@ import YearListContainer from "../components/Experience/YearList"; import { AnimatePresence, motion } from "framer-motion"; import useComponentSize from "../components/hooks/useComponentSize"; import { useRecoilState } from "recoil"; -import { primeTagState, yearState } from "../store/selectedStore"; +import { + deleteTagState, + primeTagState, + subTagState, + yearState, +} from "../store/selectedStore"; import backgroundImg from "../assets/images/background.jpg"; import MainButton from "../components/common/MainButton"; import { Plus } from "../assets"; @@ -14,12 +19,16 @@ import Modal from "../components/common/Modal"; import React from "react"; import warningImg from "../assets/images/warningIcon.png"; import { getCookie } from "../services/cookie"; +import { deleteTag } from "../services/Experience/tagApi"; const ExperiencePage = () => { const user = getCookie("user"); const [componentRef, size] = useComponentSize(); const [selectedYear, setSelectedYear] = useRecoilState(yearState); const [selectedPrimeTag, setSelectedPrimeTag] = useRecoilState(primeTagState); + const [selectedSubTag, setSelectedSubTag] = useRecoilState(subTagState); + const [selectedDeleteTag, setSelectedDeleteTag] = + useRecoilState(deleteTagState); const navigate = useNavigate(); const [isModalOpen, setIsModalOpen] = React.useState(false); @@ -33,8 +42,16 @@ const ExperiencePage = () => { }; const handleDelete = () => { - console.log("삭제 api"); - closeDeleteModal(); + if (selectedDeleteTag && user?.token) { + deleteTag(selectedDeleteTag.id, user?.token).then((res) => { + if (selectedDeleteTag.id === selectedPrimeTag?.id) { + setSelectedPrimeTag({ id: "더보기", name: "더보기" }); + } else if (selectedDeleteTag.id === selectedSubTag?.id) { + setSelectedSubTag(null); + } + closeDeleteModal(); + }); + } }; /** @@ -76,7 +93,7 @@ const ExperiencePage = () => { transition={{ type: "spring", stiffness: 40 }} > {selectedPrimeTag && - (selectedPrimeTag.name === "더보기" ? ( + (selectedPrimeTag.id === "더보기" ? ( ) : ( diff --git a/src/services/Experience/tagApi.tsx b/src/services/Experience/tagApi.tsx index 2d06267..d376e72 100644 --- a/src/services/Experience/tagApi.tsx +++ b/src/services/Experience/tagApi.tsx @@ -52,31 +52,40 @@ export const postSubTag = async ( ); }; -// 연도 내 상위 태그 조회 -export const getYearPrimeTags = async ( +// 상위 태그 내 연도 목록 조회 +export const getPrimeTagYears = async (primeTagId: string, token: string) => { + return await client.get(`/api/tags/${primeTagId}/all-years`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); +}; + +// 상위 태그 내 하위 태그 목록 조회 +export const getPrimeTagSubTags = async ( year: number, - limit: number, + primeTagId: string, token: string ) => { - return await client.get(`/api/tags/top-rank?year=${year}&limit=${limit}`, { + return await client.get(`/api/tags/${primeTagId}?year=${year}`, { headers: { Authorization: `Bearer ${token}`, }, }); }; -// 상위 태그 내 연도 목록 조회 -export const getPrimeTagYears = async (primeTagId: string, token: string) => { - return await client.get(`/api/tags/${primeTagId}/all-years`, { +// 연도 내 전체 상위 태그 정보 조회 +export const getYearAllPrimeTags = async (year: number, token: string) => { + return await client.get(`/api/tags?year=${year}`, { headers: { Authorization: `Bearer ${token}`, }, }); }; -// 상위 태그 내 하위 태그 목록 조회 -export const getPrimeTagSubTags = async (year: number, primeTagId: string, token: string) => { - return await client.get(`/api/tags/${primeTagId}?year=${year}`, { +// 태그 삭제 +export const deleteTag = async (tagId: string, token: string) => { + return await client.delete(`/api/tags/${tagId}`, { headers: { Authorization: `Bearer ${token}`, }, diff --git a/src/store/selectedStore.ts b/src/store/selectedStore.ts index e921268..7424bfd 100644 --- a/src/store/selectedStore.ts +++ b/src/store/selectedStore.ts @@ -1,6 +1,6 @@ // countStore.ts import { atom } from "recoil"; -import { TagType } from "../types/experience"; +import { TagMenuType, TagType } from "../types/experience"; export const yearState = atom({ key: "yearState", @@ -12,7 +12,17 @@ export const primeTagState = atom({ default: null, }); +export const subTagState = atom({ + key: "subTagState", + default: null, +}); + export const deleteState = atom({ key: "deleteState", default: false, }); + +export const deleteTagState = atom({ + key: "deleteTagState", + default: null, +}); diff --git a/src/types/experience.ts b/src/types/experience.ts index df86d6b..97b1322 100644 --- a/src/types/experience.ts +++ b/src/types/experience.ts @@ -42,3 +42,10 @@ export interface YearData { year: number; tags: TagType[]; } + +export interface PrimeTagData { + id: string; + name: string; + strongPointCount: number; + experienceCount: number; +}