Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build": "CI=false react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/pages/curriculum/CurriculumPage.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@

/* 카드 행 */
.cardsRow {
margin: 0 auto;
display: flex;
gap: 20px;
flex-wrap: wrap;
align-items: flex-start;
justify-content: flex-start;
}

/* 세션 카드 */
Expand All @@ -70,8 +72,8 @@
border: 1px solid #eee;
border-radius: 20px;
padding: 30px;
max-width: 335px;
flex: 1;
min-width: 200px;
width: calc(28% - 14px);
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}

Expand Down
140 changes: 49 additions & 91 deletions frontend/src/pages/pirocheck/students/StudentDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,12 @@ import Logo2 from '../../../assets/images/logo2.svg';
import Toggle1 from '../../../assets/images/icon_togle1.svg';
import Toggle2 from '../../../assets/images/icon_togle2.svg';

const IS_MOCK = true;

const MOCK_DETAIL = {
deposit: { amount: 100000, ascentDefence: 10000 },
weeks: [
{
week: 1,
days: [
{
day: 'TUESDAY',
sessionDate: '2026-06-24',
sessionTitles: 'HTML/CSS 기초, Git 기초',
attendances: [
{ attendanceId: 1, attendanceOrder: '1차', attended: true },
{ attendanceId: 2, attendanceOrder: '2차', attended: true },
{ attendanceId: 3, attendanceOrder: '3차', attended: true },
],
assignments: [
{ assignmentItemId: 1, title: '코딩앵무 클론 코딩', submitted: 'SUBMITTED' },
{ assignmentItemId: 2, title: '피로그래밍 페이지 클론 코딩', submitted: 'SUBMITTED' },
],
},
{
day: 'THURSDAY',
sessionDate: '2026-06-26',
sessionTitles: 'JS 기초, JS 심화',
attendances: [
{ attendanceId: 4, attendanceOrder: '1차', attended: false },
{ attendanceId: 5, attendanceOrder: '2차', attended: false },
{ attendanceId: 6, attendanceOrder: '3차', attended: false },
],
assignments: [
{ assignmentItemId: 3, title: '코딩앵무 클론 코딩', submitted: 'NOT_SUBMITTED' },
],
},
{
day: 'SATURDAY',
sessionDate: '2026-06-28',
sessionTitles: 'DB 개론',
attendances: [
{ attendanceId: 7, attendanceOrder: '1차', attended: false },
{ attendanceId: 8, attendanceOrder: '2차', attended: false },
{ attendanceId: 9, attendanceOrder: '3차', attended: false },
],
assignments: [],
},
],
},
{ week: 2, days: [] },
{ week: 3, days: [] },
{ week: 4, days: [] },
{ week: 5, days: [] },
],
};
const IS_MOCK = false;

const dayLabel = { TUESDAY: 'TUE', THURSDAY: 'THU', SATURDAY: 'SAT' };
const statusOptions = ['SUBMITTED', 'LATE', 'NOT_SUBMITTED'];
const statusLabel = { SUBMITTED: '성공', LATE: '미달', NOT_SUBMITTED: '실패' };

// 커리큘럼 데이터에서 날짜별 세션 제목 추출
function extractSessionTitles(curriculums, sessionDate) {
const day = curriculums.find(c => c.sessionDate === sessionDate);
if (!day || !day.sessions) return '';
return day.sessions.map(s => s.title).join(', ');
}

function WeekBlock({ weekData, onChange }) {
const [isOpen, setIsOpen] = useState(false);
const [openDays, setOpenDays] = useState({});
Expand Down Expand Up @@ -181,35 +121,38 @@ function StudentDetail() {
const [defence, setDefence] = useState('');

useEffect(() => {
if (IS_MOCK) {
setData(MOCK_DETAIL);
setDefence(MOCK_DETAIL.deposit.ascentDefence.toString());
return;
}

const fetchData = async () => {
// TODO: GET /api/admin/{userId}/deposit/view
// TODO: GET /api/admin/admin/student/{userId}/status/{week} (1~5주차)

// 커리큘럼에서 세션 제목 가져오기
const curriculumRes = await authFetch('/api/curriculums');
const curriculums = await curriculumRes.json();

// weeks 데이터에 sessionTitles 추가
// const mergedWeeks = weeks.map(w => ({
// ...w,
// days: w.days.map(d => ({
// ...d,
// sessionTitles: extractSessionTitles(curriculums, d.sessionDate),
// }))
// }));
try {
// 보증금 조회
const depositRes = await authFetch(`/api/deposit/${userId}/deposit/view`);
const depositData = await depositRes.json();
setDefence(depositData.ascentDefence.toString());

// 주차별 출석/과제 조회
const weekResults = await Promise.all(
[1, 2, 3, 4, 5].map(w =>
authFetch(`/api/admin/admin/student/${userId}/status/${w}`)
.then(r => r.json())
.catch(() => ({ week: w, days: [] }))
)
);

setData({
deposit: depositData,
weeks: weekResults,
});
} catch (e) {}
};
fetchData();
}, [userId]);

const handleSaveDefence = async () => {
if (IS_MOCK) { alert('저장됨 (임시)'); return; }
// TODO: PUT /api/admin/{userId}/deposit-defend
await authFetch(`/api/deposit/${userId}/deposit/defence`, {
method: 'PATCH',
body: JSON.stringify({ ascentDefence: Number(defence) }),
});
alert('저장됐습니다!');
};

const handleStatusChange = (type, week, day, id, value) => {
Expand Down Expand Up @@ -243,20 +186,35 @@ function StudentDetail() {
};

const handleSaveAll = async () => {
if (IS_MOCK) { alert('전체 저장됨 (임시)'); return; }
// TODO: PATCH /api/admin/users/{userId}/weeks/{week} 주차별로 호출
await Promise.all(
data.weeks.map(w =>
Promise.all(
w.days.map(d => {
const body = {
attendances: d.attendances.map(a => ({
attendanceId: a.attendanceId,
status: a.attended,
})),
assignments: d.assignments.map(a => ({
assignmentItemId: a.assignmentItemId,
submitted: a.submitted,
})),
};
return authFetch(`/api/admin/users/${userId}/weeks/${w.week}`, {
method: 'PATCH',
body: JSON.stringify(body),
});
})
)
)
);
alert('저장됐습니다!');
};

if (!data) return null;

return (
<div className={styles.container}>
{IS_MOCK && (
<div className={styles.mockBanner}>
⚠️ 현재 임시 데이터로 표시 중입니다.
</div>
)}

<div className={styles.card}>
<div className={styles.profileArea}>
<img src={ProfileImg} className={styles.profileImg} alt="profile" />
Expand Down
Loading