diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 5bc8878..e177e6b 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -12,15 +12,31 @@ 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"; +<<<<<<< HEAD +import AdminStudentAssignment from "./pages/admin/AdminStudentAssignment.jsx"; +======= import RequireAuth from "./components/RequireAuth"; import RequireAdmin from "./components/RequireAdmin"; +>>>>>>> 08242a5045ea08b68c40b107cc871f8b3c3446eb function App() { return ( } /> } /> +<<<<<<< HEAD + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +======= } /> +>>>>>>> 08242a5045ea08b68c40b107cc871f8b3c3446eb ); 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/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 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; +} 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]); /*