Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 합/불 상태관리 페이지 추가 #205

Merged
merged 22 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6f7ace8
feat: 합불 관리 페이지 추가
geongyu09 Sep 13, 2024
96a5372
feat: 합불 요청 api 추가
geongyu09 Sep 13, 2024
410a1c9
feat: 합불 상태관리 네브바 추가
geongyu09 Sep 15, 2024
289604c
feat: pass state관련 api 로직 추가
geongyu09 Sep 15, 2024
023a3d4
design: 레이아웃 수정
geongyu09 Sep 15, 2024
bd64439
feat: 분야별 정렬 기능 추가
geongyu09 Sep 15, 2024
38890af
feat: TODO 작성
geongyu09 Sep 15, 2024
0c4a80c
refactor: 유틸 함수 function으로 옮김
geongyu09 Sep 15, 2024
9738a23
fix: 빌드 버그 수정
geongyu09 Sep 15, 2024
7cae5fa
fix: tailwind가 동적으로 적용이 되지 않던 버그 수정
geongyu09 Sep 15, 2024
192cb56
refactor: url을 기존 코딩 스타일에 맞추어 변경
geongyu09 Sep 16, 2024
7ec8cc1
refactor: 불필요한 조건문 제거
geongyu09 Sep 16, 2024
10af5ee
refactor: searchParams를 삼항연산자가 아닌 URLSearchParams로 변경
geongyu09 Sep 16, 2024
c6a4cd3
fix: query 문의 loading상태를 보여주도록 수정
geongyu09 Sep 16, 2024
ebb1b9a
refactor: 올바르게 카멜케이스가 되어있지 않던 부분 수정
geongyu09 Sep 16, 2024
26a4913
feat: query문 불필요한 추상화 제거
geongyu09 Sep 16, 2024
325aa41
refactor: 기존에 정의해둔 tailwind custom styles 사용
geongyu09 Sep 16, 2024
8d852bb
refactor: getApplicantPassState 유틸 함수에 불필요하게 적용된 switch-case 문을 객체로 변경
geongyu09 Sep 16, 2024
c522277
refactor: 합격 상태에 따른 비교 계층 변경
geongyu09 Sep 16, 2024
836ca3d
refactor: 불필요하게applicants에 조건문을 사용하던 부분 수정
geongyu09 Sep 16, 2024
e266f6a
fix: 분야별 정렬 부분에 들어가는 이미지에 alt 추가
geongyu09 Sep 16, 2024
46c03d1
feat: 목 데이터를 지우고 실제 서버와 통신하도록 수정
geongyu09 Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions frontend/components/passState/ApplicantsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"use client";

import { gridRatio } from "@/app/(WithNavbar)/pass-state/[generation]/page";
import {
useAllApplicantsWithPassState,
usePostApplicantPassState,
} from "@/src/apis/passState";
import { CURRENT_GENERATION } from "@/src/constants";
import { cn } from "@/src/utils/cn";
import { usePathname } from "next/navigation";
import Txt from "../common/Txt.component";
import { ApplicantPassState } from "@/src/apis/kanban";

function getApplicantPassState(passState: ApplicantPassState) {
switch (passState) {
case "non-processed":
return "처리중";
case "first-passed":
return "1차 합격";
case "final-passed":
return "최종 합격";
case "non-passed":
return "탈락";
}
}

const ApplicantsList = () => {
const selectedGeneration = usePathname().split("/")[2];

const {
data: allApplicants,
isLoading,
isError,
} = useAllApplicantsWithPassState({
generation: `${CURRENT_GENERATION}`,
});
const { mutate: updateApplicantPassState } = usePostApplicantPassState({
generation: `${CURRENT_GENERATION}`,
});

if (+selectedGeneration !== CURRENT_GENERATION) {
return <div>현재 지원중인 기수만 확인 가능합니다.</div>;
}

if (isLoading) {
return <div>Loading...</div>;
}

if (isError) {
return <div>Error</div>;
}

if (!allApplicants) {
return <div>아직은 지원자가 없습니다 🥲</div>;
}

const onChangeapplicantsPassState = (
applicantName: string,
params: {
applicantsId: string;
afterState: "pass" | "non-pass";
}
) => {
const ok = confirm(
`${applicantName}님을 ${params.afterState} 처리하시겠습니까?`
);
if (!ok) return;
updateApplicantPassState(params);
};

const { answers: applicants } = allApplicants;

return (
<ul className="flex flex-col gap-8">
{applicants.map(
({ state: { passState }, field, field1, field2, id, name }) => (
<li key={id} className={cn("grid items-center", gridRatio)}>
<Txt typography="h6" className="text-left truncate">
{`[${field}] ${name}`}
</Txt>
<Txt className="text-left truncate">{`${field1}/${field2}`}</Txt>
<Txt className="text-left truncate">
{getApplicantPassState(passState)}
</Txt>
<div className="flex justify-between">
<button
className="border px-4 py-2 rounded-lg"
onClick={() =>
onChangeapplicantsPassState(name, {
applicantsId: id,
afterState: "pass",
})
}
>
합격
</button>
<button
className="border px-4 py-2 rounded-lg"
onClick={() =>
onChangeapplicantsPassState(name, {
applicantsId: id,
afterState: "non-pass",
})
}
>
불합격
</button>
</div>
</li>
)
)}
</ul>
);
};

