diff --git a/src/frontend/eyesee-admin/src/app/providers.tsx b/src/frontend/eyesee-admin/src/app/providers.tsx index ac92a90..1c76404 100644 --- a/src/frontend/eyesee-admin/src/app/providers.tsx +++ b/src/frontend/eyesee-admin/src/app/providers.tsx @@ -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"; diff --git a/src/frontend/eyesee-user/package.json b/src/frontend/eyesee-user/package.json index e6f5233..ab69668 100644 --- a/src/frontend/eyesee-user/package.json +++ b/src/frontend/eyesee-user/package.json @@ -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", diff --git a/src/frontend/eyesee-user/pnpm-lock.yaml b/src/frontend/eyesee-user/pnpm-lock.yaml index 98993be..e76d261 100644 --- a/src/frontend/eyesee-user/pnpm-lock.yaml +++ b/src/frontend/eyesee-user/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: react-dom: specifier: 19.0.0-rc-02c0e824-20241028 version: 19.0.0-rc-02c0e824-20241028(react@19.0.0-rc-02c0e824-20241028) + zustand: + specifier: ^5.0.1 + version: 5.0.1(@types/react@18.3.12)(react@19.0.0-rc-02c0e824-20241028) devDependencies: '@svgr/webpack': specifier: ^8.1.0 @@ -2535,6 +2538,24 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zustand@5.0.1: + resolution: {integrity: sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -5454,3 +5475,8 @@ snapshots: yaml@2.6.0: {} yocto-queue@0.1.0: {} + + zustand@5.0.1(@types/react@18.3.12)(react@19.0.0-rc-02c0e824-20241028): + optionalDependencies: + '@types/react': 18.3.12 + react: 19.0.0-rc-02c0e824-20241028 diff --git a/src/frontend/eyesee-user/src/apis/examCode.ts b/src/frontend/eyesee-user/src/apis/examCode.ts new file mode 100644 index 0000000..aff87ae --- /dev/null +++ b/src/frontend/eyesee-user/src/apis/examCode.ts @@ -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> => { + const response = await api.post(`/sessions/join`, data); + return response.data; +}; diff --git a/src/frontend/eyesee-user/src/apis/userInformation.ts b/src/frontend/eyesee-user/src/apis/userInformation.ts new file mode 100644 index 0000000..8aefc31 --- /dev/null +++ b/src/frontend/eyesee-user/src/apis/userInformation.ts @@ -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> => { + const response = await api.post(`/sessions/student`, data); + return response.data; +}; diff --git a/src/frontend/eyesee-user/src/app/enter/page.tsx b/src/frontend/eyesee-user/src/app/enter/page.tsx index 326d72b..a88eb0a 100644 --- a/src/frontend/eyesee-user/src/app/enter/page.tsx +++ b/src/frontend/eyesee-user/src/app/enter/page.tsx @@ -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]); @@ -23,9 +41,9 @@ const EnterPage = () => {
diff --git a/src/frontend/eyesee-user/src/app/information/page.tsx b/src/frontend/eyesee-user/src/app/information/page.tsx index 0e32114..4e76469 100644 --- a/src/frontend/eyesee-user/src/app/information/page.tsx +++ b/src/frontend/eyesee-user/src/app/information/page.tsx @@ -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({ + const [information, setInformation] = useState({ + 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); @@ -32,7 +48,11 @@ const InformationPage = () => { setInformation={setInformation} />
- +
); diff --git a/src/frontend/eyesee-user/src/app/notice/page.tsx b/src/frontend/eyesee-user/src/app/notice/page.tsx new file mode 100644 index 0000000..c9c3b54 --- /dev/null +++ b/src/frontend/eyesee-user/src/app/notice/page.tsx @@ -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 ( +
+ +
+ + +
+
+ +
+
+ ); +}; + +export default NoticePage; diff --git a/src/frontend/eyesee-user/src/assets/icons/InfoIcon.svg b/src/frontend/eyesee-user/src/assets/icons/InfoIcon.svg new file mode 100644 index 0000000..28dae3a --- /dev/null +++ b/src/frontend/eyesee-user/src/assets/icons/InfoIcon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/frontend/eyesee-user/src/components/common/NextButton.tsx b/src/frontend/eyesee-user/src/components/common/NextButton.tsx index 744d12c..8891c0a 100644 --- a/src/frontend/eyesee-user/src/components/common/NextButton.tsx +++ b/src/frontend/eyesee-user/src/components/common/NextButton.tsx @@ -7,6 +7,7 @@ type NextButtonProps = { isAvailable: boolean; title: string; noArrow?: boolean; + onSubmit?: () => void; }; const NextButton = ({ @@ -14,6 +15,7 @@ const NextButton = ({ isAvailable, title, noArrow, + onSubmit, }: NextButtonProps) => { const route = useRouter(); const handleClick = () => { @@ -23,7 +25,8 @@ const NextButton = ({ }; return (
{ + const { exam } = useExamStore(); + + return ( +
+
+
강의명
+
{exam.examName}
+
+
+
담당 교수
+
확인중
+
+
+
시험 시작시간
+
{exam.examStartTime}
+
+
+
진행 시간
+
{exam.examDuration}분
+
+
+
문제 수
+
확인 중
+
+
+
총 점수
+
확인 중
+
+
+ ※ 카메라 권한을 허용해야 합니다. +
+
+ ); +}; + +export default ExamCard; diff --git a/src/frontend/eyesee-user/src/components/notice/NoticeCard.tsx b/src/frontend/eyesee-user/src/components/notice/NoticeCard.tsx new file mode 100644 index 0000000..1f09ef5 --- /dev/null +++ b/src/frontend/eyesee-user/src/components/notice/NoticeCard.tsx @@ -0,0 +1,21 @@ +import InfoIcon from "@/assets/icons/InfoIcon.svg"; + +const NoticeCard = () => { + return ( +
+
+

유의사항

+ +
+
+ 시험 정보는 시험 감독자(관리자)가 입력한 정보입니다. +
+ 아래 기재된 정보와 관련하여 궁금한 사항은 +
+ 시험장에 상주하는 관리자를 통해 확인하시기 바랍니다. +
+
+ ); +}; + +export default NoticeCard; diff --git a/src/frontend/eyesee-user/src/store/useExamStore.ts b/src/frontend/eyesee-user/src/store/useExamStore.ts new file mode 100644 index 0000000..4821eb1 --- /dev/null +++ b/src/frontend/eyesee-user/src/store/useExamStore.ts @@ -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((set) => ({ + exam: InitialExamResponse, + setExam: (exam) => set({ exam }), +})); diff --git a/src/frontend/eyesee-user/src/types/common.ts b/src/frontend/eyesee-user/src/types/common.ts new file mode 100644 index 0000000..d6fe0f5 --- /dev/null +++ b/src/frontend/eyesee-user/src/types/common.ts @@ -0,0 +1,6 @@ +export type RESTYPE = { + statusCode: number; + code: string; + message: string; + data: T; +}; diff --git a/src/frontend/eyesee-user/src/types/exam.ts b/src/frontend/eyesee-user/src/types/exam.ts new file mode 100644 index 0000000..3cbb4d3 --- /dev/null +++ b/src/frontend/eyesee-user/src/types/exam.ts @@ -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; +}; diff --git a/src/frontend/eyesee-user/src/types/information.ts b/src/frontend/eyesee-user/src/types/information.ts deleted file mode 100644 index 8d4667b..0000000 --- a/src/frontend/eyesee-user/src/types/information.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type Information = { - name: string; - major: string; - studentNumber: string; - seatNumber: number; -};