-
Notifications
You must be signed in to change notification settings - Fork 2
[FE] [FEAT] : 출석하기(담당자) 페이지 디자인 수정 및 기능 api 연결 #165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,109 @@ | ||||||
| import { useState, useEffect } from 'react'; | ||||||
| import styles from '../VerificationModal.module.css'; | ||||||
| import { useAttendance } from '../../contexts/AttendanceContext'; | ||||||
| import { getUserList } from '../../utils/attendanceManage'; | ||||||
|
|
||||||
| const AddUsersModal = () => { | ||||||
| const { sessions, selectedSessionId, handleAddUsers, closeAddUsersModal } = | ||||||
| useAttendance(); | ||||||
| const [selectedUserId, setSelectedUserId] = useState(null); | ||||||
| const [users, setUsers] = useState([]); | ||||||
|
|
||||||
| // ESC 키로 모달 또는 토스트를 닫는 기능 | ||||||
| useEffect(() => { | ||||||
| // 유저 리스트 가져오기 | ||||||
| const fetchUsers = async () => { | ||||||
| try { | ||||||
| const userList = await getUserList(); | ||||||
| setUsers(userList); | ||||||
| } catch (err) { | ||||||
| console.error('사용자 목록을 불러오는 데 실패했습니다:', err); | ||||||
| } | ||||||
| }; | ||||||
| if (selectedSessionId) { | ||||||
| fetchUsers(); | ||||||
| } | ||||||
|
|
||||||
| const handleKeyDown = (event) => { | ||||||
| if (event.key === 'Escape') { | ||||||
| closeAddUsersModal(); | ||||||
| } | ||||||
| }; | ||||||
| document.addEventListener('keydown', handleKeyDown); | ||||||
| return () => { | ||||||
| document.removeEventListener('keydown', handleKeyDown); | ||||||
| }; | ||||||
| }, [closeAddUsersModal]); | ||||||
|
|
||||||
| const handleComplete = () => { | ||||||
| const currentSession = sessions.find( | ||||||
| (s) => s.attendanceSessionId === selectedSessionId | ||||||
| ); | ||||||
|
|
||||||
| if (!currentSession) { | ||||||
| alert('세션을 먼저 선택해주세요.'); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
import { toast } from 'react-toastify'; |
||||||
| return; | ||||||
| } | ||||||
| if (!selectedUserId) { | ||||||
| alert('추가할 인원를 1명 이상 선택해주세요.'); | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 그래야겠죠? 👀 |
||||||
| return; | ||||||
| } | ||||||
gxuoo marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| handleAddUsers(selectedSessionId, selectedUserId); | ||||||
| closeAddUsersModal(); | ||||||
| }; | ||||||
|
|
||||||
| return ( | ||||||
| <div className={styles.overlay}> | ||||||
| <div className={styles.modal}> | ||||||
| <div className={styles.modalHeader}> | ||||||
| <h1>세션에 유저 추가하기</h1> | ||||||
| <button | ||||||
| type="button" | ||||||
| className={styles.closeButton} | ||||||
| onClick={() => { | ||||||
| closeAddUsersModal(); | ||||||
| }} | ||||||
| > | ||||||
| × | ||||||
| </button> | ||||||
| </div> | ||||||
|
|
||||||
| <div className={styles.modalContent}> | ||||||
| <div className={styles.inputGroup}> | ||||||
| <label htmlFor="userSelect" className={styles.label}> | ||||||
| 유저 선택 | ||||||
| </label> | ||||||
| <select | ||||||
| id="userSelect" | ||||||
| className={styles.selectInput} | ||||||
| value={selectedUserId || ''} | ||||||
| onChange={(e) => setSelectedUserId(e.target.value)} | ||||||
| > | ||||||
| <option value="" disabled> | ||||||
| ------ 유저를 선택하세요 ------ | ||||||
| </option> | ||||||
| {users && | ||||||
| users.map((user) => ( | ||||||
| <option key={user.userId} value={user.userId}> | ||||||
| {user.name} ({user.email}) | ||||||
| </option> | ||||||
| ))} | ||||||
| </select> | ||||||
| </div> | ||||||
| </div> | ||||||
|
|
||||||
| <div className={styles.modifyButtonGroup}> | ||||||
| <button | ||||||
| className={`${styles.button} ${styles.submitButton}`} | ||||||
| onClick={handleComplete} | ||||||
| > | ||||||
| 추가 | ||||||
| </button> | ||||||
| </div> | ||||||
| </div> | ||||||
| </div> | ||||||
| ); | ||||||
| }; | ||||||
|
|
||||||
| export default AddUsersModal; | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,43 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import styles from './AttendanceManagementCard.module.css'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useAttendance } from '../../contexts/AttendanceContext'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useEffect, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getRoundUserAttendance } from '../../utils/attendanceManage'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const attendanceEnglishToKorean = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PRESENT: '출석', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LATE: '지각', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ABSENT: '결석', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EXCUSED: '공결', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PENDING: '미정', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const AttendanceManagementCard = ({ styles: commonStyles }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { selectedRound, handleAttendanceChange, participants } = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useAttendance(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectedSessionId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectedRound, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAttendanceChange, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| roundAttendanceVersion, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = useAttendance(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [users, setUsers] = useState([]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fetchUsers = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (selectedSessionId && selectedRound) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userList = await getRoundUserAttendance(selectedRound); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // const sortedUsers = (userList || []).sort( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (a, b) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // new Date(`${a.date}T${a.startTime}`) - | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // new Date(`${b.date}T${b.startTime}`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setUsers(userList); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setUsers([]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fetchUsers(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [selectedSessionId, selectedRound, roundAttendanceVersion]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. useEffect 내 에러 처리 누락
useEffect(() => {
const fetchUsers = async () => {
if (selectedSessionId && selectedRound) {
- const userList = await getRoundUserAttendance(selectedRound);
- setUsers(userList);
+ try {
+ const userList = await getRoundUserAttendance(selectedRound);
+ setUsers(userList || []);
+ } catch (error) {
+ console.error('출석 정보를 불러오는 데 실패했습니다:', error);
+ setUsers([]);
+ }
} else {
setUsers([]);
}
};
fetchUsers();
}, [selectedSessionId, selectedRound, roundAttendanceVersion]);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러 처리 추가해주시면 좋을 거 같아요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gxuoo 동의해 주셔서 감사합니다! 에러 처리를 추가하면 API 호출 실패 시 사용자 경험이 개선되고 디버깅도 훨씬 쉬워질 것입니다. @NaturalSoda4552 님, 위의 제안된 코드를 참고하여 (ᵔᴥᵔ)ʃ♡ |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.attendanceManagementCardContainer}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -18,7 +51,7 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <th>이름</th> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <th>상태</th> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <th>변경</th> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <th>횟수</th> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <th>이메일</th> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </tr> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </thead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <tbody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -28,27 +61,28 @@ const AttendanceManagementCard = ({ styles: commonStyles }) => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 회차를 선택해주세요. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </tr> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : participants.length > 0 ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| participants.map((participant) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <tr key={participant.memberId}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td>{participant.name}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td>{participant.attendance}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : users.length > 0 ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| users.map((user) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <tr key={user.userId}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td>{user.userName}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td>{attendanceEnglishToKorean[user.attendanceStatus]}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <select | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={styles.attendanceSelect} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={participant.attendance} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={user.attendanceStatus} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={(e) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAttendanceChange( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| participant.memberId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.target.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAttendanceChange(user.userId, e.target.value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="출석">출석</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="결석">결석</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 출석(PRESENT), 지각(LATE), 결석(ABSENT), 공결(EXCUSED) */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="PRESENT">출석</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="ABSENT">결석</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="LATE">지각</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="EXCUSED">공결</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <option value="PENDING">미정</option> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </select> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td>-</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <td>{user.email}</td> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </tr> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CSS 규칙 중복 정의 - 스타일 충돌 위험
.inputGroup,.label,.button클래스가 파일에서 여러 번 정의되어 있습니다(라인 57-59, 67-70, 100-121 그리고 다시 191-225). CSS 캐스케이딩으로 인해 나중의 정의가 이전 정의를 덮어쓰게 되므로,sendButton,submitButton,resetPasswordButton등 기존 버튼 변수(라인 123-142)의 스타일이 예상치 못하게 변경될 수 있습니다.특히 새로운
.button정의(218-225)는 기존 정의에서 제공하는 중요한 속성들(display: inline-flex,width,height,font-family,line-height,letter-spacing등)을 잃어버립니다.제안: 중복을 제거하고 필요한 스타일을 기존 정의에 통합하거나, 새로운 선택자를 사용하여 스타일을 명확히 분리하세요.
다음은 권장 접근 방식입니다:
옵션 1: 기존 규칙 통합
.inputGroup { margin-bottom: 21px; } .inputGroup label { display: block; margin-bottom: 12px; font-weight: 500; color: #555; } -.label { +/* 기존 .label을 제거하고 필요시 새 규칙과 병합 */ +.label { display: block; - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; + font-size: 0.9rem; + font-weight: 600; + color: #555; }옵션 2: 새로운 선택자 사용
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 고쳐주시면 좋을 것 같습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gxuoo, 네, CSS 클래스 중복 문제를 해결해드리겠습니다. 새로 추가된 스타일들이 기존 스타일과 충돌하지 않도록 더 구체적인 클래스명을 사용하는 것이 좋겠습니다.
다음과 같이 수정하시면 됩니다:
변경된 클래스명:
.inputGroup→.addUserInputGroup.label→.addUserLabel.button→.addUserButton이렇게 수정하신 후,
AddUsersModal.jsx파일에서도 해당 클래스명을 동일하게 업데이트해주셔야 합니다:styles.inputGroup→styles.addUserInputGroupstyles.label→styles.addUserLabelstyles.button→styles.addUserButton이렇게 하면 기존 버튼 스타일(
.sendButton,.submitButton,.resetPasswordButton등)과 충돌 없이 새로운 모달의 스타일을 독립적으로 관리할 수 있습니다.