From c7f6dd8c886f67558f3c893a4c6c968dec25986b Mon Sep 17 00:00:00 2001 From: Imggaggu Date: Wed, 21 May 2025 19:48:34 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=A0=84=20=EC=BB=A4?= =?UTF-8?q?=EB=B0=8B...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.jsx | 3 +- .../pages/admin/AdminStudentAssignment.jsx | 125 ++++++++++++++++++ .../admin/AdminStudentAssignment.module.css | 112 ++++++++++++++++ 3 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/admin/AdminStudentAssignment.jsx create mode 100644 frontend/src/pages/admin/AdminStudentAssignment.module.css diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index ed53cf3..e483d0a 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -11,7 +11,7 @@ import ManageTask from "./pages/admin/ManageTask.jsx"; import AttendanceCode from "./pages/admin/AttendanceCode"; import Attendance from "./pages/generation/Attendance"; import AdminStudentAttendance from "./pages/admin/AdminStudentAttendance"; - +import AdminStudentAssignment from "./pages/admin/AdminStudentAssignment.jsx"; function App() { return ( @@ -27,6 +27,7 @@ function App() { } /> } /> } /> + } /> ); diff --git a/frontend/src/pages/admin/AdminStudentAssignment.jsx b/frontend/src/pages/admin/AdminStudentAssignment.jsx new file mode 100644 index 0000000..2114cb4 --- /dev/null +++ b/frontend/src/pages/admin/AdminStudentAssignment.jsx @@ -0,0 +1,125 @@ +import React, { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import AdminStudentHeader from "../../components/AdminStudentHeader"; +import WeeklyOpenBlock from "../../components/WeeklyOpenBlock"; +import AssignmentInfoBlock from "../../components/AssignmentInfoBlock"; +import api from "../../api/api"; +import styles from "./AdminStudentAssignment.module.css"; + +const AdminStudentAssignment = () => { + const { studentId, week } = useParams(); + const [studentInfo, setStudentInfo] = useState(null); + const [weeks, setWeeks] = useState([]); + const [highlightCard, setHighlightCard] = useState(null); + const [selectedWeekLabel, setSelectedWeekLabel] = useState(null); + + useEffect(() => { + api.get(`/admin/users/${studentId}`).then((res) => { + setStudentInfo(res.data.data); + }); + + api + .get(`/admin/managestudent/{studentId}`, { + params: { userId: studentId }, + withCredentials: true, + }) + .then((res) => { + const formatted = res.data.data.map((weekItem) => ({ + week: weekItem.week, + label: `${weekItem.week}주차 ${weekItem.title}`, + days: weekItem.days.map((dayItem) => ({ + day: dayItem.day, + subject: weekItem.title, + tasks: dayItem.details.map((task) => ({ + id: task.id, + label: task.assignmentName, + status: task.status, + modified: false, + })), + })), + })); + + setWeeks(formatted); + + const matched = formatted.find((w) => String(w.week) === String(week)); + if (matched) { + setSelectedWeekLabel(matched.label); + if (matched.days.length > 0) { + setHighlightCard({ + weekLabel: matched.label, + day: matched.days[0].day, + tasks: matched.days[0].tasks, + }); + } + } + }); + }, [studentId, week]); + + const handleStatusChange = (weekIdx, dayIdx, taskIdx, newStatus) => { + const updated = [...weeks]; + const task = updated[weekIdx].days[dayIdx].tasks[taskIdx]; + task.status = newStatus; + task.modified = true; + setWeeks(updated); + }; + + const handleSave = async (taskId, status) => { + await api.put("/admin/assignment/status", { + assignmentId: taskId, + status, + }); + }; + + return ( +
+ window.history.back()} + /> + + {highlightCard && ( +
+ +
+ )} + +
+ {weeks.map((weekItem, weekIdx) => ( +
+

{weekItem.label}

+ {weekItem.days.map((dayItem, dayIdx) => ( +
+

{dayItem.day}   {dayItem.subject}

+
+ {dayItem.tasks.map((task, taskIdx) => ( +
+ {task.label} + + +
+ ))} +
+ +
+ ))} +
+ ))} +
+
+ ); +}; + +export default AdminStudentAssignment; \ No newline at end of file diff --git a/frontend/src/pages/admin/AdminStudentAssignment.module.css b/frontend/src/pages/admin/AdminStudentAssignment.module.css new file mode 100644 index 0000000..04e3e68 --- /dev/null +++ b/frontend/src/pages/admin/AdminStudentAssignment.module.css @@ -0,0 +1,112 @@ +.container { + display: flex; + flex-direction: column; + padding: 20px; + font-family: "Inter", sans-serif; + color: white; + background-color: #1e1e1e; +} + +/* 과제 개요 카드 (상단 형광 카드) */ +.info { + background-color: #045e07; + border-radius: 12px; + padding: 16px; + margin-bottom: 24px; +} + +/* 주차별 목록 */ +.weekList { + display: flex; + flex-direction: column; + gap: 24px; +} + +/* 주차 구간 */ +.weekBlock { + border-left: 4px solid #00c851; + background-color: #2d2d2d; + border-radius: 10px; + padding: 16px; +} + +.weekTitle { + font-size: 18px; + font-weight: bold; + color: #00ff99; + margin-bottom: 12px; +} + +/* 요일별 카드 */ +.dayCard { + background-color: #3a3a3a; + padding: 12px 16px; + border-radius: 10px; + margin-bottom: 16px; +} + +.dayLabel { + font-size: 16px; + font-weight: 600; + margin-bottom: 8px; +} + +/* 개별 과제 */ +.taskList { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 12px; +} + +.taskRow { + display: flex; + align-items: center; + gap: 10px; + background-color: #505050; + border-radius: 6px; + padding: 8px 12px; +} + +.taskLabel { + flex: 1; + font-size: 14px; + color: white; +} + +/* 드롭다운 */ +.taskRow select { + padding: 6px 8px; + border-radius: 6px; + background-color: #2a2a2a; + color: white; + border: 1px solid #777; +} + +/* save 버튼 */ +.saveButton { + padding: 4px 10px; + border-radius: 6px; + background-color: #00c851; + color: white; + font-weight: bold; + border: none; + cursor: pointer; +} + +.saveButton:disabled { + background-color: gray; + cursor: not-allowed; +} + +/* submit 버튼 */ +.submitBtn { + margin-top: 8px; + padding: 6px 12px; + border-radius: 8px; + background-color: #1fa067; + color: white; + font-weight: bold; + border: none; + cursor: pointer; +} From 4b35e53ac425968e1e001d7bd284fd3d6396c255 Mon Sep 17 00:00:00 2001 From: Imggaggu Date: Wed, 21 May 2025 20:37:07 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[Feat]=20admin=20attendance=20student=20api?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/adminattendance.js | 26 ++++++++++++++++++ .../pages/admin/AdminStudentAttendance.jsx | 27 +++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 frontend/src/api/adminattendance.js diff --git a/frontend/src/api/adminattendance.js b/frontend/src/api/adminattendance.js new file mode 100644 index 0000000..4506980 --- /dev/null +++ b/frontend/src/api/adminattendance.js @@ -0,0 +1,26 @@ +import api from "./api"; + +// api/attendanceApi.js + +export const getStudentBasicInfo = async (studentId) => { + try { + const res = await api.get(`/admin/managestudent/${studentId}`); + return res.data; + } catch (error) { + console.error("학생 기본 정보 불러오기 실패:", error); + throw error; + } +}; + +export const getStudentAttendance = async (studentId) => { + try { + const res = await api.get("/admin/attendance/user", { + params: { userId: studentId }, + withCredentials: true, + }); + return res.data; + } catch (error) { + console.error("학생 출석 정보 불러오기 실패:", error); + throw error; + } +}; diff --git a/frontend/src/pages/admin/AdminStudentAttendance.jsx b/frontend/src/pages/admin/AdminStudentAttendance.jsx index 827248b..6eed5d1 100644 --- a/frontend/src/pages/admin/AdminStudentAttendance.jsx +++ b/frontend/src/pages/admin/AdminStudentAttendance.jsx @@ -5,6 +5,7 @@ import DailyAttendanceCard from "../../components/AdminDailyAttendanceCard"; import api from "../../api/api"; import styles from "./AdminStudentAttendance.module.css"; import AdminWeeklyAttendanceList from "../../components/AdminWeeklyAttendanceList"; +import { getStudentBasicInfo, getStudentAttendance } from "../../api/adminattendance"; const AdminStudentAttendance = () => { const { studentId } = useParams(); @@ -13,22 +14,20 @@ const AdminStudentAttendance = () => { const [selectedDate, setSelectedDate] = useState(null); useEffect(() => { - // 1. 학생 정보 가져오기 - api.get(`/admin/users/${studentId}`).then((res) => { - setStudentInfo(res.data.data); - }); + const fetchData = async () => { + try { + const studentRes = await getStudentBasicInfo(studentId); + setStudentInfo(studentRes.data); - // 2. 주차별 출석 데이터 가공 - api - .get("/admin/attendance/user", { - params: { userId: studentId }, - withCredentials: true, - }) - .then((res) => { - const raw = res.data.data; - const processed = processWeeklyAttendance(raw); + const attendanceRes = await getStudentAttendance(studentId); + const processed = processWeeklyAttendance(attendanceRes.data); setAttendanceData(processed); - }); + } catch (err) { + console.error("데이터 불러오기 실패:", err); + } + }; + + fetchData(); }, [studentId]); /* From b399b35bbe34d7cead94dd7b45db5248a7ea1338 Mon Sep 17 00:00:00 2001 From: Imggaggu Date: Wed, 21 May 2025 21:20:24 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[Fix]=20assignment.js=20api=20=EC=9E=AC?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/assignment.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/src/api/assignment.js b/frontend/src/api/assignment.js index cf1a11a..323730a 100644 --- a/frontend/src/api/assignment.js +++ b/frontend/src/api/assignment.js @@ -1,6 +1,16 @@ import api from "./api"; - +/* export const fetchAssignmentsByUser = async (userId) => { const res = await api.get(`/assignment/grouped/${userId}`); return res.data; }; +*/ +export const fetchAssignmentsByUser = async (userId) => { + try { + const res = await api.get(`/api/assignment/${userId}`); + return res.data; // 백엔드가 반환하는 JSON 그대로 + } catch (err) { + console.error("과제 데이터 불러오기 실패:", err); + throw err; + } +}; \ No newline at end of file