#113 feat: 제출 이력 패널 추가#114
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughSummary by CodeRabbit
풀이이 PR은 과제 제출 페이지에 제출 이력 조회 및 표시 기능을 추가합니다. 사용자가 이전 제출 버전을 확인하고 복구할 수 있도록 API, 스키마, 쿼리, 컴포넌트, 페이지 통합을 함께 구현합니다. 변경사항제출 이력 조회 및 UI 통합
예상 코드 리뷰 노력🎯 3 (Moderate) | ⏱️ ~25분 관련 PR
제안 라벨
제안 리뷰어
간단한 팁: Zod 검증 실패 메시지가 사용자 로그에 노출되지 않도록 로깅/에러 처리 경로를 확인하세요. 관련 문서: https://github.com/colinhacks/zod#validation. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts (1)
44-44: ⚡ Quick win불필요한 non-null assertion 제거 권장
unitId는 필수 파라미터(unitId: number)로 정의되어 있어undefined가 될 수 없습니다. Non-null assertion 연산자(!)는 불필요하며, 코드를 읽는 사람에게 혼란을 줄 수 있습니다.🧹 개선 제안
submit({ - unitId: unitId!, + unitId, assignmentId: Number(assignmentId), code, });참고: TypeScript의 non-null assertion은 컴파일러에게 "내가 확신하니 타입 체크를 건너뛰어"라고 말하는 것이므로, 타입 시스템이 이미 보장하는 경우에는 사용하지 않는 것이 좋습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts` at line 44, The non-null assertion on unitId is unnecessary; in the useAssignmentSubmission code (look for the useAssignmentSubmission function and the object/property where you set unitId: unitId!), remove the trailing "!" so it reads unitId: unitId, since unitId is already declared as a required number parameter; this simplifies the code and avoids misleading callers about potential undefined values.src/shared/lib/course.ts (1)
82-88: ⚡ Quick win완전한 날짜 단위 계산을 위해
Math.floor사용 권장
Math.round대신Math.floor를 사용하면 더 예측 가능한 동작을 보장합니다. 현재 코드는 시간대에 따라 결과가 달라질 수 있는데, 예를 들어:
- 오후 2시에 13시간 전 호출 → 0일 전 (현재 오전 1시점)
- 오전 10시에 13시간 전 호출 → 1일 전 (어제 오후 9시점)
"며칠 전"을 표현할 때는 완전히 경과한 날짓수만 계산하는 것이 자연스럽습니다.
Math.floor는 이 의도를 명확하게 드러냅니다.더불어,
ensureUTC함수는 타임존 정보를 올바르게 처리하고 있습니다. 기존 타임존 마커(Z 또는 +/-)를 유지하거나, 없을 경우 UTC로 명시적으로 표기하므로 ISO 8601 표준을 잘 따르고 있습니다.💡 개선 제안
export const formatDaysAgo = (isoString: string): string => { const midnight = (d: Date) => d.setHours(0, 0, 0, 0); - const days = Math.round( + const days = Math.floor( (midnight(new Date()) - midnight(new Date(ensureUTC(isoString)))) / 86400000 ); return days === 0 ? '오늘' : `${days}일 전`; };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/shared/lib/course.ts` around lines 82 - 88, The function formatDaysAgo currently uses Math.round causing partial days to sometimes round up; change the days calculation in formatDaysAgo to use Math.floor instead of Math.round so it counts only fully elapsed days (keep the midnight helper and ensureUTC usage as-is); update the expression that computes days (the Math.round(...) call) to Math.floor(...) to ensure consistent "몇일 전" behavior across time zones.src/pages/submit-assignment/ui/SubmissionHistoryPanel.tsx (3)
18-52: ⚡ Quick win빈 제출 내역 처리를 추가하면 좋겠습니다.
현재
submissionList가 비어있을 때 빈<ul>만 렌더링되어 사용자에게 아무 정보도 제공하지 않습니다. 빈 상태에 대한 안내 메시지를 추가하면 UX가 개선됩니다.💡 빈 상태 UI 추가 예시
const SubmissionHistoryPanel = ({ submissionList, currentCodeId, onRetrieve, }: SubmissionHistoryPanelProps) => { return ( <section aria-label='제출 내역'> + {submissionList.length === 0 ? ( + <div className='py-10 text-center text-gray-400'> + 제출 내역이 없습니다. + </div> + ) : ( <ul className='py-5 px-12.5 flex flex-col gap-5'> {submissionList.map((submission, index) => ( <li key={submission.codeId} className='flex items-center justify-between text-white font-base font-normal'> {/* ... */} </li> ))} </ul> + )} </section> ); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/submit-assignment/ui/SubmissionHistoryPanel.tsx` around lines 18 - 52, Add a friendly empty-state message when submissionList is empty: inside SubmissionHistoryPanel (the component rendering submissionList) check submissionList.length === 0 and render a visually consistent placeholder (e.g., a centered list item or div within the same <section> and <ul> markup) that explains there are no submissions yet and suggests an action; ensure the placeholder uses the same styling/spacing and accessible text (aria-label or role) and does not break existing elements that reference currentCodeId, formatDaysAgo, or onRetrieve.
31-41: ⚡ Quick winTailwind v4 패턴 개선을 고려해보세요 (선택사항).
현재 조건부 스타일링이 템플릿 리터럴과 삼항 연산자로 구현되어 있는데, 가독성 측면에서 개선할 수 있습니다. 또한
bg-[#CAC2F7]같은 임의 색상 값은 디자인 시스템 일관성을 위해 테마 색상으로 정의하는 것이 좋습니다.♻️ 가독성 개선 예시
CSS에서 테마 색상 정의:
`@theme` { --color-highlight-bg: `#CAC2F7`; }컴포넌트에서 사용:
<time dateTime={submission.submittedAt} - className={`px-3.5 py-1.5 - ${ - submission.codeId === currentCodeId - ? 'bg-[`#CAC2F7`] rounded-[35px]' - : 'bg-transparent' - } - `}> + className={`px-3.5 py-1.5 rounded-[35px] ${ + submission.codeId === currentCodeId ? 'bg-highlight-bg' : 'bg-transparent' + }`}> {formatDaysAgo(submission.submittedAt)} </time>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/submit-assignment/ui/SubmissionHistoryPanel.tsx` around lines 31 - 41, Replace the inline conditional template literal on the time element in SubmissionHistoryPanel.tsx with a clearer Tailwind v4 pattern: use a classnames/clsx utility or your project's cn helper to compose base classes plus a conditional class (referencing the time element, submission.submittedAt, submission.codeId and currentCodeId) and swap the arbitrary color bg-[`#CAC2F7`] for a design-token class (e.g., bg-highlight or a theme token like bg-primary-200) defined in your Tailwind/theme so the conditional becomes cn("px-3.5 py-1.5 rounded-[35px]", submission.codeId === currentCodeId && "bg-highlight"). Ensure formatDaysAgo(submission.submittedAt) remains unchanged.
28-28: ⚡ Quick win상태 메시지를 상수로 분리하는 것을 권장합니다.
현재 하드코딩된 "모든 테스트 통과"와 "테스트 실패" 문자열은 유지보수성과 일관성 측면에서 개선 여지가 있습니다. PR 설명에서 언급하신 것처럼 네이밍 재검토와 함께 상수로 추출하면 좋겠습니다.
♻️ 제안: 상수 분리
+const SUBMISSION_STATUS = { + SUCCESS: '모든 테스트 통과', + FAILURE: '테스트 실패', +} as const; + const SubmissionHistoryPanel = ({ submissionList, currentCodeId, onRetrieve, }: SubmissionHistoryPanelProps) => { return ( <section aria-label='제출 내역'> <ul className='py-5 px-12.5 flex flex-col gap-5'> {submissionList.map((submission, index) => ( <li key={submission.codeId} className='flex items-center justify-between text-white font-base font-normal'> <span className='flex items-center gap-6'> <StatusCircle variant={submission.isSuccess ? 'PASSED' : 'FAILED'} /> - {`#${submissionList.length - index} ${submission.isSuccess ? '모든 테스트 통과' : '테스트 실패'}`} + {`#${submissionList.length - index} ${submission.isSuccess ? SUBMISSION_STATUS.SUCCESS : SUBMISSION_STATUS.FAILURE}`} </span>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pages/submit-assignment/ui/SubmissionHistoryPanel.tsx` at line 28, Extract the hardcoded status strings in SubmissionHistoryPanel (the `#${submissionList.length - index} ${submission.isSuccess ? '모든 테스트 통과' : '테스트 실패'}` usage) into named constants (e.g., TEST_SUCCESS_LABEL and TEST_FAILURE_LABEL) placed near the top of the file or in a shared constants module, then replace the inline literals with those constants (use the constants inside the ternary for `submission.isSuccess`) to improve maintainability and consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx`:
- Around line 57-60: The query treats 0 as falsy causing enabled:
!!currentCodeId to skip valid codeId 0; update the useQuery call for
assignmentQueries.getAssignmentCode to avoid defaulting to 0 and use an explicit
nullish check: call getAssignmentCode(Number(currentCodeId)) (or only convert
when present) and set enabled to currentCodeId != null (or currentCodeId !==
undefined && currentCodeId !== null) so a legitimate 0 is allowed while
null/undefined still disables the query; locate the useQuery invocation and the
assignmentQueries.getAssignmentCode reference to apply this change.
- Around line 42-47: The computed unitId from courseDetails.units.find(...) can
be undefined; validate it before converting with Number and before calling
useAssignmentSubmission (or any logic that consumes unitId). Update
AssignmentSubmitPage to check if unitId is present (e.g., guard/early return or
conditional rendering) and only call useAssignmentSubmission or Number(unitId)
when unitId !== undefined, or provide a sensible fallback and surface an
error/loading state so you don't pass NaN into useAssignmentSubmission.
In `@src/pages/submit-assignment/ui/CodeEditor.tsx`:
- Around line 101-107: The Button rendering in CodeEditor uses the optional prop
onTerminalToggle but is always rendered; guard the render to avoid click-time
errors by only rendering the Button when onTerminalToggle is provided (or
alternatively render a disabled Button when it is undefined). Locate the Button
instance in CodeEditor.tsx and wrap it in a conditional check for
onTerminalToggle (or set disabled and no-op handler) so clicks cannot call an
undefined function.
---
Nitpick comments:
In `@src/features/assignment/submit-assignment/lib/useAssignmentSubmission.ts`:
- Line 44: The non-null assertion on unitId is unnecessary; in the
useAssignmentSubmission code (look for the useAssignmentSubmission function and
the object/property where you set unitId: unitId!), remove the trailing "!" so
it reads unitId: unitId, since unitId is already declared as a required number
parameter; this simplifies the code and avoids misleading callers about
potential undefined values.
In `@src/pages/submit-assignment/ui/SubmissionHistoryPanel.tsx`:
- Around line 18-52: Add a friendly empty-state message when submissionList is
empty: inside SubmissionHistoryPanel (the component rendering submissionList)
check submissionList.length === 0 and render a visually consistent placeholder
(e.g., a centered list item or div within the same <section> and <ul> markup)
that explains there are no submissions yet and suggests an action; ensure the
placeholder uses the same styling/spacing and accessible text (aria-label or
role) and does not break existing elements that reference currentCodeId,
formatDaysAgo, or onRetrieve.
- Around line 31-41: Replace the inline conditional template literal on the time
element in SubmissionHistoryPanel.tsx with a clearer Tailwind v4 pattern: use a
classnames/clsx utility or your project's cn helper to compose base classes plus
a conditional class (referencing the time element, submission.submittedAt,
submission.codeId and currentCodeId) and swap the arbitrary color bg-[`#CAC2F7`]
for a design-token class (e.g., bg-highlight or a theme token like
bg-primary-200) defined in your Tailwind/theme so the conditional becomes
cn("px-3.5 py-1.5 rounded-[35px]", submission.codeId === currentCodeId &&
"bg-highlight"). Ensure formatDaysAgo(submission.submittedAt) remains unchanged.
- Line 28: Extract the hardcoded status strings in SubmissionHistoryPanel (the
`#${submissionList.length - index} ${submission.isSuccess ? '모든 테스트 통과' : '테스트
실패'}` usage) into named constants (e.g., TEST_SUCCESS_LABEL and
TEST_FAILURE_LABEL) placed near the top of the file or in a shared constants
module, then replace the inline literals with those constants (use the constants
inside the ternary for `submission.isSuccess`) to improve maintainability and
consistency.
In `@src/shared/lib/course.ts`:
- Around line 82-88: The function formatDaysAgo currently uses Math.round
causing partial days to sometimes round up; change the days calculation in
formatDaysAgo to use Math.floor instead of Math.round so it counts only fully
elapsed days (keep the midnight helper and ensureUTC usage as-is); update the
expression that computes days (the Math.round(...) call) to Math.floor(...) to
ensure consistent "몇일 전" behavior across time zones.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9737464d-f9a5-4da0-a8b5-6c129634005a
⛔ Files ignored due to path filters (1)
src/assets/svg/retrieveIcon.svgis excluded by!**/*.svg
📒 Files selected for processing (10)
src/entities/assignment/api/assignmentApi.tssrc/entities/assignment/api/assignmentQueries.tssrc/entities/assignment/model/schemas.tssrc/features/assignment/submit-assignment/lib/useAssignmentSubmission.tssrc/pages/submit-assignment/AssignmentSubmitPage.tsxsrc/pages/submit-assignment/ui/CodeEditor.tsxsrc/pages/submit-assignment/ui/SubmissionHistoryPanel.tsxsrc/pages/submit-assignment/ui/Terminal.tsxsrc/shared/config/endpoints.tssrc/shared/lib/course.ts
| // unitId 찾기 | ||
| const unitId = courseDetails.units.find((unit) => | ||
| unit.assignments.some( | ||
| (assignment) => assignment.id === Number(assignmentId) | ||
| ) | ||
| )?.id; |
There was a problem hiding this comment.
unitId가 undefined일 수 있는 상황을 처리해야 합니다.
find 메서드는 조건에 맞는 항목이 없을 경우 undefined를 반환합니다. 현재 이 unitId가 66번 라인에서 Number(unitId)로 변환되면 NaN이 되어 useAssignmentSubmission 훅에 전달됩니다. 이는 API 호출이나 로직 처리 시 예상치 못한 오류를 발생시킬 수 있습니다.
🛡️ 제안: unitId 유효성 검증 추가
// unitId 찾기
const unitId = courseDetails.units.find((unit) =>
unit.assignments.some(
(assignment) => assignment.id === Number(assignmentId)
)
)?.id;
+
+ if (!unitId) {
+ // unitId가 없는 경우 처리 (에러 페이지로 리다이렉트 등)
+ throw new Error('Assignment not found in any unit');
+ }
const {data: submissionList} = useQuery({
...assignmentQueries.getAssignmentSubmissionHistory(
Number(unitId),
Number(assignmentId)
),
- enabled: !!unitId,
});
// ...
const {onSubmit, result, isSubmitPending, isModalOpen, closeModal} =
useAssignmentSubmission(
Number(unitId),
courseDetails,
Number(assignmentId),
setCurrentCodeId
);또는 조건부 렌더링으로 처리:
+ if (!unitId) {
+ return <div>과제를 찾을 수 없습니다.</div>;
+ }
+
const {data: submissionList} = useQuery({
...assignmentQueries.getAssignmentSubmissionHistory(
Number(unitId),
Number(assignmentId)
),
- enabled: !!unitId,
});🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx` around lines 42 - 47,
The computed unitId from courseDetails.units.find(...) can be undefined;
validate it before converting with Number and before calling
useAssignmentSubmission (or any logic that consumes unitId). Update
AssignmentSubmitPage to check if unitId is present (e.g., guard/early return or
conditional rendering) and only call useAssignmentSubmission or Number(unitId)
when unitId !== undefined, or provide a sensible fallback and surface an
error/loading state so you don't pass NaN into useAssignmentSubmission.
| const {data: assignmentCode} = useQuery({ | ||
| ...assignmentQueries.getAssignmentCode(Number(codeId || 0)), | ||
| enabled: !!codeId, | ||
| ...assignmentQueries.getAssignmentCode(Number(currentCodeId || 0)), | ||
| enabled: !!currentCodeId, | ||
| }); |
There was a problem hiding this comment.
currentCodeId가 0일 때의 엣지 케이스를 확인하세요.
현재 코드에서 currentCodeId || 0은 0을 기본값으로 사용하지만, 59번 라인의 enabled: !!currentCodeId는 currentCodeId가 0일 때 false가 되어 쿼리가 실행되지 않습니다. 만약 데이터베이스에서 codeId가 0부터 시작한다면 이는 버그가 될 수 있습니다.
🔍 제안: 명시적인 null 체크
const {data: assignmentCode} = useQuery({
- ...assignmentQueries.getAssignmentCode(Number(currentCodeId || 0)),
- enabled: !!currentCodeId,
+ ...assignmentQueries.getAssignmentCode(Number(currentCodeId ?? 0)),
+ enabled: currentCodeId != null,
});?? (nullish coalescing)과 != null 체크를 사용하면 0을 유효한 값으로 처리하면서도 undefined/null은 올바르게 필터링할 수 있습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/pages/submit-assignment/AssignmentSubmitPage.tsx` around lines 57 - 60,
The query treats 0 as falsy causing enabled: !!currentCodeId to skip valid
codeId 0; update the useQuery call for assignmentQueries.getAssignmentCode to
avoid defaulting to 0 and use an explicit nullish check: call
getAssignmentCode(Number(currentCodeId)) (or only convert when present) and set
enabled to currentCodeId != null (or currentCodeId !== undefined &&
currentCodeId !== null) so a legitimate 0 is allowed while null/undefined still
disables the query; locate the useQuery invocation and the
assignmentQueries.getAssignmentCode reference to apply this change.
⚙️ Related ISSUE Number
Related #113
📄 Work Description
📷 Screenshot
💬 To Reviewers
현재 제출 성공 여부 레이블을 성공 시 '모든 테스트 통과', 실패 시 '테스트 실패'로 지정했는데 살짝 어색한 감이 있어서 변경할 수도 있을 거 같습니다.
🔗 Reference