export default ApplicantsList;
21 changes: 0 additions & 21 deletions frontend/src/apis/pass-state/index.tsx

This file was deleted.

131 changes: 131 additions & 0 deletions frontend/src/apis/passState/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { https } from "../../functions/axios";
import { ApplicantPassState } from "../kanban";

interface PassInfo {
currentPage: number;
listCount: number;
pageLimit: number;
startPage: number;
endPage: number;
boardLimit: number;
}

interface Answer {
field: "개발자" | "디자이너" | "기획자";
field1: "APP" | "WEB" | "AI" | "GAME";
field2: "APP" | "WEB" | "AI" | "GAME";
2yunseong marked this conversation as resolved.
Show resolved Hide resolved
name: string;
id: string;
year: number;
state: {
passState: ApplicantPassState;
};
}

interface AllApplicantReq {
pageInfo: PassInfo;
answers: Answer[];
}

const _mock: AllApplicantReq = {
pageInfo: {
currentPage: 1,
listCount: 1,
pageLimit: 1,
startPage: 1,
endPage: 1,
boardLimit: 1,
},
answers: [
{
field: "개발자",
field1: "APP",
field2: "APP",
name: "김개발",
id: "1",
year: 28,
state: {
passState: "final-passed",
},
},
{
field: "개발자",
field1: "APP",
field2: "APP",
name: "김개발",
id: "1",
year: 28,
state: {
passState: "non-passed",
},
},
],
};

const getAllApplicantsPath = (generation: string) =>
`/page/1/year/${generation}/pass-states?order=state`;

const getApplicantByIdWithField = ({
applicantsId,
afterState,
}: {
applicantsId: string;
afterState: "non-pass" | "pass";
}) => `/applicants/${applicantsId}/state?afterState=${afterState}`;

const getAllApplicantsWithPassState = async (generation: string) => {
// const { data } = await https.get<AllApplicantReq>(
// getAllApplicantsPath(generation)
// );

const data = _mock;

return data;
};

const postApplicantPassState = async ({
afterState,
applicantsId,
}: {
applicantsId: string;
afterState: "non-pass" | "pass";
}) => {
await https.post(getApplicantByIdWithField({ applicantsId, afterState }));
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https.post(/applicants/${applicantsId}/state?afterState=${afterState})로 작성하지 않고
getApplicantByIdWithField 함수를 만들어서 사용하신 이유가 있을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 path를 따로 뺀 이유는 아래와 같은 장점이 있기 때문입니다.

  • 같은 주소에 다른 요청을 하는 경우 path가 재사용됩니다. 재사용되는 값은 변수등으로 빼서 재사용하기 편하게 사용하는 것이 좋습니다. 당연히 url이 변경되었을 떄에도 수정하기 쉽습니다.
  • 해당 url의 의미를 파악하기도 좋습니다.

( 참고 영상 : 해당 부분을 다루고 있으므로 참고해도 좋을 것 같습니다. )

하지만 무엇보다 그냥 제가 스타일을 안지킨게 문제이므로.. 수정 후에 해당 부분을 같이 이야기 할 수 있으면 좋을 것 같습니다!


interface useAllApplicantsWithPassStateParams {
generation: string;
}
2yunseong marked this conversation as resolved.
Show resolved Hide resolved
export const useAllApplicantsWithPassState = ({
generation,
}: useAllApplicantsWithPassStateParams) => {
return useQuery(
[getAllApplicantsPath(generation)],
() => getAllApplicantsWithPassState(generation),
{
enabled: !!generation,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 ㅋㅋㅋ 잘 지키고 계시는 군요 ㅋㅋㅋ

}
);
};

interface usePostApplicantPassStateParams {
applicantsId: string;
afterState: "non-pass" | "pass";
}
export const usePostApplicantPassState = ({
generation,
}: {
generation: string;
}) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (params: usePostApplicantPassStateParams) =>
postApplicantPassState(params),
onSuccess: () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

성공 뿐 아니라 실패시 어떤 작업을 하면 좋을지 한번쯤 생각해보시면 좋을 것 같아요

queryClient.invalidateQueries({
queryKey: [getAllApplicantsPath(generation)],
});
},
});
};