diff --git a/src/App.tsx b/src/App.tsx index e44f77f..092af36 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -34,7 +34,7 @@ const App: React.FC = () => { {/* 공고 상세 / JDDetailPage */} } path="/"> - } /> + } /> {/* 공고 수정 / JDEditPage*/} } path="/"> diff --git a/src/components/JD/Announcement.tsx b/src/components/JD/Announcement.tsx index b0bb683..dc20760 100644 --- a/src/components/JD/Announcement.tsx +++ b/src/components/JD/Announcement.tsx @@ -36,6 +36,7 @@ const JobAnnouncementCard: React.FC = ({ const Container = styled.div` border-radius: 0.75rem; + cursor: pointer; display: flex; padding: 1.25rem; flex-direction: column; diff --git a/src/components/JD/ExperienceList.tsx b/src/components/JD/ExperienceList.tsx index 08dcd30..4a3e2f7 100644 --- a/src/components/JD/ExperienceList.tsx +++ b/src/components/JD/ExperienceList.tsx @@ -14,9 +14,15 @@ import PopperPagination from "../Experience/PopperPagination"; import { basicKeywords } from "../../assets/data/keywords"; import { myKeywords } from "../../services/Experience/myKeywords"; import Checkbox from "../common/Checkbox"; -import { getAllExperienceList } from "../../services/JD/ExperienceApi"; +import { + getAllExperienceList, + searchTagExperienceList, + searchTextExperienceList, +} from "../../services/JD/ExperienceApi"; import { getCookie } from "../../services/cookie"; import { getAllTags } from "../../services/JD/tagApi"; +import { useParams } from "react-router-dom"; +import { formatDateRange } from "../../pages/JDListPage"; type TabType = "basic" | "my"; @@ -24,6 +30,35 @@ interface ExperienceListProps { showBookmarksOnly: boolean; } +type StrongPointAPI = { + id: string; + name: string; +}; + +type ExpTagAPI = { + id: string; + name: string; +}; + +type ContentAPI = { + question: string; + answer: string; +}; + +type ExperienceAPI = { + id: string; + title: string; + parentTag: ExpTagAPI; + childTag: ExpTagAPI; + strongPoints: StrongPointAPI[]; + contents: ContentAPI[]; + startedAt: string; + endedAt: string; + bookmarked: "ON" | "OFF"; +}; + +type Experiences = ExperienceAPI[]; + const ExperienceList: React.FC = ({ showBookmarksOnly, }) => { @@ -36,11 +71,117 @@ const ExperienceList: React.FC = ({ const [mainTag, setMainTag] = useState(""); // 선택된 상위태그 const [subTag, setSubTag] = useState(""); //선택된 하위태그 const [filterCount, setfilterCount] = useState(-1); //검색된 경험의 숫자, 검색 안된 상태에서는 -1 - const bookmarkData = ExpData.filter((post) => post.bookmark); // 북마크된 데이터들 const [keywordTabOption, setKeywordTabOption] = React.useState("basic"); const user = getCookie("user"); - const [experienceData, setExperienceData] = useState({}); + const [experienceData, setExperienceData] = useState([ + { + id: "fa0a5813-c879-432d-b276-24364847534c", + title: "경험 제목1 ", + parentTag: { + id: "c191d753-0c59-42eb-8245-79ee5c9c5797", + name: "상위 태그 이름", + }, + childTag: { + id: "860c446b-a021-43d5-9da6-5034a5bdaee7", + name: "하위 태그 이름", + }, + strongPoints: [ + { + id: "fdbf03bf-c1a3-4442-997e-467605868052", + name: "역량 키워드 이름 1", + }, + { + id: "096c3d2e-4073-4724-9a15-c1d6617c63a1", + name: "역량 키워드 이름 2", + }, + ], + contents: [ + { + question: "질문1", + answer: "답변1", + }, + { + question: "질문2", + answer: "답변2", + }, + ], + startedAt: "2024-05-22T07:45:23.720822702", + endedAt: "2024-05-23T07:45:23.720832019", + bookmarked: "ON", + }, + { + id: "7694c6e7-b7a8-4ee8-a698-67c345932663", + title: "경험 제목 2", + parentTag: { + id: "c191d753-0c59-42eb-8245-79ee5c9c5797", + name: "상위 태그 이름", + }, + childTag: { + id: "860c446b-a021-43d5-9da6-5034a5bdaee7", + name: "하위 태그 이름", + }, + strongPoints: [ + { + id: "fdbf03bf-c1a3-4442-997e-467605868052", + name: "역량 키워드 이름 1", + }, + { + id: "096c3d2e-4073-4724-9a15-c1d6617c63a1", + name: "역량 키워드 이름 2", + }, + ], + contents: [ + { + question: "질문1", + answer: "답변1", + }, + { + question: "질문2", + answer: "답변2", + }, + ], + startedAt: "2023-05-22T07:45:23.720822702", + endedAt: "2024-05-23T07:45:23.720832019", + bookmarked: "OFF", + }, + { + id: "7694c6e7-b7a8-4ee8-a698-67c345932663", + title: "경험 제목 3", + parentTag: { + id: "c191d753-0c59-42eb-8245-79ee5c9c5797", + name: "상위 태그 이름", + }, + childTag: { + id: "860c446b-a021-43d5-9da6-5034a5bdaee7", + name: "하위 태그 이름", + }, + strongPoints: [ + { + id: "fdbf03bf-c1a3-4442-997e-467605868052", + name: "역량 키워드 이름 1", + }, + { + id: "096c3d2e-4073-4724-9a15-c1d6617c63a1", + name: "역량 키워드 이름 2", + }, + ], + contents: [ + { + question: "질문1", + answer: "답변1", + }, + { + question: "질문2", + answer: "답변2", + }, + ], + startedAt: "2023-05-22T07:45:23.720822702", + endedAt: "2024-05-23T07:45:23.720832019", + bookmarked: "ON", + }, + ]); + const jdId = useParams().jdId; const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); @@ -67,13 +208,51 @@ const ExperienceList: React.FC = ({ ); useEffect(() => { - // getExperienceList(user.token); + if (jdId) { + // getExperienceList(jdId, user.token); + } }, []); //모든 경험리스트 불러오기 - const getExperienceList = async (token: string) => { + const getExperienceList = async (jdId: string, token: string) => { try { - const response = await getAllExperienceList(token); + const response = await getAllExperienceList(jdId, token); + console.log(response); + } catch (error) { + console.error(error); + alert(JSON.stringify(error)); + } + }; + + const getSearchedExperienceList = async ( + jdId: string, + searchText: string, + token: string + ) => { + // try { + // const response = await searchTextExperienceList(jdId, searchText, token); + // console.log(response); + // setExperienceData(response.data.experiences); + // console.log(experienceData); + // } catch (error) { + // console.error(error); + // alert(JSON.stringify(error)); + // } + }; + + const getFilteredExperienceList = async ( + jdId: string, + parenttag: string, + childtag: string | null, + token: string + ) => { + try { + const response = await searchTagExperienceList( + jdId, + parenttag, + childtag, + token + ); console.log(response); } catch (error) { console.error(error); @@ -101,12 +280,24 @@ const ExperienceList: React.FC = ({ }; // 역량 키워드 필터된 경험 데이터, 북마크 - const filteredExpData = ExpData.filter((item) => - item.tags.some((tag) => checkedKeywords.includes(tag)) - ); - const filteredbookedData = bookmarkData.filter((item) => - item.tags.some((tag) => checkedKeywords.includes(tag)) - ); + const filteredExpData = experienceData.filter((experience) => { + const strongPointNames = experience.strongPoints.map((point) => point.name); + const matchedKeywords = strongPointNames.filter((name) => + checkedKeywords.includes(name) + ); + return matchedKeywords.length > 0; + }); + const bookedData = experienceData.filter((experience) => { + // 북마크가 ON인 요소만 필터링 + return experience.bookmarked === "ON"; + }); + const filteredBookedData = experienceData.filter((experience) => { + const strongPointNames = experience.strongPoints.map((point) => point.name); + const matchedKeywords = strongPointNames.filter((name) => + checkedKeywords.includes(name) + ); + return experience.bookmarked === "ON" && matchedKeywords.length > 0; + }); //상위태그 하위태그 필터링 const handleTagSelection = ( @@ -126,12 +317,18 @@ const ExperienceList: React.FC = ({ useEffect(() => { if (mainTag) { console.log("maintag: " + mainTag); + if (jdId) { + getFilteredExperienceList(jdId, mainTag, null, user.token); + } } }, [mainTag]); useEffect(() => { if (subTag) { console.log("subtag: " + subTag); + if (jdId) { + getFilteredExperienceList(jdId, mainTag, subTag, user.token); + } } }, [subTag]); @@ -196,7 +393,14 @@ const ExperienceList: React.FC = ({ setSubTag(""); }} /> - filter {/* 클릭시 검색 api 호출 */} + filter { + getSearchedExperienceList(jdId!, searchText, user.token); + }} + />{" "} + {/* 클릭시 검색 api 호출 */} {showTagPopup && }
@@ -299,39 +503,40 @@ const ExperienceList: React.FC = ({ {selectedTab === "경험검색" ? ( <> - {(checkedKeywords.length === 0 ? ExpData : filteredExpData).map( - (post, index: number) => ( - setshowDetail(true)} - /> - ) - )} + {(checkedKeywords.length === 0 + ? experienceData + : filteredExpData + ).map((post, index: number) => ( + point.name)} + period={formatDateRange(post.startedAt, post.endedAt)} + bookmark={post.bookmarked === "ON" ? true : false} + checkedKeywords={checkedKeywords} + onClick={() => setshowDetail(true)} + /> + ))} ) : ( {(checkedKeywords.length === 0 - ? bookmarkData - : filteredbookedData + ? bookedData + : filteredBookedData ).map((post, index: number) => ( point.name)} + period={formatDateRange(post.startedAt, post.endedAt)} + bookmark={post.bookmarked === "ON" ? true : false} checkedKeywords={checkedKeywords} onClick={() => setshowDetail(true)} /> diff --git a/src/components/JD/JDContainer.tsx b/src/components/JD/JDContainer.tsx index 1a3007b..005d09f 100644 --- a/src/components/JD/JDContainer.tsx +++ b/src/components/JD/JDContainer.tsx @@ -11,10 +11,12 @@ import { formatDate } from "../../pages/JDDetailPage"; interface JobContainerProps { jdId: string; token: string; + status: string; } -const JDContainer: React.FC = ({ jdId, token }) => { +const JDContainer: React.FC = ({ jdId, token, status }) => { // const jdData = jobDetails[1]; + const [jdState, setjdState] = useState(""); const [jdData, setJdData] = useState({ enterpriseName: "", title: "", @@ -43,6 +45,7 @@ const JDContainer: React.FC = ({ jdId, token }) => { endedAt: response.data.endedAt, }; setJdData(jdApiData); + setjdState(jdData.writeStatus); } catch (error) { console.error(error); alert(JSON.stringify(error)); @@ -53,10 +56,21 @@ const JDContainer: React.FC = ({ jdId, token }) => { getJobData(jdId, token); }, []); + useEffect(() => { + if (status === "작성완료") { + setjdState("WRITTEN"); + } else if (status === "작성중") { + setjdState("WRITING"); + } + }, [status]); + return ( - +
{jdData.createdAt}
diff --git a/src/components/JD/JDModal.tsx b/src/components/JD/JDModal.tsx index 266aa1a..39a2fcd 100644 --- a/src/components/JD/JDModal.tsx +++ b/src/components/JD/JDModal.tsx @@ -1,6 +1,6 @@ import React, { FC } from "react"; import styled from "styled-components"; -import warning from "../../assets/icons/icon_warning.svg"; +import warning from "../../assets/icons/icon_delete_warning.svg"; import { useNavigate } from "react-router-dom"; interface ModalProps { @@ -20,12 +20,7 @@ const Modal: FC = ({ isOpen, onClose }) => { warning
이전 페이지로 돌아가시겠어요?
-
- 저장하지 않으면 -
- 입력한 내용은 복원할 수 없어요! -
-
+
작성하던 공고가 저장되지 않아요!
@@ -85,7 +80,7 @@ const MainWrapper = styled.div` font-weight: 600; line-height: 1.625rem; letter-spacing: -0.055rem; - margin-top: 1.25rem; + margin-top: 1rem; } .subtext { color: var(--neutral-500, #A6AAC0); @@ -95,7 +90,7 @@ const MainWrapper = styled.div` font-weight: 500; line-height: 1.25rem; letter-spacing: -0.02rem; - margin-top: 1.25rem; + margin-bottom: 1rem; span{ color: var(--main-500, #7D82FF); font-size: 1.2rem; diff --git a/src/pages/ApplyEditPage.tsx b/src/pages/ApplyEditPage.tsx index 0529054..454c910 100644 --- a/src/pages/ApplyEditPage.tsx +++ b/src/pages/ApplyEditPage.tsx @@ -398,7 +398,13 @@ const ApplyEditPage: React.FC = () => { ) : ( - {jdId ? : null} + {jdId ? ( + + ) : null} )} @@ -646,6 +652,7 @@ const QuestionsWrapper = styled.div` display: flex; align-items: center; justify-content: center; + cursor: pointer; } //overflow-y: scroll; `; diff --git a/src/pages/ApplyPage.tsx b/src/pages/ApplyPage.tsx index e94a04b..a1e10c3 100644 --- a/src/pages/ApplyPage.tsx +++ b/src/pages/ApplyPage.tsx @@ -27,7 +27,7 @@ const ApplyPage: React.FC = () => { { question: "", answer: "" }, ]); //문항 데이터 const [editing, setEditing] = useState(true); //수정중 여부 - const [completed, setCompleted] = useState(false); //작성 완료 + const [completed, setCompleted] = useState(""); //작성 완료 const [isAllFilled, setIsAllFilled] = useState(false); // 문항이 빈칸이 없는지 검사 const [detailId, setDetailId] = useRecoilState(detailStore); //경험의 고유 id(0이 아니여야함) @@ -223,13 +223,7 @@ const ApplyPage: React.FC = () => { - - 작성완료 - (!editing ? setCompleted(!completed) : null)} - /> - + {editing ? ( { > 저장 + ) : completed === "작성완료" ? ( + + 수정 + ) : ( - + 수정 )} @@ -332,7 +330,13 @@ const ApplyPage: React.FC = () => { ) : ( - {jdId ? : null} + {jdId ? ( + + ) : null} )} @@ -420,6 +424,7 @@ const TextCountWrapper = styled.div` const SaveButton = styled.button<{ isNotNull: boolean }>` display: inline-flex; padding: 0.625rem 4rem; + cursor: pointer; justify-content: center; align-items: center; color: #fff; @@ -552,6 +557,7 @@ const JDButton = styled.button` top: 1rem; width: 2rem; height: 7rem; + cursor: pointer; flex-shrink: 0; border: none; border-radius: 0.66019rem 0rem 0rem 0.66019rem; @@ -564,6 +570,7 @@ const ExperienceButton = styled.button` left: -2rem; top: 8.5rem; width: 2rem; + cursor: pointer; height: 7rem; flex-shrink: 0; border: none; @@ -580,6 +587,7 @@ const QuestionsWrapper = styled.div` display: flex; align-items: center; justify-content: center; + cursor: pointer; } //overflow-y: scroll; `; diff --git a/src/pages/JDDetailPage.tsx b/src/pages/JDDetailPage.tsx index 6e65ea2..e794fe6 100644 --- a/src/pages/JDDetailPage.tsx +++ b/src/pages/JDDetailPage.tsx @@ -22,7 +22,7 @@ import JDDeleteModal from "../components/JD/JDDeleteModal"; const JDDetailPage: React.FC = () => { const [active, setActive] = useState(false); const [activebutton, setActivebutton] = useState(""); - const jdId = useParams().id; + const jdId = useParams().jdId; const nav = useNavigate(); const [detailId, setDetailId] = useRecoilState(detailStore); const [jdData, setJdData] = useState({ @@ -190,7 +190,9 @@ const JDDetailPage: React.FC = () => { {jdData.writeStatus !== "CLOSED" && (
- {"D-" + jdData.remainingDate} + {parseInt(jdData.remainingDate) <= 0 + ? "D-DAY" + : "D-" + jdData.remainingDate}
)}
{jdData.title}
@@ -327,6 +329,7 @@ const Title = styled.h1` const TopButton = styled.button` display: inline-flex; + cursor: pointer; padding: 0.5rem 2.75rem; align-items: flex-start; gap: 0.625rem; @@ -410,6 +413,7 @@ const JobStatusBar = styled.div` .left_container{ display: flex; flex-direction: row; + cursor: pointer; align-items: center; .job_date { color: ${(props) => props.theme.colors.neutral500}; @@ -420,6 +424,7 @@ const JobStatusBar = styled.div` .right_container{ display: flex; flex-direction: row; + cursor: pointer; align-items: center; justify-content: center; color: ${(props) => props.theme.colors.neutral500}; @@ -549,6 +554,7 @@ const ExperienceButton = styled.button` width: 2rem; height: 7rem; flex-shrink: 0; + cursor: pointer; border: none; border-radius: 0.66019rem 0rem 0rem 0.66019rem; background: var(--neutral-300, #eaebf3); diff --git a/src/services/JD/ExperienceApi.tsx b/src/services/JD/ExperienceApi.tsx index 83a2325..423ddcc 100644 --- a/src/services/JD/ExperienceApi.tsx +++ b/src/services/JD/ExperienceApi.tsx @@ -1,10 +1,54 @@ import client from "../client"; -// 경험 목록 조회 -export const getAllExperienceList = async (token: string) => { - return await client.get(`/api/experiences`, { +// 북마크 된 경험 목록 조회 +export const getAllExperienceList = async (jdId: string, token: string) => { + return await client.get(`/api/experiences/bookmark/${jdId}`, { headers: { Authorization: `Bearer ${token}`, }, }); }; + +// 경험 목록 검색 - 텍스트 검색 +export const searchTextExperienceList = async ( + jdId: string, + searchText: string, + token: string +) => { + return await client.get( + `/api/experiences/bookmark/${jdId}?search=${searchText}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); +}; + +// 경험 목록 검색 - 태그필터링 +export const searchTagExperienceList = async ( + jdId: string, + parentTag: string, + childTag: string | null, + token: string +) => { + if (childTag) { + return await client.get( + `/api/experiences/bookmark/${jdId}?parent-tag=${parentTag}&child-tag=${childTag}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + } else { + return await client.get( + `/api/experiences/bookmark/${jdId}?parent-tag=${parentTag}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + } +};