Skip to content

Commit

Permalink
FE: [feat] 시험 정보 페이지 추가 #34
Browse files Browse the repository at this point in the history
  • Loading branch information
hyeona01 committed Nov 19, 2024
1 parent 5cb7f1d commit afa394c
Show file tree
Hide file tree
Showing 16 changed files with 249 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/frontend/eyesee-admin/src/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { deleteAccessToken, getAccessToken } from "@/utils/auth";
import { getAccessToken } from "@/utils/auth";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactNode } from "react";

Expand Down
3 changes: 2 additions & 1 deletion src/frontend/eyesee-user/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"axios": "^1.7.7",
"next": "15.0.2",
"react": "19.0.0-rc-02c0e824-20241028",
"react-dom": "19.0.0-rc-02c0e824-20241028"
"react-dom": "19.0.0-rc-02c0e824-20241028",
"zustand": "^5.0.1"
},
"devDependencies": {
"@svgr/webpack": "^8.1.0",
Expand Down
26 changes: 26 additions & 0 deletions src/frontend/eyesee-user/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/frontend/eyesee-user/src/apis/examCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { api } from ".";
import { RESTYPE } from "@/types/common";
import { ExamRequest, ExamResponse } from "@/types/exam";

export const enterSession = async (
data: ExamRequest
): Promise<RESTYPE<ExamResponse>> => {
const response = await api.post(`/sessions/join`, data);
return response.data;
};
10 changes: 10 additions & 0 deletions src/frontend/eyesee-user/src/apis/userInformation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { api } from ".";
import { RESTYPE } from "@/types/common";
import { UserInfoRequest, UserInfoResponse } from "@/types/exam";

export const userInformation = async (
data: UserInfoRequest
): Promise<RESTYPE<UserInfoResponse>> => {
const response = await api.post(`/sessions/student`, data);
return response.data;
};
22 changes: 20 additions & 2 deletions src/frontend/eyesee-user/src/app/enter/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
"use client";

import { enterSession } from "@/apis/examCode";
import NextButton from "@/components/common/NextButton";
import SubHeader from "@/components/common/SubHeader";
import InputTestCode from "@/components/enterTestCode/InputTestCode";
import { useExamStore } from "@/store/useExamStore";
import { useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";

const EnterPage = () => {
const [code, setCode] = useState("");
const [isAvailable, setIsAvailable] = useState(false);
const { setExam } = useExamStore();
const router = useRouter();

// 시험 코드 제출 핸들러
const handleSubmit = async () => {
try {
const response = await enterSession({ examCode: code }); // API 호출
console.log("응답 데이터:", response); // 성공 시 데이터 처리
setExam(response.data);
router.push("/notice");
} catch (error) {
console.error("시험 세션 참여 실패:", error); // 실패 시 에러 처리
alert("시험 코드 확인에 실패했습니다. 다시 시도해주세요.");
}
};

useEffect(() => {
if (code != "") {
if (code.trim() != "") {
setIsAvailable(true);
}
}, [code]);
Expand All @@ -23,9 +41,9 @@ const EnterPage = () => {
</div>
<div className="fixed bottom-6 right-0">
<NextButton
action="/agreement"
title="NEXT"
isAvailable={isAvailable}
onSubmit={handleSubmit}
/>
</div>
</div>
Expand Down
32 changes: 26 additions & 6 deletions src/frontend/eyesee-user/src/app/information/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
"use client";

import { userInformation } from "@/apis/userInformation";
import NextButton from "@/components/common/NextButton";
import SubHeader from "@/components/common/SubHeader";
import InformationSection from "@/components/information/InformationSection";
import { Information } from "@/types/information";
import { UserInfoRequest } from "@/types/exam";
import { informationValidation } from "@/utils/validation";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";

const InformationPage = () => {
const router = useRouter();
const [isAvailable, setIsAvailable] = useState(false);
const [information, setInformation] = useState<Information>({
const [information, setInformation] = useState<UserInfoRequest>({
examCode: "",
name: "",
major: "",
studentNumber: "",
seatNumber: 0,
department: "",
userNum: 0,
seatNum: 0,
});

// 수험자 정보 제출 핸들러
const handleSubmit = async () => {
try {
const response = await userInformation(information); // API 호출
console.log("응답 데이터:", response); // 성공 시 데이터 처리
router.push("/camera");
} catch (error) {
console.error("수험 정보 입력 실패:", error); // 실패 시 에러 처리
alert("수험 정보 입력에 실패했습니다. 다시 시도해주세요.");
}
};

useEffect(() => {
if (informationValidation(information)) {
setIsAvailable(true);
Expand All @@ -32,7 +48,11 @@ const InformationPage = () => {
setInformation={setInformation}
/>
<div className="fixed bottom-6 right-0">
<NextButton action="/camera" title="NEXT" isAvailable={isAvailable} />
<NextButton
onSubmit={handleSubmit}
title="NEXT"
isAvailable={isAvailable}
/>
</div>
</div>
);
Expand Down
21 changes: 21 additions & 0 deletions src/frontend/eyesee-user/src/app/notice/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import NextButton from "@/components/common/NextButton";
import SubHeader from "@/components/common/SubHeader";
import ExamCard from "@/components/notice/ExamCard";
import NoticeCard from "@/components/notice/NoticeCard";

const NoticePage = () => {
return (
<div>
<SubHeader title="시험 유의사항" />
<div className="flex flex-col items-center">
<NoticeCard />
<ExamCard />
</div>
<div className="fixed bottom-6 right-0">
<NextButton title="NEXT" isAvailable={true} action="/agreement" />
</div>
</div>
);
};

export default NoticePage;
3 changes: 3 additions & 0 deletions src/frontend/eyesee-user/src/assets/icons/InfoIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ type NextButtonProps = {
isAvailable: boolean;
title: string;
noArrow?: boolean;
onSubmit?: () => void;
};

const NextButton = ({
action,
isAvailable,
title,
noArrow,
onSubmit,
}: NextButtonProps) => {
const route = useRouter();
const handleClick = () => {
Expand All @@ -23,7 +25,8 @@ const NextButton = ({
};
return (
<div
onClick={handleClick}
// onClick={handleClick}
onClick={onSubmit ? onSubmit : handleClick}
className={`z-50 flex justify-between items-center gap-14 text-white text-[14px] tracking-[4.2px] px-4 py-3 ${
isAvailable ? "bg-[rgb(14,29,60,0.8)]" : "bg-[rgb(146,146,146,0.8)]"
}`}
Expand Down
41 changes: 41 additions & 0 deletions src/frontend/eyesee-user/src/components/notice/ExamCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use client";

import { useExamStore } from "@/store/useExamStore";

const ExamCard = () => {
const { exam } = useExamStore();

return (
<div className="mt-8 py-[26px] px-[20px] bg-[#0E1D3C] shadow-lg flex flex-col rounded-ss-2xl rounded-ee-2xl">
<div className="text-white flex items-center py-3 border-b border-white px-3">
<div className="text-[14px] w-[25vw]">강의명</div>
<div className="text-[12px] font-semibold">{exam.examName}</div>
</div>
<div className="text-white flex items-center py-3 border-b border-white px-3">
<div className="text-[14px] w-[25vw]">담당 교수</div>
<div className="text-[12px] font-semibold">확인중</div>
</div>
<div className="text-white flex items-center py-3 border-b border-white px-3">
<div className="text-[14px] w-[25vw]">시험 시작시간</div>
<div className="text-[12px] font-semibold">{exam.examStartTime}</div>
</div>
<div className="text-white flex items-center py-3 border-b border-white px-3">
<div className="text-[14px] w-[25vw]">진행 시간</div>
<div className="text-[12px] font-semibold">{exam.examDuration}</div>
</div>
<div className="text-white flex items-center py-3 border-b border-white px-3">
<div className="text-[14px] w-[25vw]">문제 수</div>
<div className="text-[12px] font-semibold">확인 중</div>
</div>
<div className="text-white flex items-center py-3 border-b border-white px-3">
<div className="text-[14px] w-[25vw]">총 점수</div>
<div className="text-[12px] font-semibold">확인 중</div>
</div>
<div className="text-white text-[10px] mt-3">
※ 카메라 권한을 허용해야 합니다.
</div>
</div>
);
};

export default ExamCard;
21 changes: 21 additions & 0 deletions src/frontend/eyesee-user/src/components/notice/NoticeCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import InfoIcon from "@/assets/icons/InfoIcon.svg";

const NoticeCard = () => {
return (
<div className="w-[80vw] mt-8 py-5 rounded-xl bg-[rgba(14,29,60,0.1)]">
<div className="flex items-center justify-center gap-1 text-black font-semibold text-[14px] text-center mb-3">
<p>유의사항</p>
<InfoIcon />
</div>
<div className="text-[10px] font-light text-black text-center">
시험 정보는 시험 감독자(관리자)가 입력한 정보입니다.
<br />
아래 기재된 정보와 관련하여 궁금한 사항은
<br />
시험장에 상주하는 관리자를 통해 확인하시기 바랍니다.
</div>
</div>
);
};

export default NoticeCard;
12 changes: 12 additions & 0 deletions src/frontend/eyesee-user/src/store/useExamStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ExamResponse, InitialExamResponse } from "@/types/exam";
import { create } from "zustand";

type ExamStore = {
exam: ExamResponse;
setExam: (exam: ExamResponse) => void;
};

export const useExamStore = create<ExamStore>((set) => ({
exam: InitialExamResponse,
setExam: (exam) => set({ exam }),
}));
6 changes: 6 additions & 0 deletions src/frontend/eyesee-user/src/types/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type RESTYPE<T> = {
statusCode: number;
code: string;
message: string;
data: T;
};
46 changes: 46 additions & 0 deletions src/frontend/eyesee-user/src/types/exam.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export type ExamRequest = {
examCode: string;
};

export type ExamResponse = {
examId: number;
examName: string;
examSemester: string;
examStudentNumber: number;
examLocation: string;
examDate: string;
examStartTime: string;
examDuration: number;
examStatus: string;
examNotice: string;
sessionId: number;
examCode: string;
};

export const InitialExamResponse = {
examId: 1,
examName: "융합캡스톤디자인",
examSemester: "24-2",
examStudentNumber: 30,
examLocation: "정보문화관",
examDate: "2024-11-20",
examStartTime: "14:30",
examDuration: 120,
examStatus: "INPROGRESS",
examNotice: "notice!!!!!",
sessionId: 1,
examCode: "d1213jfaj3",
};

export type UserInfoRequest = {
examCode: string;
name: string;
department: string;
userNum: number;
seatNum: number;
};

export type UserInfoResponse = {
access_token: string;
refresh_token: string;
};
6 changes: 0 additions & 6 deletions src/frontend/eyesee-user/src/types/information.ts

This file was deleted.

0 comments on commit afa394c

Please sign in to comment.