diff --git a/public/splash1.png b/public/splash1.png new file mode 100644 index 0000000..3e7ae8c Binary files /dev/null and b/public/splash1.png differ diff --git a/src/app/(afterLogin)/_components/pageTitle.tsx b/src/app/(afterLogin)/_components/pageTitle.tsx index 89021b8..8eab46d 100644 --- a/src/app/(afterLogin)/_components/pageTitle.tsx +++ b/src/app/(afterLogin)/_components/pageTitle.tsx @@ -35,11 +35,11 @@ export default function PageTitle() { const message = () => { switch (currentSegment) { case "applications": - return "지원 현황"; + return "회사별 지원 진행 상황과 제출 문서를 정리해보세요"; case "documents": return "문서를 업로드하고, 버전별로 체계적으로 관리하세요"; case "schedule": - return "일정"; + return "지원일정을 한눈에 관리하세요"; default: return ""; } diff --git a/src/app/(afterLogin)/applications/page.tsx b/src/app/(afterLogin)/applications/page.tsx index 283facb..b8a5319 100644 --- a/src/app/(afterLogin)/applications/page.tsx +++ b/src/app/(afterLogin)/applications/page.tsx @@ -1,17 +1,20 @@ "use client"; import Link from "next/link"; -import { useState, useMemo } from "react"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; import { useFetchAllApplications, useUpdateApplication, + useDeleteApplication, } from "@/hooks/useApplications"; import Divider from "../documents/_components/divider"; import LoadingSpinner from "@/app/_components/loadingSpinner"; import SearchIcon from "@/assets/Search.svg"; import PlusIcon from "@/assets/Plus.svg"; import { CompanyApplicationWithId } from "@/type/applicationType"; -import DropDownButton from "./_components/dropDonwButton"; +import PencilSimpleIcon from "@/assets/PencilSimple.svg"; +import TrashSimpleIcon from "@/assets/TrashSimple.svg"; const formatDate = (dateString?: string | null) => { if (!dateString) return "-"; @@ -23,33 +26,88 @@ const formatDate = (dateString?: string | null) => { }; export default function ApplicationsPage() { + const router = useRouter(); const [page, setPage] = useState(0); const [searchQuery, setSearchQuery] = useState(""); + const [selectedIds, setSelectedIds] = useState([]); - const { data, isLoading, isError } = useFetchAllApplications( + const { data, isLoading, isError, refetch } = useFetchAllApplications( page, searchQuery ); - const { mutate } = useUpdateApplication(); - - const applications = data?.data.content; + const { mutate: updateMutate } = useUpdateApplication(); + const { mutateAsync: deleteMutateAsync } = useDeleteApplication(); + const applications = data?.data.content ?? []; const pageInfo = data?.data; + const filteredApplications = applications; + + useEffect(() => { + setSelectedIds([]); + }, [page, searchQuery]); - const filteredApplications = useMemo(() => { - if (!searchQuery) { - return applications; + const handleSelectAll = (e: React.ChangeEvent) => { + if (e.target.checked) { + const allIds = filteredApplications.map((app: CompanyApplicationWithId) => + app.id.toString() + ); + setSelectedIds(allIds); + } else { + setSelectedIds([]); } - return applications.filter((app: CompanyApplicationWithId) => - app.companyName.toLowerCase().includes(searchQuery.toLowerCase()) - ); - }, [searchQuery, applications]); + }; + const handleSelectOne = ( + e: React.ChangeEvent, + id: string + ) => { + if (e.target.checked) { + setSelectedIds((prev) => [...prev, id]); + } else { + setSelectedIds((prev) => prev.filter((selectedId) => selectedId !== id)); + } + }; + + const handleEdit = () => { + if (selectedIds.length !== 1) { + alert("수정할 항목을 하나만 선택해주세요."); + return; + } + router.push(`/applications/edit/${selectedIds[0]}`); + }; + + const handleDelete = async () => { + if (selectedIds.length === 0) { + alert("삭제할 항목을 선택해주세요."); + return; + } + if ( + window.confirm( + `선택된 ${selectedIds.length}개의 항목을 삭제하시겠습니까?` + ) + ) { + const deletePromises = selectedIds.map((id) => + deleteMutateAsync(Number(id)) + ); + + try { + await Promise.all(deletePromises); + + alert("선택된 항목이 모두 삭제되었습니다."); + setSelectedIds([]); + refetch(); + } catch (error) { + alert("일부 항목 삭제에 실패했습니다. 페이지를 새로고침합니다."); + refetch(); + } + } + }; const handlePrevPage = () => setPage((prev) => Math.max(prev - 1, 0)); const handleNextPage = () => - setPage((prev) => Math.min(prev + 1, pageInfo.totalPages - 1)); + setPage((prev) => + pageInfo ? Math.min(prev + 1, pageInfo.totalPages - 1) : prev + ); - // 특정 페이지 클릭 const handlePageClick = (i: number) => setPage(i); const th_style = "relative text-center p-4 text-sm font-medium"; @@ -60,7 +118,7 @@ export default function ApplicationsPage() { status: string ) => { const changedApp = { ...app, status }; - mutate( + updateMutate( { applicationId: app.id, changedApplication: changedApp }, { onError: () => { @@ -80,7 +138,7 @@ export default function ApplicationsPage() { return ( <> -
+
setSearchQuery(e.target.value)} - onKeyDown={(e) => e.key === "Enter"} />
- - 지원내역 등록 - + +
+ + + + + +
{isLoading && ( @@ -119,6 +199,17 @@ export default function ApplicationsPage() { + @@ -126,10 +217,10 @@ export default function ApplicationsPage() { 지역 - + {filteredApplications.length > 0 ? ( filteredApplications.map((app: CompanyApplicationWithId) => { - const resume = app.documents?.find( - (doc) => doc.type === "RESUME" - ); - const portfolio = app.documents?.find( - (doc) => doc.type === "PORTFOLIO" - ); const deadline = app.schedules?.find( (s: { title: string }) => s.title === "마감일" @@ -157,24 +242,51 @@ export default function ApplicationsPage() { return ( + ); }) ) : ( -
+ 0 && + selectedIds.length === filteredApplications.length + } + /> + + 회사명 - 이력서 + 연동 문서 - 포트폴리오 + 직무 마감일 @@ -137,18 +228,12 @@ export default function ApplicationsPage() { 상태 지원 날짜
+ + handleSelectOne(e, app.id.toString()) + } + /> + {app.companyName} {app.companyAddress || "-"} - {resume ? resume.title : "-"} +
+ {app.documents && app.documents.length > 0 ? ( + app.documents.map((doc) => ( +
+ {doc.title} +
+ )) + ) : ( +
-
+ )} +
- {portfolio ? portfolio.title : "-"} + {app.position ? app.position : "-"} {formatDate(deadline)} @@ -193,14 +305,16 @@ export default function ApplicationsPage() { - + {app.schedules + ? formatDate(app.schedules[0]?.dateTime) + : "-"}
+ {searchQuery ? "검색 결과가 없습니다." : "등록된 지원내역이 없습니다."} @@ -224,7 +338,7 @@ export default function ApplicationsPage() { key={i} onClick={() => handlePageClick(i)} className={`px-3 py-1 border rounded-md ${ - page === i ? "bg-main text-white" : "" + page === i ? "bg-orange-500 text-white" : "" }`} > {i + 1} diff --git a/src/app/(afterLogin)/documents/[documentId]/_components/documentDescription.tsx b/src/app/(afterLogin)/documents/[documentId]/_components/documentDescription.tsx index a392120..3623782 100644 --- a/src/app/(afterLogin)/documents/[documentId]/_components/documentDescription.tsx +++ b/src/app/(afterLogin)/documents/[documentId]/_components/documentDescription.tsx @@ -135,36 +135,41 @@ export default function DocumentDescription({

{activeDocument.title}

- {companies?.map((company) => { - const statusText = getStatusText(company.status as ApplicationStatus); - return ( -
-
-
-
-

- {company.companyName} -

- - {statusText} - + {companies.length === 0 ? ( + + 아직 연동된 회사가 없습니다. + + ) : ( + companies?.map((company) => { + const statusText = getStatusText( + company.status as ApplicationStatus + ); + return ( +
+
+
+
+

+ {company.companyName} +

+ + {statusText} + +
+ 주소: {company.companyAddress}
- 주소: {company.companyAddress} -
- -
-
- ); - })} - + +
+ + ); + }) + )}
@@ -209,7 +214,7 @@ export default function DocumentDescription({
- {/* {new Date(documentVersion.lastModifiedDate).toLocaleDateString()} */} + {new Date(documentVersion.createdDate!).toLocaleDateString()}
- - - - Upload -
); diff --git a/src/app/(afterLogin)/documents/page.tsx b/src/app/(afterLogin)/documents/page.tsx index 52583f0..6e4adfa 100644 --- a/src/app/(afterLogin)/documents/page.tsx +++ b/src/app/(afterLogin)/documents/page.tsx @@ -43,6 +43,12 @@ export default function DocumentsPage() { } }, [isAdding]); + useEffect(() => { + if (!isLoading && documents.length === 0) { + setIsAdding(true); + } + }, [isLoading, documents]); + const handleRoute = (doc: DocumentTypeWithId) => { useDocumentStore.setState({ document: doc }); router.push(`/documents/${doc.id}`); @@ -50,8 +56,10 @@ export default function DocumentsPage() { const handleBlur = () => { if (newDocumentTitle === "") { - setIsAdding(false); - setNewDocumentTitle(""); + if (documents.length > 0) { + setIsAdding(false); + setNewDocumentTitle(""); + } } }; @@ -73,8 +81,10 @@ export default function DocumentsPage() { }; const handleCancelAdd = () => { - setIsAdding(false); - setNewDocumentTitle(""); + if (documents.length > 0) { + setIsAdding(false); + setNewDocumentTitle(""); + } }; const th_style = "relative text-center p-4 font-medium"; @@ -96,7 +106,7 @@ export default function DocumentsPage() { 에러가 발생하였습니다.
잠시 후 시도해주세요. )} - {data && ( + {data && !isLoading && ( @@ -119,19 +129,10 @@ export default function DocumentsPage() { - {documents.map((doc: DocumentTypeWithId, index: number) => { + {documents.map((doc: DocumentTypeWithId) => { return ( - + + + ) : ( + + + + + + + )} diff --git a/src/app/_components/alertBubble1.tsx b/src/app/_components/alertBubble1.tsx new file mode 100644 index 0000000..5bb266c --- /dev/null +++ b/src/app/_components/alertBubble1.tsx @@ -0,0 +1,21 @@ +import Building from "@/assets/Building.svg"; + +export default function AlertBubble1() { + return ( +
+
+
+
+ +
+

Jobnote

+
+
내 커리어 관리의 시작
+
+ +

+ 이력서 등록이 완료되었습니다! +

+
+ ); +} diff --git a/src/app/_components/alertBubble2.tsx b/src/app/_components/alertBubble2.tsx new file mode 100644 index 0000000..d6445fe --- /dev/null +++ b/src/app/_components/alertBubble2.tsx @@ -0,0 +1,23 @@ +import Building from "@/assets/Building.svg"; + +export default function AlertBubble2() { + return ( +
+
+
+
+ +
+

+ Jobnote +

+
+
+ 내 커리어 관리의 시작 +
+
+ +

D-2 최종면접이 얼마 남지 않았어요!

+
+ ); +} diff --git a/src/app/_components/companyBubble.tsx b/src/app/_components/companyBubble.tsx new file mode 100644 index 0000000..bdcac58 --- /dev/null +++ b/src/app/_components/companyBubble.tsx @@ -0,0 +1,18 @@ +import FiSrCommentsIcon from "@/assets/fi-sr-comments.svg"; + +export default function CompanyBubble() { + return ( +
+
+
+ +
+

회사

+
+ +

+ [최종합격] 안녕하세요 저희 회사에 지원해주셔서 +

+
+ ); +} diff --git a/src/app/_components/loadingSpinner.tsx b/src/app/_components/loadingSpinner.tsx index 6c9b36d..899d0ee 100644 --- a/src/app/_components/loadingSpinner.tsx +++ b/src/app/_components/loadingSpinner.tsx @@ -1,6 +1,6 @@ export default function LoadingSpinner() { return ( -
+
{/* 전체동의 */} -
+
-
{/* (필수) 서비스 이용약관 동의 */}
-
- - -
+ + +
- {index === documents.length - 1 && !isAdding && ( - - )} -
@@ -211,16 +212,34 @@ export default function DocumentsPage() { > 저장 + {documents.length > 0 && ( + + )} +