diff --git a/frontend/app/(WithNavbar)/pass-state/[generation]/page.tsx b/frontend/app/(WithNavbar)/pass-state/[generation]/page.tsx
new file mode 100644
index 00000000..6decf046
--- /dev/null
+++ b/frontend/app/(WithNavbar)/pass-state/[generation]/page.tsx
@@ -0,0 +1,61 @@
+import Txt from "@/components/common/Txt.component";
+import ApplicantsList from "@/components/passState/ApplicantsList";
+import { CURRENT_GENERATION } from "@/src/constants";
+import Image from "next/image";
+import Link from "next/link";
+
+interface PassStatePageProps {
+ searchParams: {
+ sortedBy?: "field";
+ };
+}
+
+const PassStatePage = ({ searchParams: { sortedBy } }: PassStatePageProps) => {
+ return (
+
+
+
+ {CURRENT_GENERATION}기 지원자 합격 상태 관리 페이지
+
+
+ {sortedBy === "field" && (
+
+
+ 현재 분야별로 정렬되어 있습니다. (WEB - APP - AI - GAME)
+
+
+
+ )}
+
+
+ 지원자 이름
+
+
+
+ 분야
+
+
+
+
+ 합격 상태
+
+
+
+
+
+
+
+ );
+};
+
+export default PassStatePage;
diff --git a/frontend/components/common/Txt.component.tsx b/frontend/components/common/Txt.component.tsx
index 4cb918a8..c33a0b40 100644
--- a/frontend/components/common/Txt.component.tsx
+++ b/frontend/components/common/Txt.component.tsx
@@ -18,6 +18,7 @@ const colorType = {
white: "text-white",
blue: "text-primary",
light_gray: "text-secondary-200",
+ red: "text-error",
};
interface TxtProps extends PropsWithChildren {
diff --git a/frontend/components/common/navbar/Navbar.tsx b/frontend/components/common/navbar/Navbar.tsx
index 903dc90f..b02c756c 100644
--- a/frontend/components/common/navbar/Navbar.tsx
+++ b/frontend/components/common/navbar/Navbar.tsx
@@ -6,6 +6,7 @@ import { NavbarOperation } from "./NavbarOperation";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { NavbarGenerationToggle } from "./NavbarGenerationToggle";
+import { NavbarManagePassState } from "./NavbarManagePassState";
const CommonNavbar = () => {
const [_, currentType, generation] = usePathname().split("/");
@@ -31,7 +32,8 @@ const CommonNavbar = () => {
/>
))}
-
+
+
diff --git a/frontend/components/common/navbar/NavbarManagePassState.tsx b/frontend/components/common/navbar/NavbarManagePassState.tsx
new file mode 100644
index 00000000..80290883
--- /dev/null
+++ b/frontend/components/common/navbar/NavbarManagePassState.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import { useQuery } from "@tanstack/react-query";
+import CommonNavbarCell from "./NavbarCell";
+import { getMyInfo } from "@/src/apis/interview";
+import { usePathname } from "next/navigation";
+
+type NavbarManagePassStateProps = {
+ isShort: boolean;
+ currentType: string;
+};
+export const NavbarManagePassState = ({
+ currentType,
+ isShort,
+}: NavbarManagePassStateProps) => {
+ const currentPath = usePathname();
+ const generation = currentPath.split("/")[2];
+ const { data: userData, isLoading } = useQuery(["user"], getMyInfo);
+ if (isLoading || !userData) {
+ return Loading...
;
+ }
+
+ if (userData.role !== "ROLE_OPERATION") {
+ return 관리자만 접근이 가능합니다.
;
+ }
+
+ return (
+
+ );
+};
diff --git a/frontend/components/common/navbar/NavbarOperation.tsx b/frontend/components/common/navbar/NavbarOperation.tsx
index 9b491e5e..d27374a2 100644
--- a/frontend/components/common/navbar/NavbarOperation.tsx
+++ b/frontend/components/common/navbar/NavbarOperation.tsx
@@ -7,8 +7,12 @@ import { usePathname } from "next/navigation";
type NavbarOperationProps = {
currentType: string;
+ isShort: boolean;
};
-export const NavbarOperation = ({ currentType }: NavbarOperationProps) => {
+export const NavbarOperation = ({
+ currentType,
+ isShort,
+}: NavbarOperationProps) => {
const currentPath = usePathname();
const generation = currentPath.split("/")[2];
const { data: userData } = useQuery(["user"], getMyInfo);
@@ -23,7 +27,7 @@ export const NavbarOperation = ({ currentType }: NavbarOperationProps) => {
return (
{
+ if (
+ passStateOrder[a.state.passState] !== passStateOrder[b.state.passState]
+ ) {
+ return (
+ passStateOrder[a.state.passState] - passStateOrder[b.state.passState]
+ );
+ }
+ return field1Order[a.field1] - field1Order[b.field1];
+ });
+}
+
+interface ApplicantsListProps {
+ sortedBy?: "position" | "field";
+}
+const ApplicantsList = ({ sortedBy }: ApplicantsListProps) => {
+ const selectedGeneration = usePathname().split("/")[2];
+ const queryClient = useQueryClient();
+
+ const {
+ data: allApplicants,
+ isLoading,
+ isError,
+ } = useQuery(["allApplicantsWithPassState", selectedGeneration], () =>
+ getAllApplicantsWithPassState(selectedGeneration)
+ );
+
+ const { mutate: updateApplicantPassState } = useMutation({
+ mutationFn: (params: PostApplicantPassStateParams) =>
+ postApplicantPassState(params),
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ["allApplicantsWithPassState", selectedGeneration],
+ });
+ },
+ });
+
+ {
+ if (+selectedGeneration !== CURRENT_GENERATION) {
+ return 현재 지원중인 기수만 확인 가능합니다.
;
+ }
+
+ if (isLoading) {
+ return Loading...
;
+ }
+
+ if (isError) {
+ return Error
;
+ }
+
+ if (!allApplicants) {
+ return 아직은 지원자가 없습니다 🥲
;
+ }
+ }
+
+ const onChangeApplicantsPassState = (
+ applicantName: string,
+ params: PostApplicantPassStateParams
+ ) => {
+ const ok = confirm(
+ `${applicantName}님을 ${params.afterState} 처리하시겠습니까?`
+ );
+ if (!ok) return;
+ updateApplicantPassState(params);
+ };
+
+ const applicants =
+ sortedBy === "field"
+ ? sortApplicantsByField1(allApplicants)
+ : allApplicants;
+
+ return (
+
+ {applicants.map(
+ ({ state: { passState }, field, field1, field2, id, name }) => (
+ -
+
+ {`[${field}] ${name}`}
+
+ {`${field1}/${field2}`}
+
+ {getApplicantPassState(passState)}
+
+
+
+
+
+
+ )
+ )}
+
+ );
+};
+
+export default ApplicantsList;
diff --git a/frontend/src/apis/passState/index.tsx b/frontend/src/apis/passState/index.tsx
new file mode 100644
index 00000000..dd7e2f1e
--- /dev/null
+++ b/frontend/src/apis/passState/index.tsx
@@ -0,0 +1,36 @@
+import { https } from "../../functions/axios";
+import { ApplicantPassState } from "../kanban";
+
+export interface Answer {
+ field: "개발자" | "디자이너" | "기획자";
+ field1: "APP" | "WEB" | "AI" | "GAME";
+ field2: "APP" | "WEB" | "AI" | "GAME" | "선택 없음";
+ name: string;
+ id: string;
+ year: number;
+ state: {
+ passState: ApplicantPassState;
+ };
+}
+
+export const getAllApplicantsWithPassState = async (generation: string) => {
+ // TODO: 머지 하기 전 주석 해제 및 목데이터 삭제
+ const { data } = await https.get(
+ `year/${generation}/applicants/pass-state?order=newest`
+ );
+
+ return data;
+};
+
+export interface PostApplicantPassStateParams {
+ applicantsId: string;
+ afterState: "non-pass" | "pass";
+}
+export const postApplicantPassState = async ({
+ afterState,
+ applicantsId,
+}: PostApplicantPassStateParams) => {
+ await https.post(
+ `/applicants/${applicantsId}/state?afterState=${afterState}`
+ );
+};
diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts
index 3176cdd6..394f705e 100644
--- a/frontend/src/constants/index.ts
+++ b/frontend/src/constants/index.ts
@@ -43,7 +43,8 @@ export interface NavbarItem {
| "applicant"
| "sharepoint"
| "admin"
- | "toggle";
+ | "toggle"
+ | "pass-state";
target?: "_blank" | "_self" | "_parent" | "_top";
href: string;
}
@@ -74,13 +75,13 @@ export const MainNavbar = (generation: number): NavbarItem[] => [
type: "applicant",
href: `/applicant/${generation}`,
},
- {
- title: "신입모집 쉐어 포인트",
- short_title: "쉐어포인트",
- type: "sharepoint",
- target: "_blank",
- href: "https://ejnu.sharepoint.com/sites/msteams_bbf640/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2Fmsteams_bbf640%2FShared%20Documents%2F2023%EB%85%84%2F1%ED%95%99%EA%B8%B0%2F%EC%8B%A0%EC%9E%85%EB%AA%A8%EC%A7%91&p=true&ga=1",
- },
+ // {
+ // title: "신입모집 쉐어 포인트",
+ // short_title: "쉐어포인트",
+ // type: "sharepoint",
+ // target: "_blank",
+ // href: "https://ejnu.sharepoint.com/sites/msteams_bbf640/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2Fmsteams_bbf640%2FShared%20Documents%2F2023%EB%85%84%2F1%ED%95%99%EA%B8%B0%2F%EC%8B%A0%EC%9E%85%EB%AA%A8%EC%A7%91&p=true&ga=1",
+ // },
];
export const APPLICANT_KEYS = [
diff --git a/frontend/src/functions/formatter.ts b/frontend/src/functions/formatter.ts
index d4db27b9..75d4dfb0 100644
--- a/frontend/src/functions/formatter.ts
+++ b/frontend/src/functions/formatter.ts
@@ -1,3 +1,4 @@
+import { ApplicantPassState } from "../apis/kanban";
import { Score, ScoreKeyword } from "../constants/applicant/28";
export const scoreListToObject = (
@@ -24,3 +25,14 @@ export const scoreObjectToList = (scores: Score[]) => {
[]
);
};
+
+export function getApplicantPassState(passState: ApplicantPassState) {
+ const passStateMap = {
+ "non-processed": "처리중",
+ "first-passed": "1차 합격",
+ "final-passed": "최종 합격",
+ "non-passed": "탈락",
+ };
+
+ return passStateMap[passState];
+}