diff --git a/frontend/app/(WithNavbar)/layout.tsx b/frontend/app/(WithNavbar)/layout.tsx
index 843e8ecd..387af465 100644
--- a/frontend/app/(WithNavbar)/layout.tsx
+++ b/frontend/app/(WithNavbar)/layout.tsx
@@ -1,22 +1,12 @@
import CommonNavbar from "@/components/common/navbar/Navbar";
-import { PropsWithChildren } from "react";
-import { headers } from "next/headers";
+import { type PropsWithChildren } from "react";
interface WithNavbarLayout extends PropsWithChildren {}
const ApplicantPage = ({ children }: WithNavbarLayout) => {
- const headersList = headers();
- const header_url = headersList.get("x-url") || "";
- const [_, __, currentPath, generation, ___] = header_url.split(/[/?]+/);
- const isShort = currentPath === "kanban";
-
return (
-
+
{children}
);
diff --git a/frontend/app/kanban/[generation]/detail/page.tsx b/frontend/app/kanban/[generation]/detail/page.tsx
index 78a9e9f2..b4b909b0 100644
--- a/frontend/app/kanban/[generation]/detail/page.tsx
+++ b/frontend/app/kanban/[generation]/detail/page.tsx
@@ -47,12 +47,11 @@ const DetailContentJunction = ({
const KanbanBoardDetailPage = ({
params: { generation },
- searchParams: { columnIndex, applicantId, type, cardId },
+ searchParams: { applicantId, type, cardId },
}: KanbanBoardDetailPageProps) => {
return (
{
+ const [_, currentType, generation] = usePathname().split("/");
+ const isShort = currentType === "kanban";
-const CommonNavbar = ({
- generation,
- isShort = false,
- currentPath,
-}: CommonNavbarProps) => {
return (
diff --git a/frontend/components/common/navbar/NavbarCell.tsx b/frontend/components/common/navbar/NavbarCell.tsx
index 064ee556..8d4a2d1a 100644
--- a/frontend/components/common/navbar/NavbarCell.tsx
+++ b/frontend/components/common/navbar/NavbarCell.tsx
@@ -1,43 +1,44 @@
+"use client";
+
import Image from "next/image";
import LtIcon from "@/public/icons/lt.icon.svg";
import LtIconWhite from "@/public/icons/lt.icon.white.svg";
+import Link from "next/link";
+import { cn } from "@/src/utils/cn";
+import { NavbarItem } from "@/src/constants";
-type CommonNavbarCellProps = {
- currentPath: string;
- item: {
- type: string;
- href: string;
- target: string;
- short_title: string;
- title: string;
- };
+export type CommonNavbarCellProps = NavbarItem & {
+ currentType: string;
isShort: boolean;
};
const CommonNavbarCell = ({
- currentPath,
- item,
+ href,
+ short_title,
+ target,
+ title,
+ type,
+ currentType,
isShort,
}: CommonNavbarCellProps) => {
const linkButtonClassName =
"flex justify-between p-4 hover:bg-secondary-100 hover:text-white rounded-lg";
+
return (
-
- {isShort ? item.short_title : item.title}
+ {isShort ? short_title : title}
-
+
);
};
diff --git a/frontend/components/common/navbar/NavbarGenerationToggle.tsx b/frontend/components/common/navbar/NavbarGenerationToggle.tsx
new file mode 100644
index 00000000..f31b6ff3
--- /dev/null
+++ b/frontend/components/common/navbar/NavbarGenerationToggle.tsx
@@ -0,0 +1,32 @@
+import CommonNavbarCell from "./NavbarCell";
+import { CURRENT_GENERATION } from "@/src/constants";
+
+type NavbarGenerationToggleProps = {
+ generation: string;
+ isShort: boolean;
+};
+export const NavbarGenerationToggle = ({
+ generation,
+ isShort,
+}: NavbarGenerationToggleProps) => {
+ const isCurrentGeneration = +generation === CURRENT_GENERATION;
+ const targetGeneration = isCurrentGeneration
+ ? CURRENT_GENERATION - 1
+ : CURRENT_GENERATION;
+
+ const short_title = isCurrentGeneration ? "지난 모집" : "현재 모집";
+ const title = isCurrentGeneration
+ ? "지난 신입모집 보기"
+ : "현재 신입모집 보기";
+
+ return (
+
+ );
+};
diff --git a/frontend/components/common/navbar/NavbarOperation.tsx b/frontend/components/common/navbar/NavbarOperation.tsx
index 2b296545..9b491e5e 100644
--- a/frontend/components/common/navbar/NavbarOperation.tsx
+++ b/frontend/components/common/navbar/NavbarOperation.tsx
@@ -3,18 +3,14 @@
import { useQuery } from "@tanstack/react-query";
import CommonNavbarCell from "./NavbarCell";
import { getMyInfo } from "@/src/apis/interview";
+import { usePathname } from "next/navigation";
-interface NavbarOperationProps {
- generation: string;
- isShort?: boolean;
- currentPath: string;
-}
-
-export const NavbarOperation = ({
- generation,
- isShort = false,
- currentPath,
-}: NavbarOperationProps) => {
+type NavbarOperationProps = {
+ currentType: string;
+};
+export const NavbarOperation = ({ currentType }: NavbarOperationProps) => {
+ const currentPath = usePathname();
+ const generation = currentPath.split("/")[2];
const { data: userData } = useQuery(["user"], getMyInfo);
if (!userData) {
return ;
@@ -26,15 +22,13 @@ export const NavbarOperation = ({
return (
);
};
diff --git a/frontend/components/kanban/column/ColumnWithBackButton.component.tsx b/frontend/components/kanban/column/ColumnWithBackButton.component.tsx
index 850326ea..094f48b6 100644
--- a/frontend/components/kanban/column/ColumnWithBackButton.component.tsx
+++ b/frontend/components/kanban/column/ColumnWithBackButton.component.tsx
@@ -1,28 +1,30 @@
"use client";
import { KanbanColumnData } from "@/src/stores/kanban/Kanban.atoms";
-import { useAtom } from "jotai";
+import { useAtomValue } from "jotai";
import { useQuery } from "@tanstack/react-query";
import { getAllKanbanData } from "@/src/apis/kanban";
import { KanbanSelectedButtonNumberState } from "@/src/stores/kanban/Navbar.atoms";
import KanbanDetailBackButton from "../BackButton.component";
import KanbanCardComponent from "../card/Card.component";
import Icon from "@/components/common/Icon";
+import { useSearchParams } from "next/navigation";
interface KanbanDetailCardProps {
- columnIndex: number;
generation: string;
cardId: string;
applicantId: string;
}
const KanbanColumnDetailCard = ({
- columnIndex,
generation,
cardId,
applicantId,
}: KanbanDetailCardProps) => {
- const [navbarId] = useAtom(KanbanSelectedButtonNumberState);
+ const searchParams = useSearchParams();
+ const columnIndex = +(searchParams.get("columnIndex") ?? "0");
+
+ const navbarId = useAtomValue(KanbanSelectedButtonNumberState);
const {
data: kanbanDataArray,
diff --git a/frontend/cypress/e2e/application.second-personal-information.cy.ts b/frontend/cypress/e2e/application.second-personal-information.cy.ts
new file mode 100644
index 00000000..61060a18
--- /dev/null
+++ b/frontend/cypress/e2e/application.second-personal-information.cy.ts
@@ -0,0 +1,69 @@
+describe("2번째 인적사항 e2e 테스트", () => {
+ beforeEach(() => {
+ cy.viewport(1200, 900);
+
+ cy.goSecondPersonalInformation();
+
+ cy.get("span")
+ .filter((index, element) => Cypress.$(element).text().trim() === "전공*")
+ .parent()
+ .next("input")
+ .as("major");
+
+ cy.get("span")
+ .filter(
+ (index, element) => Cypress.$(element).text().trim() === "복수전공"
+ )
+ .parent()
+ .next("input")
+ .as("revengeMajor");
+
+ cy.get("span")
+ .filter((index, element) => Cypress.$(element).text().trim() === "부전공")
+ .parent()
+ .next("input")
+ .as("minor");
+
+ cy.get("button").contains("다음").as("nextButton");
+ });
+
+ it("전공 입력 후 다음 버튼 클릭하면 기타 질문 사항으로 이동", () => {
+ cy.get("@major").type("컴퓨터정보통신공학과");
+ cy.get("@nextButton").click();
+ });
+
+ it("전공, 복수전공 입력 후 다음 버튼 클릭하면 기타 질문 사항으로 이동", () => {
+ cy.get("@major").type("컴퓨터정보통신공학과");
+ cy.get("@revengeMajor").type("건축학과");
+ cy.get("@nextButton").click();
+ });
+
+ it("전공, 부전공 입력 후 다음 버튼 클릭하면 기타 질문 사항으로 이동", () => {
+ cy.get("@major").type("컴퓨터정보통신공학과");
+ cy.get("@minor").type("물리학과");
+ cy.get("@nextButton").click();
+ });
+
+ it("전공, 복수전공, 부전공 입력 후 다음 버튼 클릭하면 기타 질문 사항으로 이동", () => {
+ cy.get("@major").type("컴퓨터정보통신공학과");
+ cy.get("@revengeMajor").type("건축학과");
+ cy.get("@minor").type("물리학과");
+ cy.get("@nextButton").click();
+ });
+
+ it("아무것도 입력하지 않고 다음 버튼 클릭하면 '필수 질문을 작성해주세요.'라는 alert창이 보인다.", () => {
+ cy.get("@nextButton").click();
+ cy.on("window:alert", (text) => {
+ console.log("Alert message:", text);
+ });
+ });
+
+ it("전공을 입력하지 않고 복수전공, 부전공을 입력 후 다음 버튼 클릭하면 '필수 질문을 작성해주세요.'라는 alert창이 보인다.", () => {
+ cy.get("@revengeMajor").type("건축학과");
+ cy.get("@minor").type("물리학과");
+ cy.get("@nextButton").click();
+ cy.on("window:alert", (text) => {
+ console.log("Alert message:", text);
+ });
+ });
+});
diff --git a/frontend/cypress/support/commands.ts b/frontend/cypress/support/commands.ts
index 1defec0a..1fe34286 100644
--- a/frontend/cypress/support/commands.ts
+++ b/frontend/cypress/support/commands.ts
@@ -1,9 +1,10 @@
///
declare namespace Cypress {
- interface Chainable {
+ interface Chainable {
checkLocalStorage(key: string, expectedValue: string): Chainable;
checkAlert(expectedValue: string): Chainable;
+ goSecondPersonalInformation(): Chainable;
}
}
@@ -22,3 +23,53 @@ Cypress.Commands.add("checkAlert", (expectedValue: string) => {
expect(str).to.equal(expectedValue);
});
});
+
+Cypress.Commands.add("goSecondPersonalInformation", () => {
+ cy.clearAllLocalStorage();
+ cy.visit("http://localhost:3000/application");
+ cy.get("label").contains("개발자").should("exist").click();
+ cy.get("span")
+ .contains("1순위")
+ .next()
+ .contains("label", "APP")
+ .should("exist")
+ .click();
+ cy.get("span")
+ .filter((index, element) => Cypress.$(element).text().trim() === "2순위")
+ .next()
+ .contains("label", "WEB")
+ .should("exist")
+ .click();
+ cy.get("button").contains("다음").should("exist").click();
+ cy.get("span")
+ .contains("이름")
+ .parent()
+ .next()
+ .type("심민보")
+ .invoke("val")
+ .should("satisfy", (value) => value.length <= 5);
+ cy.get("span")
+ .contains("연락처")
+ .parent()
+ .next()
+ .type("00000000000")
+ .invoke("val")
+ .should("match", /^\d{3}-\d{4}-\d{4}$/);
+ cy.get("span")
+ .contains("학번")
+ .parent()
+ .next()
+ .type("123456")
+ .invoke("val")
+ .should("match", /^\d{6}$/);
+ cy.get("span")
+ .contains("학적상태")
+ .parent()
+ .next()
+ .type("재학")
+ .invoke("val")
+ .should("satisfy", (value) => value.length >= 1);
+ cy.get("label").contains("4학년").should("exist").click();
+ cy.get("label").contains("2학기").should("exist").click();
+ cy.get("button").contains("다음").should("exist").click();
+});
diff --git a/frontend/public/icons/lt.icon.white.svg b/frontend/public/icons/lt.icon.white.svg
index 0dc8166c..15f21c6d 100644
--- a/frontend/public/icons/lt.icon.white.svg
+++ b/frontend/public/icons/lt.icon.white.svg
@@ -1,3 +1,3 @@
-