From 80ff490bcdbdb958846d7d960ce449ac67ebb91b Mon Sep 17 00:00:00 2001 From: hyuna Date: Thu, 18 Apr 2024 17:32:20 +0900 Subject: [PATCH 1/3] =?UTF-8?q?add=20:=20=EC=99=B8=EC=B6=9C=20=EC=88=98?= =?UTF-8?q?=EB=9D=BD=20=EB=B0=8F=20=EC=99=B8=EC=B6=9C=EC=9E=90=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/outList/index.ts | 33 ++- src/api/type.ts | 14 ++ src/app/outAccept/page.tsx | 262 ++++++++++++++++++++++ src/app/outList/page.tsx | 4 +- src/components/button/index.tsx | 4 +- src/components/dropdown/index.tsx | 1 + src/components/list/application/index.tsx | 83 +++++-- src/components/modal/index.tsx | 105 +++++++++ tailwind.config.ts | 1 + 9 files changed, 480 insertions(+), 27 deletions(-) create mode 100644 src/app/outAccept/page.tsx create mode 100644 src/components/modal/index.tsx diff --git a/src/api/outList/index.ts b/src/api/outList/index.ts index 3226c16..8325279 100644 --- a/src/api/outList/index.ts +++ b/src/api/outList/index.ts @@ -1,6 +1,6 @@ import { useMutation, useQuery } from "@tanstack/react-query"; import { instance } from ".."; -import { applicationOK, earlyReturnHome } from "../type"; +import { Accept, ClassProp, applicationOK, earlyReturnHome } from "../type"; export const Application = () => { return useQuery({ @@ -36,3 +36,34 @@ export const ReturnSchool = () => { }, }); }; + +export const GetClass = () => { + return useMutation({ + mutationFn: async (param: ClassProp) => { + try { + const response = await instance.get( + `${param.type}/grade?grade=${param.grade}&class_num=${param.class}` + ); + return response.data; + } catch (error) { + throw error; + } + }, + }); +}; +export const OutAcceptApi = () => { + return useMutation({ + mutationFn: async (param) => { + try { + const response = await instance.patch(`${param.type}/status`, { + type: param.type, + status: param.status, + ids: param.ids, + }); + return response.data; + } catch (error) { + throw error; + } + }, + }); +}; diff --git a/src/api/type.ts b/src/api/type.ts index ec69044..eac2e8f 100644 --- a/src/api/type.ts +++ b/src/api/type.ts @@ -6,6 +6,7 @@ export interface applicationOK { grade: number; class_num: number; num: number; + reason: string; } export interface earlyReturnHome { @@ -15,4 +16,17 @@ export interface earlyReturnHome { grade: number; class_num: number; num: number; + reason: string; +} + +export interface Accept { + type: string; + status: "OK" | "NO"; + ids: string[]; +} + +export interface ClassProp { + grade: number; + class: number; + type: string; } diff --git a/src/app/outAccept/page.tsx b/src/app/outAccept/page.tsx new file mode 100644 index 0000000..487ae48 --- /dev/null +++ b/src/app/outAccept/page.tsx @@ -0,0 +1,262 @@ +"use client"; +import { OutAcceptApi, GetClass } from "@/api/outList"; +import { applicationOK, earlyReturnHome } from "@/api/type"; +import BackGround from "@/components/background"; +import Button from "@/components/button"; +import Dropdown from "@/components/dropdown"; +import NonReturn from "@/components/list/application"; +import Modal from "@/components/modal"; +import { getFullToday } from "@/util/date"; +import { getStudentString } from "@/util/util"; +import React, { useEffect, useState } from "react"; + +const OutAccept = () => { + const [selectedTab, setSelectedTab] = useState(true); + const [applicationData, setApplicationData] = useState(); + const [earlyData, setEarlyData] = useState(); + const [selectGrade, setSelectGrade] = useState(1); + const [selectClass, setSelectClass] = useState(1); + const [selectedStudents, setSelectedStudents] = useState([]); + const [acmodal, setAcModal] = useState(false); + const [nomodal, setNomodal] = useState(false); + const [selectedStudentName, setSelectedStudentName] = useState([]); + const { mutate: outAcceptMutate } = GetClass(); + const { mutate: AcceptMutate } = OutAcceptApi(); + + useEffect(() => { + AcceptDataList(); + }, [selectedTab]); + + useEffect(() => { + AcceptDataList(); + }, [selectGrade, selectClass]); + + const handleGradeChange = (selectedOption: number) => { + if (selectedOption === 5) { + setSelectGrade(5); + setSelectClass(5); + } else { + setSelectGrade(selectedOption); + } + }; + + const handleClassChange = (selectedOption: number) => { + if (selectGrade === 5) { + alert("학년을 선택해주세요"); + setSelectClass(selectedOption); + } else { + setSelectClass(selectedOption); + } + }; + + const AcceptDataList = async () => { + try { + if (selectGrade && selectClass) { + const reqOption = selectedTab ? "application" : "early-return"; + await outAcceptMutate( + { + type: reqOption, + grade: selectGrade, + class: selectClass, + }, + { + onSuccess: (data) => { + setApplicationData(data); + }, + onError: (error) => { + console.log(error); + }, + } + ); + } + } catch (error) { + console.error("Out accept error", error); + } + }; + + const handleAcceptListClick = (id: string, name: string) => { + const isStudentSelected = selectedStudents.includes(id); + if (isStudentSelected) { + setSelectedStudents((prevSelectedStudents) => + prevSelectedStudents.filter((selectedStudent) => selectedStudent !== id) + ); + setSelectedStudentName((prevSelectedStudentName) => + prevSelectedStudentName.filter( + (selectedStudentName) => selectedStudentName !== name + ) + ); + } else { + setSelectedStudents((prevSelectedStudents) => [ + ...prevSelectedStudents, + id, + ]); + setSelectedStudentName((prevSelectedStudentName) => [ + ...prevSelectedStudentName, + name, + ]); + } + }; + + const Accept = () => { + setAcModal(true); + }; + + const onCancel = () => { + setAcModal(false); + setNomodal(false); + }; + + const No = () => { + setNomodal(true); + }; + + const onClickTab = (tab: boolean) => { + setSelectedTab(tab); + }; + + const confirmReturn = async () => { + try { + if (selectGrade && selectClass) { + const reqOption = selectedTab ? "application" : "early-return"; + await AcceptMutate( + { + type: reqOption, + status: "NO", + ids: selectedStudents, + }, + { + onSuccess: () => { + setNomodal(false); + }, + onError: (error) => { + console.error("Out accept error", error); + setNomodal(false); + }, + } + ); + } + } catch (error) { + alert("외출 거절에 실패하였습니다"); + setNomodal(false); + } + }; + + const Acceptconfirm = async () => { + try { + if (selectGrade && selectClass) { + const reqOption = selectedTab ? "application" : "early-return"; + await AcceptMutate( + { + type: reqOption, + status: "OK", + ids: selectedStudents, + }, + { + onSuccess: () => { + setAcModal(false); + // location.reload(); + }, + onError: (error) => { + console.error("Out accept error", error); + setAcModal(false); + }, + } + ); + } + } catch (error) { + alert("외출 수락에 실패하였습니다"); + setAcModal(false); + } + }; + + return ( + +
+ + +
+
+ + +
+ + } + > +
+ {selectedTab + ? applicationData?.map((item, index) => ( + handleAcceptListClick(item.id, item.username)} + reason={item.reason} + /> + )) + : applicationData?.map((item, index) => ( + handleAcceptListClick(item.id, item.username)} + reason={item.reason} + /> + ))} +
+ {acmodal && ( + 1 + ? `${selectedStudentName[0]} 학생 외 ${ + selectedStudentName.length - 1 + }명` + : selectedStudentName.length === 1 + ? `${selectedStudentName[0]} 학생` + : "" + }`} + /> + )} + {nomodal && ( + 1 + ? `${selectedStudentName[0]} 학생 외 ${ + selectedStudentName.length - 1 + }명` + : selectedStudentName.length === 1 + ? `${selectedStudentName[0]} 학생` + : "" + }`} + /> + )} +
+ ); +}; + +export default OutAccept; diff --git a/src/app/outList/page.tsx b/src/app/outList/page.tsx index 922bc15..e39c990 100644 --- a/src/app/outList/page.tsx +++ b/src/app/outList/page.tsx @@ -29,7 +29,7 @@ const OutList = () => { return ( { key={index} returnTime={item.end_time} name={getStudentString(item)} + reason={item.reason} /> )) : earlyData?.map((item, index) => ( @@ -51,6 +52,7 @@ const OutList = () => { key={index} name={getStudentString(item)} type="early-return" + reason={item.reason} /> ))} diff --git a/src/components/button/index.tsx b/src/components/button/index.tsx index c4002c4..63809b5 100644 --- a/src/components/button/index.tsx +++ b/src/components/button/index.tsx @@ -67,7 +67,7 @@ const Button: React.FC = ({ case "medium": return "w-42 min-w-20 h-13 text-Button-M"; case "small": - return "w-38 h-12 text-Button-S"; + return "w-34 h-11 text-Button-S"; case "extraSmall": return "w-30 h-9 text-Button-S"; case "extraSmall2": @@ -78,7 +78,7 @@ const Button: React.FC = ({ return (
{children}
{Icon && }
diff --git a/src/components/dropdown/index.tsx b/src/components/dropdown/index.tsx index 7ee844d..c307351 100644 --- a/src/components/dropdown/index.tsx +++ b/src/components/dropdown/index.tsx @@ -64,6 +64,7 @@ const Dropdown: React.FC = ({ type, onChange }) => { break; } } + setIsDropdownVisible(false); }; const generateOptions = (options: any[]) => { diff --git a/src/components/list/application/index.tsx b/src/components/list/application/index.tsx index 715597e..1a0412b 100644 --- a/src/components/list/application/index.tsx +++ b/src/components/list/application/index.tsx @@ -1,13 +1,16 @@ "use client"; import { ReturnSchool } from "@/api/outList"; import Button from "@/components/button"; +import Modal from "@/components/modal"; import React, { useState } from "react"; interface NonReturnProp { name: string; returnTime?: string; - type: "application" | "early-return"; + type: "application" | "early-return" | "accept"; id: string; + onClick?: () => void; + reason: string; } export const NonReturn: React.FC = ({ @@ -15,13 +18,24 @@ export const NonReturn: React.FC = ({ returnTime, type, id, + onClick, + reason, }) => { const { mutate: ReturnStudent } = ReturnSchool(); - const [selected, setSelected] = useState(false); + const [click, setClick] = useState(false); + const [modal, setModal] = useState(false); + + const onClickModal = () => { + setModal(true); + }; + + const onCancelModal = () => { + setModal(false); + }; const confirmReturn = async () => { try { - const result = await ReturnStudent( + await ReturnStudent( { id: id }, { onSuccess: () => { @@ -36,29 +50,52 @@ export const NonReturn: React.FC = ({ } catch (error) { console.error(error); } + setModal(false); }; return ( -
-
{name}
- {type === "application" && ( - <> -
- {returnTime} 복귀 예정 -
-
-
- -
-
- - )} +
+
setClick(!click)} + > +
+
{name}
+ {type === "application" && ( + <> +
+ {returnTime} 복귀 예정 +
+
+
+ +
+
+ + )} + {modal && ( + + )} + {type === "accept" && ( +
{returnTime}
+ )} +
+ {click &&
{reason}
} +
); }; diff --git a/src/components/modal/index.tsx b/src/components/modal/index.tsx new file mode 100644 index 0000000..76705d9 --- /dev/null +++ b/src/components/modal/index.tsx @@ -0,0 +1,105 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import Button from "../button"; +//import AutoInput from "../input/auto/page"; + +interface ChangeProps { + text: string; + name: string; +} + +interface ModalProps { + heading1?: string; + heading2?: string; + type: "button" | "error" | "add"; + buttonMessage: string; + onCancel: () => void; + onConfirm: () => void; + name?: string; + value?: () => void; +} + +const Modal: React.FC = ({ + heading1, + heading2, + type, + buttonMessage, + onCancel, + onConfirm, + name, +}) => { + const [inputValue, setInputValue] = useState(""); + + const [addstudent, setAddstudent] = useState({ + student: "", + }); + + const AutohandleChange = ({ text, name }: ChangeProps) => { + setAddstudent({ ...addstudent, [name]: text }); + setInputValue(text); + }; + + const renderButtons = () => { + return ( +
+ + +
+ ); + }; + + return ( +
+ {type === "button" || type === "error" ? ( +
+
+
+ {heading1 &&
{heading1}
} + {heading2 &&
{heading2}
} +
+
+ {buttonMessage}하기 선택하면 다시 변경할 수 없습니다. +
+ {renderButtons()} +
+
+ ) : type === "add" ? ( +
+
+
+ {heading1 && ( +
+
+
창조실
인원추가 +
+
+ {/* */} +
+ {renderButtons()} +
+ )} +
+
+
+ ) : null} +
+ ); +}; + +export default Modal; diff --git a/tailwind.config.ts b/tailwind.config.ts index 039d9cb..a31dc96 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -295,6 +295,7 @@ const config: Config = { "50": "12.438rem", "42": "10.25rem", "38": "9.375rem", + "34": "8.75rem", "30": "7.438rem", "29": "7.25rem", "25": "6.25rem", From 5300cf11417f468513a5fb3b63a8541c9b20da40 Mon Sep 17 00:00:00 2001 From: phyuna0525 <128463899+phyuna0525@users.noreply.github.com> Date: Fri, 19 Apr 2024 23:28:56 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix=20:=20=EB=A9=94=EC=9D=B8=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/main/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/main/page.tsx b/src/app/main/page.tsx index 1f2e62d..2394efe 100644 --- a/src/app/main/page.tsx +++ b/src/app/main/page.tsx @@ -6,7 +6,7 @@ import { useState } from "react"; const Main = () => { const [date, setDate] = useState(new Date().toLocaleTimeString()); - const [name, setName] = useState("백승휘"); + const [name, setName] = useState(""); const [floor, setFloor] = useState(2); return (
From 320a04bb30bc01003e0f9d3bf729d2fd7634d142 Mon Sep 17 00:00:00 2001 From: hyuna Date: Sat, 20 Apr 2024 00:56:52 +0900 Subject: [PATCH 3/3] =?UTF-8?q?add=20:=20=EC=A0=84=EA=B3=B5=EB=8F=99?= =?UTF-8?q?=EC=95=84=EB=A6=AC=20=EC=B6=9C=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/afterManage/index.ts | 25 +++++ src/api/type.ts | 18 ++++ src/app/afterManage/page.tsx | 100 +++++++++++++++++ src/app/outAccept/page.tsx | 3 +- src/app/outList/page.tsx | 2 + src/assets/CaretDown.tsx | 24 +++++ src/components/background/index.tsx | 37 +++---- src/components/dropdown/index.tsx | 35 +++--- src/components/dropdown/status.tsx | 124 ++++++++++++++++++++++ src/components/list/afterManage/index.tsx | 119 +++++++++++++++++++++ src/components/list/application/index.tsx | 8 +- tailwind.config.ts | 4 + 12 files changed, 461 insertions(+), 38 deletions(-) create mode 100644 src/api/afterManage/index.ts create mode 100644 src/app/afterManage/page.tsx create mode 100644 src/assets/CaretDown.tsx create mode 100644 src/components/dropdown/status.tsx create mode 100644 src/components/list/afterManage/index.tsx diff --git a/src/api/afterManage/index.ts b/src/api/afterManage/index.ts new file mode 100644 index 0000000..12f327b --- /dev/null +++ b/src/api/afterManage/index.ts @@ -0,0 +1,25 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import { instance } from ".."; +import { ChangeStatus, ClubList } from "../type"; + +export const GetClubList = (club: string) => { + return useQuery({ + queryKey: ["GetClubList", club], + queryFn: async () => { + const response = await instance.get(`/attendance/club?club=${club}`); + return response.data; + }, + }); +}; + +export const FixStatus = () => { + return useMutation({ + mutationFn: async (param) => { + try { + await instance.patch(`/attendance/modify`, param); + } catch (error) { + console.log(error); + } + }, + }); +}; diff --git a/src/api/type.ts b/src/api/type.ts index eac2e8f..3099a73 100644 --- a/src/api/type.ts +++ b/src/api/type.ts @@ -30,3 +30,21 @@ export interface ClassProp { class: number; type: string; } + +export interface ClubList { + id: string; + username: string; + grade: number; + class_num: number; + num: number; + status6: string; + status7: string; + status8: string; + status9: string; + status10: string; +} + +export interface ChangeStatus { + user_id: string; + status_list: string[]; +} diff --git a/src/app/afterManage/page.tsx b/src/app/afterManage/page.tsx new file mode 100644 index 0000000..ba8c29c --- /dev/null +++ b/src/app/afterManage/page.tsx @@ -0,0 +1,100 @@ +"use client"; +import { FixStatus, GetClubList } from "@/api/afterManage"; +import { ChangeStatus, ClubList } from "@/api/type"; +import BackGround from "@/components/background"; +import Button from "@/components/button"; +import Dropdown from "@/components/dropdown"; +import AfterList from "@/components/list/afterManage"; +import { getStudentString } from "@/util/util"; +import { useEffect, useState } from "react"; + +const AfterManage = () => { + const [clubList, setClubList] = useState([]); + const [selectedTab, setSelectedTab] = useState(true); + const [selectClassTime, setSelectClassTime] = useState(8); + const [selectClub, setSelectClub] = useState("대동여지도"); + const { data: getClub } = GetClubList(selectClub); + const { mutate: changeStatus } = FixStatus(); + + const handleSaveModalConfirm = () => { + const updatedData: ChangeStatus[] = []; + clubList?.forEach((item) => { + const localData = localStorage.getItem(item.id); + if (localData) { + const parsedData = JSON.parse(localData); + const studentData = { + user_id: item.id, + status_list: [ + parsedData[0], + parsedData[1], + parsedData[2], + parsedData[3] || "ATTENDANCE", + parsedData[4] || "ATTENDANCE", + ], + }; + updatedData.push(studentData); + } + }); + changeStatus(updatedData); + }; + + useEffect(() => { + if (getClub) { + setClubList(getClub); + } + }, [getClub]); + + const handleClubChange = (selectedOption: string) => { + setSelectClub(selectedOption); + }; + + const handleClassTimeChange = (selectedOption: number) => { + setSelectClassTime(selectedOption); + }; + + const onClickTab = (tab: boolean) => { + setSelectedTab(tab); + }; + + return ( + + + +
+ } + TabOnclick={onClickTab} + > +
+ {selectedTab && + clubList.map((item, index) => ( + + ))} +
+
+ +
+ + ); +}; + +export default AfterManage; diff --git a/src/app/outAccept/page.tsx b/src/app/outAccept/page.tsx index 487ae48..cffeae1 100644 --- a/src/app/outAccept/page.tsx +++ b/src/app/outAccept/page.tsx @@ -13,7 +13,6 @@ import React, { useEffect, useState } from "react"; const OutAccept = () => { const [selectedTab, setSelectedTab] = useState(true); const [applicationData, setApplicationData] = useState(); - const [earlyData, setEarlyData] = useState(); const [selectGrade, setSelectGrade] = useState(1); const [selectClass, setSelectClass] = useState(1); const [selectedStudents, setSelectedStudents] = useState([]); @@ -175,6 +174,8 @@ const OutAccept = () => { subTitle={getFullToday()} TabOK={true} TabOnclick={onClickTab} + leftTab="외출" + rightTab="조기귀가" Dropdown={
diff --git a/src/app/outList/page.tsx b/src/app/outList/page.tsx index e39c990..a721e84 100644 --- a/src/app/outList/page.tsx +++ b/src/app/outList/page.tsx @@ -32,6 +32,8 @@ const OutList = () => { title="외출자 목록" subTitle={getFullToday()} TabOK={true} + leftTab="외출" + rightTab="조기귀가" TabOnclick={onClickTab} >
diff --git a/src/assets/CaretDown.tsx b/src/assets/CaretDown.tsx new file mode 100644 index 0000000..3325b3f --- /dev/null +++ b/src/assets/CaretDown.tsx @@ -0,0 +1,24 @@ +interface colorProp { + color?: string; +} + +export const CaretDown: React.FC = ({ color }) => { + return ( + + + + ); +}; diff --git a/src/components/background/index.tsx b/src/components/background/index.tsx index 731416b..1b921aa 100644 --- a/src/components/background/index.tsx +++ b/src/components/background/index.tsx @@ -1,15 +1,16 @@ -"use client"; import Header from "@/components/header"; import Tab from "@/components/tab"; import React from "react"; interface Prop { title: string; - subTitle: string; + subTitle?: string; Dropdown?: React.ReactNode; TabOK: boolean; children: React.ReactNode; TabOnclick: (tab: boolean) => void; + leftTab?: string; + rightTab?: string; } const BackGround: React.FC = ({ @@ -17,28 +18,28 @@ const BackGround: React.FC = ({ subTitle, Dropdown, TabOK = true, + leftTab, + rightTab, children, TabOnclick, }) => { return ( -
+
-
-
-
{title}
-
{subTitle}
+
+
+
{title}
+
{subTitle}
-
{Dropdown}
-
-
- {TabOK && ( - - )} -
+ {Dropdown &&
{Dropdown}
} +
+ {TabOK && ( + + )} {children}
diff --git a/src/components/dropdown/index.tsx b/src/components/dropdown/index.tsx index c307351..f08c8e2 100644 --- a/src/components/dropdown/index.tsx +++ b/src/components/dropdown/index.tsx @@ -13,7 +13,8 @@ const Dropdown: React.FC = ({ type, onChange }) => { const [selectedGradeOption, setSelectedGradeOption] = useState(1); const [selectedClassOption, setSelectedClassOption] = useState(1); const [selectedFloorOption, setSelectedFloorOption] = useState(2); - const [selectedClubOption, setSelectedClubOption] = useState("PiCK"); + const [selectedClubOption, setSelectedClubOption] = + useState("세미나실 2-1(대동여지도)"); const [selectedAllOption, setSelectedAllOption] = useState(1); const [selectedClassTime, setSelectedClassTime] = useState(8); const [isDropdownVisible, setIsDropdownVisible] = useState(false); @@ -55,7 +56,7 @@ const Dropdown: React.FC = ({ type, onChange }) => { setSelectedClassTime(option.value); break; case "club": - setSelectedClubOption(option.value); + setSelectedClubOption(option.label); break; case "floor": setSelectedFloorOption(option.value); @@ -106,18 +107,18 @@ const Dropdown: React.FC = ({ type, onChange }) => { ]; const clubOptions = [ - { value: "PiCK", label: "PiCK" }, - { value: "대동여지도", label: "대동여지도" }, - { value: "info", label: "info" }, - { value: "은하", label: "은하" }, - { value: "DMS", label: "DMS" }, - { value: "gram", label: "gram" }, - { value: "Lift", label: "Lift" }, - { value: "Log", label: "Log" }, - { value: "Modeep", label: "Modeep" }, - { value: "NoNamed", label: "NoNamed" }, - { value: "TeamQSS", label: "TeamQSS" }, - { value: "어게인", label: "어게인" }, + { value: "대동여지도", label: "세미나실 2-1(대동여지도)" }, + { value: "DMS", label: "세미나실 2-2(DMS)" }, + { value: "gram", label: "세미나실 2-3(gram)" }, + { value: "Lift", label: "소개2실(Lift)" }, + { value: "Log", label: "세미나실 3-1(Log)" }, + { value: "은하", label: "세미나실 3-2(은하)" }, + { value: "PiCK", label: "세미나실 3-3(PiCK)" }, + { value: "어게인", label: "보안 1실(어게인)" }, + { value: "info", label: "보안 2실(info)" }, + { value: "TeamQSS", label: "세미나실 4-1(TeamQSS)" }, + { value: "NoNamed", label: "세미나실 4-2(NoNamed)" }, + { value: "Modeep", label: "세미나실 4-3(Modeep)" }, { value: "자습", label: "자습" }, ]; @@ -148,9 +149,9 @@ const Dropdown: React.FC = ({ type, onChange }) => { }; return ( -
+
{type === "grade" @@ -165,7 +166,7 @@ const Dropdown: React.FC = ({ type, onChange }) => { : `${selectedAllOption}학년` : type === "classTime" ? `${selectedClassTime}교시` - : ""} + : `${selectedClubOption}`} arrow void; + type?: "NO"; +} + +const AfterCheck: React.FC = ({ state, onChange, type }) => { + const [isDropdownVisible, setIsDropdownVisible] = useState(false); + const [selectedOption, setSelectedOption] = useState(state); + const dropdownRef = useRef(null); + + useEffect(() => { + setSelectedOption(state); + }, [state]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { + setIsDropdownVisible(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + const style = () => { + switch (selectedOption) { + case "출석": + return "text-neutral-100 border border-neutral-100"; + case "무단": + return "border border-error-500 text-error-500"; + case "외출": + return "border border-primary-500 text-primary-500"; + case "이동": + return "border border border-tertiary-300 text-tertiary-300"; + default: + return ""; + } + }; + + const commonStyle = + "text-caption3 w-full py-2 justify-center items-center flex px-1 hover:bg-primary-200 hover:text-white"; + + const toggleDropdown = () => { + setIsDropdownVisible(!isDropdownVisible); + }; + + const ChangeEn = (option: string) => { + switch (option) { + case "무단": + return "DISALLOWED"; + case "외출": + return "GO_OUT"; + case "이동": + return "MOVEMENT"; + case "출석": + return "ATTENDANCE"; + default: + return ""; + } + }; + + const handleOptionClick = (option: string) => { + setSelectedOption(option); + onChange(ChangeEn(option)); + setIsDropdownVisible(false); + }; + + return state === "현체" ? ( +
+ {selectedOption} +
+ ) : ( +
+
{selectedOption}
+ + {isDropdownVisible && type !== "NO" && ( +
+
handleOptionClick("출석")} + > + 출석 +
+
handleOptionClick("이동")} + > + 이동 +
+
handleOptionClick("외출")} + > + 외출 +
+
handleOptionClick("무단")} + > + 무단 +
+
+ )} +
+ ); +}; + +export default AfterCheck; diff --git a/src/components/list/afterManage/index.tsx b/src/components/list/afterManage/index.tsx new file mode 100644 index 0000000..88d1a79 --- /dev/null +++ b/src/components/list/afterManage/index.tsx @@ -0,0 +1,119 @@ +"use client"; +import { ReturnSchool } from "@/api/outList"; +import Button from "@/components/button"; +import StatusDrop from "@/components/dropdown/status"; +import Modal from "@/components/modal"; +import React, { useEffect, useState } from "react"; + +interface NonReturnProp { + state1: string; + state2: string; + state3: string; + state4?: string; + state5?: string; + id: string; + onClick?: () => void; + type?: "NO"; + name: string; + time: number; +} + +export const AfterList = ({ + state1, + state2, + state3, + state4, + state5, + time, + id, + onClick, + type, + name, +}: NonReturnProp) => { + const Change = (item: string) => { + switch (item) { + case "ATTENDANCE": + return "출석"; + case "MOVEMENT": + return "이동"; + case "GO_OUT": + return "외출"; + case "DISALLOWED": + return "무단"; + case "PICNIC": + return "현체"; + case "EMPLOYMENT": + return "취업"; + default: + return ""; + } + }; + + const [statusList, setStatusList] = useState([]); + + useEffect(() => { + setStatusList([state1, state2, state3]); + }, [state1, state2, state3]); + + useEffect(() => { + localStorage.setItem(id, JSON.stringify(statusList)); + }, [id, statusList]); + + const handleChange = (index: number, newState: string) => { + const newStatusList = [...statusList]; + newStatusList[index] = newState; + setStatusList(newStatusList); + }; + + const ClassCheck = (newState: string) => { + switch (time) { + case 6: + handleChange(0, newState); + break; + case 7: + handleChange(1, newState); + break; + case 8: + handleChange(2, newState); + break; + case 9: + handleChange(3, newState); + break; + case 10: + handleChange(4, newState); + break; + default: + break; + } + }; + + const changeProp = () => { + switch (time) { + case 6: + state1; + case 7: + state2; + case 8: + state3; + case 9: + state4; + case 10: + state5; + } + }; + + return ( +
+
+
+
{name}
+ +
+
+
+ ); +}; + +export default AfterList; diff --git a/src/components/list/application/index.tsx b/src/components/list/application/index.tsx index 1a0412b..416e4df 100644 --- a/src/components/list/application/index.tsx +++ b/src/components/list/application/index.tsx @@ -1,13 +1,14 @@ "use client"; import { ReturnSchool } from "@/api/outList"; import Button from "@/components/button"; +import StatusDrop from "@/components/dropdown/status"; import Modal from "@/components/modal"; import React, { useState } from "react"; interface NonReturnProp { name: string; returnTime?: string; - type: "application" | "early-return" | "accept"; + type: "application" | "early-return" | "accept" | "after"; id: string; onClick?: () => void; reason: string; @@ -81,6 +82,7 @@ export const NonReturn: React.FC = ({
)} + {type === "after" && {}} state="출석" />} {modal && ( = ({
{returnTime}
)}
- {click &&
{reason}
} + {type !== "after" && click && ( +
{reason}
+ )}
); diff --git a/tailwind.config.ts b/tailwind.config.ts index a31dc96..eeb6531 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -313,6 +313,7 @@ const config: Config = { "25": "6.25rem", "15": "3.75rem", "90dvh": "90dvh", + "80dvh": "80dvh", }, gap: { "0.5": "0.125rem", @@ -343,6 +344,9 @@ const config: Config = { "3xl": "1850px", mxl: "1650px", }, + spacing: { + "4%": "4%", + }, }, }, };