Skip to content

Commit b14549c

Browse files
yyuneesangkyu39
andauthored
20260403 fe add adminpage absent list (#354)
* [FE]피드백 모달 수정 * [FIX] Scroll error in chrome * [FIX] rabbit review * [FIX] scroll error * [FIX] Rabbit review * [FIX] Checkin before login * [ADD]absent list modal & sort in attendance admin page * [FIX] Rabbit Review * [FIX] Error AttendanceMangement Page * [FIX] rabbit review * fix: restore missing guard clauses, React Portal, and sticky headers lost during merge --------- Co-authored-by: sangkyu39 <sangkyu.p39@gmail.com>
1 parent 2bbca4c commit b14549c

4 files changed

Lines changed: 29 additions & 19 deletions

File tree

frontend/src/components/attendancemanage/AbsenceSummaryModal.jsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import React from 'react';
1+
import { createPortal } from 'react-dom';
22
import styles from './AbsenceSummaryModal.module.css';
33

44
const AbsenceSummaryModal = ({ isOpen, onClose, userRows }) => {
55
if (!isOpen) return null;
66

77
// 결석한 기록이 있는 유저들만 필터링하고 결석 횟수 계산
8-
const absenceData = userRows
8+
const absenceData = (userRows || [])
99
.map(user => {
10-
const totalAbsences = user.attendances.filter(att => att.status === 'ABSENT').length;
11-
const totalLates = user.attendances.filter(att => att.status === 'LATE').length;
10+
const totalAbsences = (user.attendances || []).filter(att => att.status === 'ABSENT').length;
11+
const totalLates = (user.attendances || []).filter(att => att.status === 'LATE').length;
1212
return {
1313
...user,
1414
totalAbsences,
@@ -18,7 +18,7 @@ const AbsenceSummaryModal = ({ isOpen, onClose, userRows }) => {
1818
.filter(user => user.totalAbsences > 0 || user.totalLates > 0)
1919
.sort((a, b) => b.totalAbsences - a.totalAbsences || b.totalLates - a.totalLates);
2020

21-
return (
21+
const modalContent = (
2222
<div className={styles.modalOverlay} onClick={onClose}>
2323
<div
2424
className={styles.modalContent}
@@ -80,6 +80,8 @@ const AbsenceSummaryModal = ({ isOpen, onClose, userRows }) => {
8080
</div>
8181
</div>
8282
);
83+
84+
return createPortal(modalContent, document.body);
8385
};
8486

8587
export default AbsenceSummaryModal;

frontend/src/components/attendancemanage/AbsenceSummaryModal.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
padding: 24px 28px;
6868
overflow-y: auto;
6969
flex: 1;
70+
max-height: 100%;
7071
}
7172

7273
.summaryTable {
@@ -75,13 +76,16 @@
7576
}
7677

7778
.summaryTable th {
79+
position: sticky;
80+
top: -24px; /* modalBody padding-top 보정 */
7881
text-align: left;
7982
padding: 12px 14px;
8083
font-size: 14px;
8184
font-weight: 600;
8285
color: #64748b;
8386
background: #f8fafc;
8487
border-bottom: 2px solid #e2e8f0;
88+
z-index: 10;
8589
}
8690

8791
.summaryTable td {

frontend/src/components/attendancemanage/AttendanceManagementCard.jsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => {
157157
const fetchRequestIdRef = useRef(0);
158158

159159
const sortedUserRows = useMemo(() => {
160-
if (!attendanceData.rounds.length) {
161-
return attendanceData.userRows
160+
if (!(attendanceData?.rounds || []).length) {
161+
return (attendanceData?.userRows || [])
162162
.map((user, index) => ({ user, index }))
163163
.sort((a, b) => {
164164
const priorityDiff =
@@ -170,9 +170,9 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => {
170170
}
171171

172172
// 로컬 시간대 기준으로 오늘 날짜(YYYY-MM-DD)와 정렬된 회차 목록 생성
173-
const sortedRounds = [...attendanceData.rounds].sort(
173+
const sortedRounds = [...(attendanceData?.rounds || [])].sort(
174174
(a, b) =>
175-
a.roundDate.localeCompare(b.roundDate) || a.roundNumber - b.roundNumber
175+
(a.roundDate || '').localeCompare(b.roundDate || '') || (a.roundNumber || 0) - (b.roundNumber || 0)
176176
);
177177
const todayStr = new Date().toLocaleDateString('sv-SE');
178178

@@ -181,10 +181,9 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => {
181181
sortedRounds[sortedRounds.length - 1];
182182

183183
const targetRoundId = targetRound?.roundId;
184-
185-
return [...attendanceData.userRows].sort((a, b) => {
186-
const aAtt = a.attendances.find((att) => att.roundId === targetRoundId);
187-
const bAtt = b.attendances.find((att) => att.roundId === targetRoundId);
184+
return [...(attendanceData?.userRows || [])].sort((a, b) => {
185+
const aAtt = (a?.attendances || []).find((att) => att.roundId === targetRoundId);
186+
const bAtt = (b?.attendances || []).find((att) => att.roundId === targetRoundId);
188187

189188
const aStatus = aAtt?.status || 'PENDING';
190189
const bStatus = bAtt?.status || 'PENDING';
@@ -513,7 +512,7 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => {
513512
<th className={styles.nameHeader}>이름</th>
514513
<th className={styles.roleHeader}>역할</th>
515514
<th className={styles.studentIdHeader}>학번</th>
516-
{attendanceData.rounds.map((round) => (
515+
{(attendanceData?.rounds || []).map((round) => (
517516
<th key={round.roundId} className={styles.roundHeader}>
518517
{round.roundNumber}회차
519518
</th>
@@ -540,7 +539,7 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => {
540539
<td>{user.userName}</td>
541540
<td>{getRoleDisplayLabel(user.role)}</td>
542541
<td>{user.studentId}</td>
543-
{user.attendances.map((att) => {
542+
{(user.attendances || []).map((att) => {
544543
const statusClass =
545544
styles[
546545
ATTENDANCE_CONFIG[att.status]?.className ||
@@ -585,7 +584,7 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => {
585584
) : (
586585
<tr>
587586
<td
588-
colSpan={4 + attendanceData.rounds.length}
587+
colSpan={4 + (attendanceData?.rounds || []).length}
589588
className={styles.noData}
590589
>
591590
데이터가 존재하지 않습니다.

frontend/src/components/attendancemanage/SessionManagementCard.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,12 @@ const SessionManagementCard = ({ styles: commonStyles }) => {
7777
const rounds = await getRounds(selectedSessionId);
7878
setCurrentDisplayedRounds(rounds || []);
7979
} catch (e) {
80-
toast.error('라운드를 불러오지 못했습니다.');
80+
const status = e?.response?.status ?? e?.status;
81+
if (status === 403) {
82+
toast.error('세션 멤버가 아니거나 조회 권한이 없습니다.');
83+
} else {
84+
toast.error('라운드를 불러오지 못했습니다.');
85+
}
8186
setCurrentDisplayedRounds([]);
8287
}
8388
};
@@ -214,8 +219,8 @@ const SessionManagementCard = ({ styles: commonStyles }) => {
214219
</tr>
215220
</thead>
216221
<tbody>
217-
{currentDisplayedRounds.length > 0 ? (
218-
currentDisplayedRounds.map((round, index) => {
222+
{(currentDisplayedRounds || []).length > 0 ? (
223+
(currentDisplayedRounds || []).map((round, index) => {
219224
const startTime = new Date(round.startAt);
220225
const closeTime = new Date(round.closeAt);
221226

0 commit comments

Comments
 (0)