[Feat] WTH-246 : 출석 기록 조회 페이지 UI 구현#36
Conversation
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthrough출석 기록 조회 페이지와 관련 컴포넌트·타입·상수가 추가되었고, 기존 출석 카드에 클릭·키보드로 Changes
Sequence DiagramsequenceDiagram
actor User
participant AttendanceContent as AttendanceContent
participant Router as Next Router
participant AttendanceHistoryPage as /attendance/history (Page)
participant AttendanceHistoryContent as AttendanceHistoryContent
participant UI as UI Elements
User->>AttendanceContent: 카드 클릭 또는 Enter/Space
AttendanceContent->>Router: router.push('/attendance/history')
Router->>AttendanceHistoryPage: 라우트 로드
AttendanceHistoryPage->>AttendanceHistoryContent: summary prop 전달
AttendanceHistoryContent->>AttendanceHistoryContent: attendances 존재 여부 검사
alt attendances 존재
AttendanceHistoryContent->>UI: 요약 카드 및 각 기록 렌더
else 비어있음
AttendanceHistoryContent->>UI: "출석 기록이 없습니다." 렌더
end
UI->>User: 화면 표시
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-i5jwuu5mg-weethsite-4975s-projects.vercel.app |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(private)/(main)/attendance/history/page.tsx:
- Around line 4-55: Add a clear, trackable guard and ticket tag around the mock
data to prevent accidental production merge: keep the mockSummary constant but
prepend a TODO comment like "TODO(WTH-246): DO NOT MERGE - replace with API
before release", and add a runtime safety check inside AttendanceHistoryPage (or
before passing to AttendanceHistoryContent) that asserts mocks are only used in
dev or when a dedicated flag is set (e.g., check process.env.NODE_ENV ===
'development' or a env var like NEXT_PUBLIC_USE_MOCKS) and throw/log an error if
mockSummary is used in production; reference the symbols mockSummary,
AttendanceHistoryPage, and AttendanceHistoryContent when implementing the
comment and the guard.
In `@src/components/attendance/AttendanceContent.tsx`:
- Around line 54-59: The Card currently only has an onClick handler which
prevents keyboard users from activating navigation; make the Card focusable and
keyboard-activatable by adding tabIndex={0} and role="button" (or render it as a
native <button>/<a>), and implement an onKeyDown handler that listens for Enter
and Space to call router.push('/attendance/history') (same navigation logic as
the onClick). Update the Card usage in AttendanceContent.tsx so the interactive
behavior is consistent across onClick and onKeyDown and ensure any aria-label or
accessible text remains correct.
In `@src/components/attendance/AttendanceHistoryContent.tsx`:
- Around line 27-28: The JSX in AttendanceHistoryContent (the divs using
className strings) is using hardcoded spacing/size utilities (max-w-[1025px],
px-[36px], w-[49px], py-[2px], gap-[17px], pt-[15px], pb-[19px] and the
instances noted at 75-75, 110-110); replace each hardcoded value with the
corresponding design token class (e.g., max-w-*, px-*, w-*, py-*, gap-*, pt-*,
pb-* tokens) or, if no existing token maps to the needed size, open a proposal
to add named tokens and then use those token classes instead; update className
strings in AttendanceHistoryContent.tsx (the outer container and the inner flex
container and any elements using
w-[49px]/py-[2px]/gap-[17px]/pt-[15px]/pb-[19px]) to use token classes only.
- Around line 73-82: The Tag currently treats any record.status !== 'ATTEND' as
"결석", causing AttendanceStatus.PENDING to be misrendered; update the conditional
logic in AttendanceHistoryContent.tsx (the Tag rendering that references
record.status) to explicitly branch for each status (e.g., 'ATTEND', 'PENDING',
'ABSENT' or whatever AttendanceStatus enum values are) and map each to the
correct label and className (button variant and background/text classes) instead
of a binary ATTEND vs not-ATTEND check so PENDING shows the correct label and
styling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 129ba966-2b32-45b5-979f-61265e190bcb
📒 Files selected for processing (5)
src/app/(private)/(main)/attendance/history/page.tsxsrc/components/attendance/AttendanceContent.tsxsrc/components/attendance/AttendanceHistoryContent.tsxsrc/components/attendance/index.tssrc/types/attendance.ts
| // TODO: API 연동 시 실제 데이터로 교체 | ||
| const mockSummary: AttendanceSummary = { | ||
| total: 5, | ||
| attendanceCount: 3, | ||
| absenceCount: 2, | ||
| attendances: [ | ||
| { | ||
| id: 5, | ||
| status: 'ABSENT', | ||
| title: '5주차 정기모임', | ||
| start: '2026-02-23T10:00:00.000Z', | ||
| end: '2026-02-23T12:00:00.000Z', | ||
| location: '공학관 401호', | ||
| }, | ||
| { | ||
| id: 4, | ||
| status: 'ATTEND', | ||
| title: '4주차 정기모임', | ||
| start: '2026-02-16T10:00:00.000Z', | ||
| end: '2026-02-16T12:00:00.000Z', | ||
| location: '공학관 401호', | ||
| }, | ||
| { | ||
| id: 3, | ||
| status: 'ABSENT', | ||
| title: '3주차 정기모임', | ||
| start: '2026-02-09T10:00:00.000Z', | ||
| end: '2026-02-09T12:00:00.000Z', | ||
| location: '공학관 401호', | ||
| }, | ||
| { | ||
| id: 2, | ||
| status: 'ATTEND', | ||
| title: '2주차 정기모임', | ||
| start: '2026-02-02T10:00:00.000Z', | ||
| end: '2026-02-02T12:00:00.000Z', | ||
| location: '공학관 401호', | ||
| }, | ||
| { | ||
| id: 1, | ||
| status: 'ATTEND', | ||
| title: '1주차 정기모임', | ||
| start: '2026-01-26T10:00:00.000Z', | ||
| end: '2026-01-26T12:00:00.000Z', | ||
| location: '공학관 401호', | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| export default function AttendanceHistoryPage() { | ||
| return <AttendanceHistoryContent summary={mockSummary} />; | ||
| } |
There was a problem hiding this comment.
mock 데이터의 잔존 리스크를 줄이는 장치가 있으면 좋겠습니다.
현재는 UI 확인용으로 적절하지만, 배포 전 미교체를 방지할 수 있도록 추적 이슈/플래그(예: TODO(WTH-246): API 교체 전 병합 금지)를 함께 두는 것을 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(private)/(main)/attendance/history/page.tsx around lines 4 - 55,
Add a clear, trackable guard and ticket tag around the mock data to prevent
accidental production merge: keep the mockSummary constant but prepend a TODO
comment like "TODO(WTH-246): DO NOT MERGE - replace with API before release",
and add a runtime safety check inside AttendanceHistoryPage (or before passing
to AttendanceHistoryContent) that asserts mocks are only used in dev or when a
dedicated flag is set (e.g., check process.env.NODE_ENV === 'development' or a
env var like NEXT_PUBLIC_USE_MOCKS) and throw/log an error if mockSummary is
used in production; reference the symbols mockSummary, AttendanceHistoryPage,
and AttendanceHistoryContent when implementing the comment and the guard.
| <Card | ||
| variant="onlyText" | ||
| overline="출석" | ||
| title="출석 기록" | ||
| onClick={() => router.push('/attendance/history')} | ||
| /> |
There was a problem hiding this comment.
키보드 접근성 없이 클릭 내비게이션만 연결되어 있습니다.
현재 구현은 포커스 후 Enter/Space로 이동이 보장되지 않아 키보드 사용자에게 기능 접근이 막힐 수 있습니다.
🔧 제안 수정
<Card
variant="onlyText"
overline="출석"
title="출석 기록"
onClick={() => router.push('/attendance/history')}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ router.push('/attendance/history');
+ }
+ }}
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Card | |
| variant="onlyText" | |
| overline="출석" | |
| title="출석 기록" | |
| onClick={() => router.push('/attendance/history')} | |
| /> | |
| <Card | |
| variant="onlyText" | |
| overline="출석" | |
| title="출석 기록" | |
| onClick={() => router.push('/attendance/history')} | |
| onKeyDown={(e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault(); | |
| router.push('/attendance/history'); | |
| } | |
| }} | |
| /> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/attendance/AttendanceContent.tsx` around lines 54 - 59, The
Card currently only has an onClick handler which prevents keyboard users from
activating navigation; make the Card focusable and keyboard-activatable by
adding tabIndex={0} and role="button" (or render it as a native <button>/<a>),
and implement an onKeyDown handler that listens for Enter and Space to call
router.push('/attendance/history') (same navigation logic as the onClick).
Update the Card usage in AttendanceContent.tsx so the interactive behavior is
consistent across onClick and onKeyDown and ensure any aria-label or accessible
text remains correct.
| <div className="mx-auto flex w-full max-w-[1025px] flex-col gap-700 pt-600 pb-800"> | ||
| <div className="flex flex-col items-start gap-200 self-stretch px-[36px]"> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
하드코딩된 arbitrary spacing/size 클래스는 토큰 클래스로 치환해 주세요.
max-w-[1025px], px-[36px], w-[49px], py-[2px], gap-[17px], pt-[15px], pb-[19px]는 디자인 토큰 우선 규칙과 충돌합니다. 기존 토큰으로 대체하거나, 토큰이 없다면 먼저 토큰 추가 합의를 거쳐 주세요.
As per coding guidelines: "Always use design token classes first (text-, bg-, typo-, p-, gap-*) — no hardcoded values; ask before adding new tokens".
Also applies to: 75-75, 110-110
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/attendance/AttendanceHistoryContent.tsx` around lines 27 - 28,
The JSX in AttendanceHistoryContent (the divs using className strings) is using
hardcoded spacing/size utilities (max-w-[1025px], px-[36px], w-[49px], py-[2px],
gap-[17px], pt-[15px], pb-[19px] and the instances noted at 75-75, 110-110);
replace each hardcoded value with the corresponding design token class (e.g.,
max-w-*, px-*, w-*, py-*, gap-*, pt-*, pb-* tokens) or, if no existing token
maps to the needed size, open a proposal to add named tokens and then use those
token classes instead; update className strings in AttendanceHistoryContent.tsx
(the outer container and the inner flex container and any elements using
w-[49px]/py-[2px]/gap-[17px]/pt-[15px]/pb-[19px]) to use token classes only.
…into WTH-246-출석-기록-조회-페이지-UI-구현
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과❌ TypeScript: 실패 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/attendance/AttendanceHistoryContent.tsx`:
- Line 17: The build fails because the imported constant name
(ATTENDANCE_ATTENDANCE_STATUS_CONFIG) does not match the identifier used in the
component (ATTENDANCE_STATUS_CONFIG); update the import in
AttendanceHistoryContent.tsx to import { ATTENDANCE_STATUS_CONFIG } from
'@/constants/attendance' (or alternatively rename all usages to
ATTENDANCE_ATTENDANCE_STATUS_CONFIG) so the import symbol and the usage
(ATTENDANCE_STATUS_CONFIG) are consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1e0db108-512a-40ce-9cbe-577ecaaaad1f
📒 Files selected for processing (3)
src/components/attendance/AttendanceContent.tsxsrc/components/attendance/AttendanceHistoryContent.tsxsrc/constants/attendance.ts
✅ Files skipped from review due to trivial changes (1)
- src/constants/attendance.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/attendance/AttendanceContent.tsx
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-3wtqbdgzj-weethsite-4975s-projects.vercel.app |
| attendances.map((record) => { | ||
| const startDate = new Date(record.start); | ||
| const endDate = new Date(record.end); | ||
|
|
There was a problem hiding this comment.
렌더 안에서 new Date()와 날짜 포맷팅이 반복되고 있어서, 표시용 데이터로 한 번 변환한 뒤 렌더하면 JSX가 더 단순해질 것 같습니다!!
const attendanceItems = attendances.map((record) => {
const startDate = new Date(record.start);
const endDate = new Date(record.end);
return {
...record,
statusConfig: ATTENDANCE_STATUS_CONFIG[record.status],
formattedDate: `${formatKoreanDate(startDate)} (${formatTime(startDate)}~${formatTime(endDate)})`,
};
});이런 식으로 분리해두면 렌더부가 조금 더 읽기 쉽게 정리할 수 있을 것 같아욤!!
✅ PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
📸 스크린샷 or 실행영상
/attendance/history에서 확인 가능합니다!🎸 기타 사항 or 추가 코멘트
출석 기록 조회 페이지가 새로 나와서,, ui만 추가 구현했습니당!
게시판이랑 적당히 병렬적으로 작업하는 게 편할 것 같아서,, 미리 해뒀어용
Summary by CodeRabbit