토스트 타입 추가, 재량 평가 UX 수정, 스터디 카드 뱃지 UI 및 로직 수정#403
토스트 타입 추가, 재량 평가 UX 수정, 스터디 카드 뱃지 UI 및 로직 수정#403HA-SEUNG-JEONG wants to merge 12 commits intodevelopfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthrough여러 레이아웃의 수평 오버플로우 처리 변경, FormField 프로퍼티명 표준화(counterMax → maxCharCount), ENDING_SOON 상태 추가 및 관련 UI/타입 반영, StudyActiveTicker 단순화, 이미지 업로드 및 모달 제출 흐름 일부 리팩토링이 포함된 UI·타입 변경입니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25분 Possibly related PRs
Suggested labels
시
🚥 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 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 |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/modals/edit-homework-modal.tsx (1)
130-137:⚠️ Potential issue | 🟡 Minor
maxCharCount와maxLength값 불일치
maxCharCount={5000}으로 설정되어 있지만,TextAreaInput의maxLength={1000}과 스키마의.max(1000)유효성 검사가 1000자로 제한되어 있습니다. 이로 인해 카운터 UI에 잘못된 최대값이 표시됩니다.📝 값 일치를 위한 수정 제안
<FormField<EditHomeworkFormValues, 'textContent'> name="textContent" label="과제 상세 내용" direction="vertical" required - maxCharCount={5000} + maxCharCount={1000} showCounterRight={false} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/modals/edit-homework-modal.tsx` around lines 130 - 137, The displayed maxCharCount (maxCharCount={5000}) disagrees with the actual input limit (TextAreaInput id="textContent" has maxLength={1000} and the validation schema uses .max(1000)); fix by making these values consistent: either change maxCharCount to 1000 to match TextAreaInput.maxLength and the schema, or increase TextAreaInput.maxLength and the schema .max(...) to 5000 if you intend a 5000‑char limit—update maxCharCount, TextAreaInput maxLength, and the validation rule together so all three (maxCharCount, TextAreaInput id="textContent" maxLength, and the schema .max) match.src/components/modals/create-mission-modal.tsx (1)
28-31:⚠️ Potential issue | 🟡 Minor
guide필드의 유효성 검사와 UI 제한값 불일치 (기존 이슈)Zod 스키마에서
guide필드는.max(1000)으로 제한되어 있지만,maxCharCount={5000}과maxLength={5000}으로 설정되어 있습니다. 이로 인해 사용자가 1000자 이상 5000자 이하를 입력하면 UI에서는 허용하지만 폼 제출 시 유효성 검사에서 실패할 수 있습니다.이 불일치가 의도된 것인지 확인해 주세요. 의도된 것이 아니라면 값을 일치시키는 것이 좋습니다.
📝 값 일치를 위한 수정 제안
스키마를 5000으로 변경하거나:
guide: z .string() .min(1, '수행 가이드를 입력해주세요.') - .max(1000, '1000자 이하로 작성가능합니다.'), + .max(5000, '5000자 이하로 작성가능합니다.'),또는 UI 제한을 1000으로 변경:
<FormField<CreateMissionFormValues, 'guide'> ... - maxCharCount={5000} + maxCharCount={1000} ... > <TextAreaInput ... - maxLength={5000} + maxLength={1000} ... />Also applies to: 220-220, 226-226
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/modals/create-mission-modal.tsx` around lines 28 - 31, The Zod schema for the guide field (guide: z.string().min(1).max(1000)) conflicts with the UI limits (maxCharCount={5000}, maxLength={5000}) in create-mission-modal.tsx; update them to match by either increasing the Zod max to 5000 or reducing the UI props maxCharCount and maxLength to 1000 so validation and UI are consistent, and apply the same change for the other occurrences mentioned (lines around 220 and 226) where the UI props are set; ensure you update the guide field definition and the corresponding input/textarea props together and run the form validation flow to confirm submission succeeds at the intended limit.
🧹 Nitpick comments (3)
src/app/(service)/layout.tsx (1)
41-48:overflow-x-hidden중복 적용에 대한 검토 필요.html 요소(Line 41)와 내부 div 컨테이너(Line 48) 모두에
overflow-x-hidden이 적용되어 있습니다. 기능적으로는 문제없지만, 부모 요소에서 이미 가로 스크롤을 숨기고 있으므로 내부 컨테이너의overflow-x-hidden은 불필요할 수 있습니다.의도적으로 이중 방어를 적용한 것이라면 현재 상태로 유지해도 무방합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/layout.tsx around lines 41 - 48, Remove the duplicated overflow-x-hidden by deleting it from the inner container: the html element already has className="overflow-x-hidden", so update the inner div (the element with className="flex min-h-screen w-full flex-col overflow-x-hidden") to remove overflow-x-hidden (leaving className="flex min-h-screen w-full flex-col"); if you intended defensive duplication instead, leave as-is and add a comment explaining the reason.src/components/ui/study-active-ticker.tsx (1)
62-62: 동적 class 병합은clsx+tailwind-merge로 통일해 주세요.Line [62]처럼 템플릿 문자열로 합치면 충돌 클래스 정리가 어려워집니다.
♻️ 제안 코드
+import { clsx } from 'clsx'; import { Eye, Flame } from 'lucide-react'; import { useEffect, useState } from 'react'; +import { twMerge } from 'tailwind-merge'; @@ - <div className={`rounded-200 bg-transparent pt-400 pb-150 ${className}`}> + <div + className={twMerge( + clsx('rounded-200 bg-transparent pt-400 pb-150', className), + )} + >As per coding guidelines
src/**/*.{ts,tsx}: Style components using Tailwind CSS 4 with@tailwindcss/postcssplugin, clsx, tailwind-merge, and class-variance-authority (CVA) utilities.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/study-active-ticker.tsx` at line 62, Replace the template-string class concatenation on the div with a merged class approach using clsx and tailwind-merge: build the static classes ("rounded-200 bg-transparent pt-400 pb-150") and the incoming className via clsx, then pass the result through twMerge before assigning to the div; update the JSX in the component (the div that currently uses `className={`rounded-200 bg-transparent pt-400 pb-150 ${className}`}`) to use twMerge(clsx(...)) so conflicting Tailwind classes are resolved.src/components/modals/discretionary-evaluation-modal.tsx (1)
12-17: Zod 스키마에 최대 길이 검증 추가 권장
EVALUATION_CONTENT_MAX_LENGTH를 상수로 추출한 것은 좋습니다. 하지만 Zod 스키마에는 최소 길이(min(1))만 검증하고 최대 길이는 검증하지 않습니다.TextAreaInput의maxLength속성은 클라이언트 측에서만 동작하므로, 스키마 레벨에서도 검증을 추가하면 더 안전합니다.♻️ 수정 제안
const DiscretionaryEvaluationFormSchema = z.object({ - content: z.string().min(1, '평가 내역을 입력해주세요.'), + content: z + .string() + .min(1, '평가 내역을 입력해주세요.') + .max(EVALUATION_CONTENT_MAX_LENGTH, `평가 내역은 ${EVALUATION_CONTENT_MAX_LENGTH}자를 초과할 수 없습니다.`), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/modals/discretionary-evaluation-modal.tsx` around lines 12 - 17, DiscretionaryEvaluationFormSchema currently enforces only min length for the content field; add a max length check using the existing EVALUATION_CONTENT_MAX_LENGTH constant so the schema validates content.length <= EVALUATION_CONTENT_MAX_LENGTH (e.g., chain .max(...) onto the z.string() for the content property) and provide an appropriate error message; update DiscretionaryEvaluationFormSchema to reference EVALUATION_CONTENT_MAX_LENGTH to keep validation consistent with TextAreaInput.
🤖 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/modals/question-modal.tsx`:
- Around line 85-88: The code uses new URL(uploadUrl) which throws for relative
paths; update the URL parsing logic around uploadUrl/relativePath used before
calling axiosInstanceForMultipart.put so it handles both absolute and relative
URLs — detect if uploadUrl is absolute (e.g., starts with http/https) and parse
with new URL(uploadUrl) to build relativePath, otherwise treat uploadUrl as
already-relative (or construct a URL with a base like window.location.origin)
and then derive the same pathname+search string; ensure
axiosInstanceForMultipart.put receives the correct relativePath regardless of
uploadUrl format.
In `@src/components/pages/group-study-detail-page.tsx`:
- Around line 167-174: You’re passing a startDate prop to StudyActiveTicker even
though StudyActiveTickerProps (in src/components/ui/study-active-ticker.tsx)
doesn’t define it; remove the startDate={studyDetail.basicInfo.startDate} from
the StudyActiveTicker usage in group-study-detail-page.tsx, or if the ticker
must display the start date, add startDate to the StudyActiveTickerProps
interface and implement its handling inside StudyActiveTicker (adjust prop types
and rendering accordingly).
In `@src/components/pages/premium-study-detail-page.tsx`:
- Around line 76-77: Remove the stray debug log that references an undefined
variable: delete the console.log({ basic }) call in
premium-study-detail-page.tsx (or replace it with a valid variable/reference if
the intent was to log something else). Ensure no other uses of the undefined
identifier `basic` remain in the component (eg. in PremiumStudyDetailPage or
related render logic) so the TypeScript build error is resolved.
- Around line 169-175: The StudyActiveTicker invocation is passing a
non-existent prop startDate (studyDetail.basicInfo.startDate); remove this prop
from the JSX in premium-study-detail-page.tsx so the component only receives
approvedCount and maxMembersCount (and any of the accepted props viewCount or
className if needed), or alternatively add startDate to the StudyActiveTicker
prop interface in src/components/ui/study-active-ticker.tsx and handle it inside
StudyActiveTicker — update either the usage (remove startDate) or the component
prop types/implementation (add startDate) to make them consistent.
In `@src/components/ui/study-active-ticker.tsx`:
- Line 9: Remove the unused startDate prop from the component props interface in
study-active-ticker.tsx and update all call sites to stop passing startDate;
then refactor className concatenations to use clsx (and tailwind-merge or cva if
needed) instead of template strings: replace the outer div that currently uses
`className={`rounded-200 bg-transparent pt-400 pb-150 ${className}`}` with
clsx/tn merge combining "rounded-200 bg-transparent pt-400 pb-150" and the
incoming className prop, and replace the inner element that computes classes
based on `index === currentIndex` (currently using a ternary in a template
string) with clsx that conditionally applies 'translate-y-0 opacity-100' when
index === currentIndex and 'translate-y-full opacity-0' otherwise; ensure you
import clsx (and twMerge/cva if chosen) and update the props/interface and any
tests or callers accordingly.
In `@src/features/study/group/api/group-study-types.ts`:
- Line 132: BasicInfoDetail's deletedAt is currently typed as string but must
match the Timestamps interface which defines deletedAt as string | null; update
the deletedAt property on the BasicInfoDetail type to be string | null so the
two interfaces are consistent (check the BasicInfoDetail declaration and compare
to the Timestamps interface to confirm the change).
- Around line 18-22: The OpenAPI-generated enum
GroupStudyBasicInfoResponseDtoStatusEnum is missing 'ENDING_SOON', causing type
mismatches with the local GroupStudyStatus and comparisons like status ===
'ENDING_SOON' in components (study-card-countdown-badge,
my-participating-studies-section, my-study-info-card); fix by either
regenerating the OpenAPI DTOs so the schema includes the ENDING_SOON value
(re-run the generator so GroupStudyBasicInfoResponseDtoStatusEnum contains
'ENDING_SOON') or add a small adapter that maps the generated DTO to your local
shape (create a function that accepts GroupStudyBasicInfoResponseDto,
converts/extends its status to the local GroupStudyStatus including
'ENDING_SOON', and use that adapter where components read basicInfo.status).
---
Outside diff comments:
In `@src/components/modals/create-mission-modal.tsx`:
- Around line 28-31: The Zod schema for the guide field (guide:
z.string().min(1).max(1000)) conflicts with the UI limits (maxCharCount={5000},
maxLength={5000}) in create-mission-modal.tsx; update them to match by either
increasing the Zod max to 5000 or reducing the UI props maxCharCount and
maxLength to 1000 so validation and UI are consistent, and apply the same change
for the other occurrences mentioned (lines around 220 and 226) where the UI
props are set; ensure you update the guide field definition and the
corresponding input/textarea props together and run the form validation flow to
confirm submission succeeds at the intended limit.
In `@src/components/modals/edit-homework-modal.tsx`:
- Around line 130-137: The displayed maxCharCount (maxCharCount={5000})
disagrees with the actual input limit (TextAreaInput id="textContent" has
maxLength={1000} and the validation schema uses .max(1000)); fix by making these
values consistent: either change maxCharCount to 1000 to match
TextAreaInput.maxLength and the schema, or increase TextAreaInput.maxLength and
the schema .max(...) to 5000 if you intend a 5000‑char limit—update
maxCharCount, TextAreaInput maxLength, and the validation rule together so all
three (maxCharCount, TextAreaInput id="textContent" maxLength, and the schema
.max) match.
---
Nitpick comments:
In `@src/app/`(service)/layout.tsx:
- Around line 41-48: Remove the duplicated overflow-x-hidden by deleting it from
the inner container: the html element already has className="overflow-x-hidden",
so update the inner div (the element with className="flex min-h-screen w-full
flex-col overflow-x-hidden") to remove overflow-x-hidden (leaving
className="flex min-h-screen w-full flex-col"); if you intended defensive
duplication instead, leave as-is and add a comment explaining the reason.
In `@src/components/modals/discretionary-evaluation-modal.tsx`:
- Around line 12-17: DiscretionaryEvaluationFormSchema currently enforces only
min length for the content field; add a max length check using the existing
EVALUATION_CONTENT_MAX_LENGTH constant so the schema validates content.length <=
EVALUATION_CONTENT_MAX_LENGTH (e.g., chain .max(...) onto the z.string() for the
content property) and provide an appropriate error message; update
DiscretionaryEvaluationFormSchema to reference EVALUATION_CONTENT_MAX_LENGTH to
keep validation consistent with TextAreaInput.
In `@src/components/ui/study-active-ticker.tsx`:
- Line 62: Replace the template-string class concatenation on the div with a
merged class approach using clsx and tailwind-merge: build the static classes
("rounded-200 bg-transparent pt-400 pb-150") and the incoming className via
clsx, then pass the result through twMerge before assigning to the div; update
the JSX in the component (the div that currently uses `className={`rounded-200
bg-transparent pt-400 pb-150 ${className}`}`) to use twMerge(clsx(...)) so
conflicting Tailwind classes are resolved.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (35)
src/app/(admin)/layout.tsxsrc/app/(landing)/layout.tsxsrc/app/(service)/insights/ui/blog-detail-page.tsxsrc/app/(service)/layout.tsxsrc/components/card/study-card.tsxsrc/components/filtering/study-filter.tsxsrc/components/modals/create-mission-modal.tsxsrc/components/modals/discretionary-evaluation-modal.tsxsrc/components/modals/edit-homework-modal.tsxsrc/components/modals/edit-mission-modal.tsxsrc/components/modals/question-modal.tsxsrc/components/modals/submit-homework-modal.tsxsrc/components/pages/group-study-detail-page.tsxsrc/components/pages/premium-study-detail-page.tsxsrc/components/section/group-study-info-section.tsxsrc/components/section/inquiry-section.tsxsrc/components/section/my-participating-studies-section.tsxsrc/components/summary/study-info-summary.tsxsrc/components/ui/form/form-field.tsxsrc/components/ui/image-upload-input.tsxsrc/components/ui/study-active-ticker.tsxsrc/components/ui/study-card-countdown-badge.tsxsrc/components/ui/toast.tsxsrc/components/voting/voting-create-modal.tsxsrc/features/my-page/ui/my-study-info-card.tsxsrc/features/my-page/ui/profile-edit-modal.tsxsrc/features/my-page/ui/profile-info-edit-modal.tsxsrc/features/study/group/api/group-study-types.tssrc/features/study/group/const/group-study-const.tssrc/features/study/group/model/group-study-form.schema.tssrc/features/study/group/ui/delete-group-study-member.tsxsrc/features/study/group/ui/group-notice-modal.tsxsrc/features/study/group/ui/group-study-member-item.tsxsrc/features/study/group/ui/step/step3-group.tsxsrc/features/study/interview/ui/study-done-modal.tsx
💤 Files with no reviewable changes (1)
- src/components/section/inquiry-section.tsx
| const url = new URL(uploadUrl); | ||
| const relativePath = url.pathname.replace(/^\/api\/v1\//, '') + url.search; | ||
|
|
||
| await axiosInstanceForMultipart.put(relativePath, formData); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's read the file at the specified lines
cat -n src/components/modals/question-modal.tsx | sed -n '80,95p'Repository: code-zero-to-one/study-platform-client
Length of output: 677
🏁 Script executed:
# Search for uploadUrl/imageUploadUrl type definitions and usage
rg -n "imageUploadUrl|uploadUrl" -C 5 srcRepository: code-zero-to-one/study-platform-client
Length of output: 6949
🏁 Script executed:
# Check for API response types and interfaces
rg -n "type.*Upload|interface.*Upload" -C 3 srcRepository: code-zero-to-one/study-platform-client
Length of output: 1075
🏁 Script executed:
# Search for API documentation or backend specs
fd -e md -e yaml -e json . | xargs grep -l "imageUploadUrl\|uploadUrl" 2>/dev/null | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 135
🏁 Script executed:
# Look for test files that might show expected API responses
fd -e test.ts -e spec.ts -e test.tsx | xargs grep -l "imageUploadUrl\|uploadUrl" 2>/dev/nullRepository: code-zero-to-one/study-platform-client
Length of output: 66
🏁 Script executed:
# Check for any backend configuration or API response examples
rg -n "https://|http://|/api/v1" src/features/study/group/api/question-api.ts -C 10Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
# Check if there are any comments or JSDoc about the URL format
rg -n "imageUploadUrl" src -B 3 -A 3Repository: code-zero-to-one/study-platform-client
Length of output: 1219
🏁 Script executed:
# Check the comparison code in group-study-form-modal.tsx that uses fetch directly
cat -n src/features/study/group/ui/group-study-form-modal.tsx | sed -n '155,165p'Repository: code-zero-to-one/study-platform-client
Length of output: 435
🏁 Script executed:
# Search for any axios or fetch usage with uploadUrl/imageUploadUrl
rg -n "axiosInstanceForMultipart\|fetch.*uploadUrl\|fetch.*imageUploadUrl" src -B 2 -A 2Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
# Check if there are environment variables or base URL configs that might indicate URL format
rg -n "BASE_URL|baseURL|NEXT_PUBLIC" src -C 2 | head -30Repository: code-zero-to-one/study-platform-client
Length of output: 1723
Line 85의 URL 파싱이 상대 경로에서 실패할 수 있습니다.
new URL(uploadUrl)은 uploadUrl이 상대 경로면 TypeError를 던집니다. 백엔드 API 계약에서 URL 형식(절대/상대)을 명시하지 않으므로, 상대 URL을 반환하는 경우 이미지 업로드가 항상 실패합니다.
수정 제안
- const url = new URL(uploadUrl);
- const relativePath = url.pathname.replace(/^\/api\/v1\//, '') + url.search;
+ const parsedUrl = /^https?:\/\//.test(uploadUrl)
+ ? new URL(uploadUrl)
+ : new URL(uploadUrl, window.location.origin);
+ const relativePath =
+ parsedUrl.pathname.replace(/^\/api\/v1\//, '') + parsedUrl.search;
await axiosInstanceForMultipart.put(relativePath, formData);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/modals/question-modal.tsx` around lines 85 - 88, The code uses
new URL(uploadUrl) which throws for relative paths; update the URL parsing logic
around uploadUrl/relativePath used before calling axiosInstanceForMultipart.put
so it handles both absolute and relative URLs — detect if uploadUrl is absolute
(e.g., starts with http/https) and parse with new URL(uploadUrl) to build
relativePath, otherwise treat uploadUrl as already-relative (or construct a URL
with a base like window.location.origin) and then derive the same
pathname+search string; ensure axiosInstanceForMultipart.put receives the
correct relativePath regardless of uploadUrl format.
| {/* 플로팅 정보 바 */} | ||
| <div className="mt-500 w-[1164px]"> | ||
| <StudyActiveTicker | ||
| approvedCount={studyDetail.basicInfo.approvedCount} | ||
| maxMembersCount={studyDetail.basicInfo.maxMembersCount} | ||
| startDate={studyDetail.basicInfo.startDate} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
StudyActiveTicker에 존재하지 않는 startDate prop 전달
premium-study-detail-page.tsx와 동일한 문제입니다. StudyActiveTicker 컴포넌트는 startDate prop을 지원하지 않습니다 (src/components/ui/study-active-ticker.tsx의 인터페이스 참조).
🐛 수정 제안
<div className="mt-500 w-[1164px]">
<StudyActiveTicker
approvedCount={studyDetail.basicInfo.approvedCount}
maxMembersCount={studyDetail.basicInfo.maxMembersCount}
- startDate={studyDetail.basicInfo.startDate}
/>
</div>📝 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.
| {/* 플로팅 정보 바 */} | |
| <div className="mt-500 w-[1164px]"> | |
| <StudyActiveTicker | |
| approvedCount={studyDetail.basicInfo.approvedCount} | |
| maxMembersCount={studyDetail.basicInfo.maxMembersCount} | |
| startDate={studyDetail.basicInfo.startDate} | |
| /> | |
| </div> | |
| {/* 플로팅 정보 바 */} | |
| <div className="mt-500 w-[1164px]"> | |
| <StudyActiveTicker | |
| approvedCount={studyDetail.basicInfo.approvedCount} | |
| maxMembersCount={studyDetail.basicInfo.maxMembersCount} | |
| /> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/pages/group-study-detail-page.tsx` around lines 167 - 174,
You’re passing a startDate prop to StudyActiveTicker even though
StudyActiveTickerProps (in src/components/ui/study-active-ticker.tsx) doesn’t
define it; remove the startDate={studyDetail.basicInfo.startDate} from the
StudyActiveTicker usage in group-study-detail-page.tsx, or if the ticker must
display the start date, add startDate to the StudyActiveTickerProps interface
and implement its handling inside StudyActiveTicker (adjust prop types and
rendering accordingly).
| <div className="mt-500 w-[1164px]"> | ||
| <StudyActiveTicker | ||
| approvedCount={studyDetail.basicInfo.approvedCount} | ||
| maxMembersCount={studyDetail.basicInfo.maxMembersCount} | ||
| startDate={studyDetail.basicInfo.startDate} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
StudyActiveTicker에 존재하지 않는 startDate prop 전달
StudyActiveTicker 컴포넌트의 인터페이스(src/components/ui/study-active-ticker.tsx 참조)에는 startDate prop이 정의되어 있지 않습니다. 현재 컴포넌트는 approvedCount, maxMembersCount, viewCount, className만 받습니다.
🐛 수정 제안
<div className="mt-500 w-[1164px]">
<StudyActiveTicker
approvedCount={studyDetail.basicInfo.approvedCount}
maxMembersCount={studyDetail.basicInfo.maxMembersCount}
- startDate={studyDetail.basicInfo.startDate}
/>
</div>📝 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.
| <div className="mt-500 w-[1164px]"> | |
| <StudyActiveTicker | |
| approvedCount={studyDetail.basicInfo.approvedCount} | |
| maxMembersCount={studyDetail.basicInfo.maxMembersCount} | |
| startDate={studyDetail.basicInfo.startDate} | |
| /> | |
| </div> | |
| <div className="mt-500 w-[1164px]"> | |
| <StudyActiveTicker | |
| approvedCount={studyDetail.basicInfo.approvedCount} | |
| maxMembersCount={studyDetail.basicInfo.maxMembersCount} | |
| /> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/pages/premium-study-detail-page.tsx` around lines 169 - 175,
The StudyActiveTicker invocation is passing a non-existent prop startDate
(studyDetail.basicInfo.startDate); remove this prop from the JSX in
premium-study-detail-page.tsx so the component only receives approvedCount and
maxMembersCount (and any of the accepted props viewCount or className if
needed), or alternatively add startDate to the StudyActiveTicker prop interface
in src/components/ui/study-active-ticker.tsx and handle it inside
StudyActiveTicker — update either the usage (remove startDate) or the component
prop types/implementation (add startDate) to make them consistent.
| approvedCount: number; | ||
| maxMembersCount: number; | ||
| startDate: string; // YYYY-MM-DD | ||
| startDate: string; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# 먼저 파일의 전체 내용을 확인하여 prop 정의와 사용 여부를 파악
cat -n src/components/ui/study-active-ticker.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 2760
🏁 Script executed:
# 호출부 검색
rg "<StudyActiveTicker" src --type tsx --type ts -C3Repository: code-zero-to-one/study-platform-client
Length of output: 109
🏁 Script executed:
# props 인터페이스 확인
rg -A20 "interface.*Props" src/components/ui/study-active-ticker.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 605
🏁 Script executed:
rg "StudyActiveTicker" src -t ts -t tsx -C3Repository: code-zero-to-one/study-platform-client
Length of output: 109
🏁 Script executed:
# 또는 파일 내용으로 검색
rg "StudyActiveTicker" srcRepository: code-zero-to-one/study-platform-client
Length of output: 707
🏁 Script executed:
rg -B2 -A10 "StudyActiveTicker" src/components/pages/premium-study-detail-page.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 1297
🏁 Script executed:
rg -B2 -A10 "StudyActiveTicker" src/components/pages/group-study-detail-page.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 1363
인터페이스에서 미사용 startDate prop을 제거하고 className 병합을 clsx로 리팩터링하세요.
startDate는 인터페이스에 정의되어 있으나 함수 매개변수에서 구조 분해되지 않으며 컴포넌트 본문에서 사용되지 않습니다. 호출부에서는 이미 전달하고 있으므로 인터페이스에서 제거 후 호출부 수정이 필요합니다.
또한 62번 줄과 67-71번 줄의 className 동적 결합이 템플릿 문자열을 사용하고 있으므로, 가이드라인에 따라 clsx와 tailwind-merge 또는 class-variance-authority 사용으로 변경하세요.
수정 대상 코드
// 제거할 부분 (line 9)
startDate: string;
// 리팩터링 필요 (line 62)
<div className={`rounded-200 bg-transparent pt-400 pb-150 ${className}`}>
// 리팩터링 필요 (lines 67-71)
className={`absolute inset-0 flex items-center gap-150 transition-all duration-500 ${
index === currentIndex
? 'translate-y-0 opacity-100'
: 'translate-y-full opacity-0'
}`}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/ui/study-active-ticker.tsx` at line 9, Remove the unused
startDate prop from the component props interface in study-active-ticker.tsx and
update all call sites to stop passing startDate; then refactor className
concatenations to use clsx (and tailwind-merge or cva if needed) instead of
template strings: replace the outer div that currently uses
`className={`rounded-200 bg-transparent pt-400 pb-150 ${className}`}` with
clsx/tn merge combining "rounded-200 bg-transparent pt-400 pb-150" and the
incoming className prop, and replace the inner element that computes classes
based on `index === currentIndex` (currently using a ternary in a template
string) with clsx that conditionally applies 'translate-y-0 opacity-100' when
index === currentIndex and 'translate-y-full opacity-0' otherwise; ensure you
import clsx (and twMerge/cva if chosen) and update the props/interface and any
tests or callers accordingly.
| export type GroupStudyStatus = | ||
| | 'RECRUITING' | ||
| | 'ENDING_SOON' | ||
| | 'IN_PROGRESS' | ||
| | 'COMPLETED'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# 1. 로컬 GroupStudyStatus 타입 정의 확인
echo "== Local GroupStudyStatus type =="
cat -n src/features/study/group/api/group-study-types.ts | head -30
echo -e "\n== Check if file exists and full content if small =="
wc -l src/features/study/group/api/group-study-types.tsRepository: code-zero-to-one/study-platform-client
Length of output: 1281
🏁 Script executed:
# 2. OpenAPI 생성 타입 파일 찾기
echo "== Finding OpenAPI generated files =="
fd -t f "\.ts$" src/api/openapi/ 2>/dev/null | head -20 || echo "Directory not found"
# Alternative: search for group-study related files
echo -e "\n== Finding group-study related API files =="
find . -name "*group-study*" -type f 2>/dev/null | grep -E "\.(ts|tsx)$" || echo "No files found"Repository: code-zero-to-one/study-platform-client
Length of output: 6587
🏁 Script executed:
# 3. 다운스트림 컴포넌트 확인
echo "== Checking downstream component files =="
fd -t f "study-info-summary.tsx" 2>/dev/null
fd -t f "study-card.tsx" 2>/dev/nullRepository: code-zero-to-one/study-platform-client
Length of output: 363
🏁 Script executed:
# 4. ENDING_SOON 사용처 검색
echo "== Searching for ENDING_SOON usage =="
rg "ENDING_SOON" --type ts --type tsx -n 2>/dev/null | head -30 || echo "Not found"Repository: code-zero-to-one/study-platform-client
Length of output: 119
🏁 Script executed:
# 5. OpenAPI 관련 상태 타입 검색
echo "== Searching for status type definitions in OpenAPI =="
rg "type.*Status|status.*\:" src/ -t ts -t tsx -B2 -A2 2>/dev/null | head -50 || echo "Not found"Repository: code-zero-to-one/study-platform-client
Length of output: 136
🏁 Script executed:
# OpenAPI GroupStudyFullResponseDto 확인
echo "== Checking GroupStudyFullResponseDto =="
cat -n src/api/openapi/models/group-study-full-response-dto.ts | head -50
echo -e "\n== File size check =="
wc -l src/api/openapi/models/group-study-full-response-dto.tsRepository: code-zero-to-one/study-platform-client
Length of output: 1553
🏁 Script executed:
# 다운스트림 컴포넌트에서 상태 비교 확인
echo "== study-info-summary.tsx around line 148 =="
sed -n '140,160p' src/components/summary/study-info-summary.tsx
echo -e "\n== study-card.tsx around line 136 =="
sed -n '130,150p' src/components/card/study-card.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 1634
🏁 Script executed:
# OpenAPI 모델들에서 status 필드 찾기
echo "== Searching status in OpenAPI models =="
rg "status" src/api/openapi/models/group-study*.ts -A2 -B2 | head -80
echo -e "\n== Searching for StatusEnum or similar =="
rg "enum.*Status|Status.*=" src/api/openapi/models/group-study*.ts -B2 -A5 | head -80Repository: code-zero-to-one/study-platform-client
Length of output: 12084
🏁 Script executed:
# GroupStudyListItemDto와 다른 주요 모델들 확인
echo "== GroupStudyListItemDto =="
cat -n src/api/openapi/models/group-study-list-item-dto.ts | head -60
echo -e "\n== GroupStudyDetailResponse =="
cat -n src/api/openapi/models/group-study-detail-response.ts | head -60Repository: code-zero-to-one/study-platform-client
Length of output: 2252
🏁 Script executed:
# Full verification of the mismatch
echo "== OpenAPI GroupStudyBasicInfoResponseDtoStatusEnum full definition =="
cat -n src/api/openapi/models/group-study-basic-info-response-dto.ts | grep -A5 "GroupStudyBasicInfoResponseDtoStatusEnum"
echo -e "\n== Confirm downstream usage pattern =="
rg "basicInfo.*status|status.*ENDING_SOON" src/components/ src/features/ -B2 -A2 | head -40Repository: code-zero-to-one/study-platform-client
Length of output: 3557
OpenAPI DTO의 상태 열거형이 ENDING_SOON을 포함하지 않아 타입 불일치가 발생합니다.
로컬 GroupStudyStatus 타입(라인 18-22)에 ENDING_SOON이 추가되었으나, OpenAPI 자동 생성 타입 GroupStudyBasicInfoResponseDtoStatusEnum은 'RECRUITING' | 'IN_PROGRESS' | 'COMPLETED'만 포함하고 있습니다. 이로 인해 다음 컴포넌트들에서 basicInfo.status === 'ENDING_SOON' 비교 시 TypeScript 타입 에러가 발생합니다:
study-card-countdown-badge.tsx:status === 'ENDING_SOON'my-participating-studies-section.tsx:study.status === 'ENDING_SOON'my-study-info-card.tsx:status === 'ENDING_SOON'(여러 곳)
해결 방법: OpenAPI 스키마의 상태 열거형을 재생성하여 ENDING_SOON을 포함하거나, 또는 DTO 응답을 감싸는 어댑터를 생성하여 status 필드를 확장하세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/features/study/group/api/group-study-types.ts` around lines 18 - 22, The
OpenAPI-generated enum GroupStudyBasicInfoResponseDtoStatusEnum is missing
'ENDING_SOON', causing type mismatches with the local GroupStudyStatus and
comparisons like status === 'ENDING_SOON' in components
(study-card-countdown-badge, my-participating-studies-section,
my-study-info-card); fix by either regenerating the OpenAPI DTOs so the schema
includes the ENDING_SOON value (re-run the generator so
GroupStudyBasicInfoResponseDtoStatusEnum contains 'ENDING_SOON') or add a small
adapter that maps the generated DTO to your local shape (create a function that
accepts GroupStudyBasicInfoResponseDto, converts/extends its status to the local
GroupStudyStatus including 'ENDING_SOON', and use that adapter where components
read basicInfo.status).
| rejectedCount: number; | ||
| kickedCount: number; | ||
| deletedAt: string | null; | ||
| deletedAt: string; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
wc -l src/features/study/group/api/group-study-types.tsRepository: code-zero-to-one/study-platform-client
Length of output: 135
🏁 Script executed:
cat -n src/features/study/group/api/group-study-types.ts | sed -n '125,140p'Repository: code-zero-to-one/study-platform-client
Length of output: 542
🏁 Script executed:
cat -n src/features/study/group/api/group-study-types.ts | sed -n '190,205p'Repository: code-zero-to-one/study-platform-client
Length of output: 577
🏁 Script executed:
cat -n src/features/study/group/api/group-study-types.ts | sed -n '110,135p'Repository: code-zero-to-one/study-platform-client
Length of output: 1002
🏁 Script executed:
cat -n src/features/study/group/api/group-study-types.ts | head -n 130 | tail -n 50Repository: code-zero-to-one/study-platform-client
Length of output: 1989
🏁 Script executed:
rg "interface BasicInfo[^D]" src/features/study/group/api/group-study-types.ts -A 15Repository: code-zero-to-one/study-platform-client
Length of output: 1435
🏁 Script executed:
rg "extends.*Timestamps|Timestamps.*extends" src/features/study/group/api/group-study-types.ts -B 2 -A 5Repository: code-zero-to-one/study-platform-client
Length of output: 546
🏁 Script executed:
rg "BasicInfoDetail|DetailInfo" src/features/study/group/api/group-study-types.ts | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 535
🏁 Script executed:
cat -n src/features/study/group/api/group-study-types.ts | sed -n '1,70p'Repository: code-zero-to-one/study-platform-client
Length of output: 2517
🏁 Script executed:
cat -n src/features/study/group/api/group-study-types.ts | sed -n '40,80p'Repository: code-zero-to-one/study-platform-client
Length of output: 1396
BasicInfoDetail의 deletedAt 타입을 Timestamps 인터페이스와 일치하도록 수정해 주세요.
Line 132의 deletedAt: string은 활성 스터디 응답의 null 값을 수용하지 못합니다. 동일 파일 Line 198의 Timestamps 인터페이스에서는 deletedAt: string | null로 정의되어 있어 타입 계약이 불일치합니다. BasicInfoDetail의 deletedAt을 string | null로 변경하여 일관성을 유지하세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/features/study/group/api/group-study-types.ts` at line 132,
BasicInfoDetail's deletedAt is currently typed as string but must match the
Timestamps interface which defines deletedAt as string | null; update the
deletedAt property on the BasicInfoDetail type to be string | null so the two
interfaces are consistent (check the BasicInfoDetail declaration and compare to
the Timestamps interface to confirm the change).
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/components/pages/premium-study-detail-page.tsx (1)
168-172:⚠️ Potential issue | 🔴 Critical
StudyActiveTicker에 지원되지 않는startDateprop 전달을 제거해 주세요.Line 171에서
startDate를 전달하고 있는데, 현재StudyActiveTicker의 props와 불일치하여 타입체크/빌드 실패를 유발할 수 있습니다.🐛 제안 수정안
<StudyActiveTicker approvedCount={studyDetail.basicInfo.approvedCount} maxMembersCount={studyDetail.basicInfo.maxMembersCount} - startDate={studyDetail.basicInfo.startDate} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/pages/premium-study-detail-page.tsx` around lines 168 - 172, The StudyActiveTicker invocation is passing an unsupported prop startDate (StudyActiveTicker approvedCount, maxMembersCount are valid) which causes type/build failures; remove the startDate={studyDetail.basicInfo.startDate} prop from the StudyActiveTicker call so only approvedCount and maxMembersCount are passed, and run the type checker to confirm the error is resolved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/components/pages/premium-study-detail-page.tsx`:
- Around line 168-172: The StudyActiveTicker invocation is passing an
unsupported prop startDate (StudyActiveTicker approvedCount, maxMembersCount are
valid) which causes type/build failures; remove the
startDate={studyDetail.basicInfo.startDate} prop from the StudyActiveTicker call
so only approvedCount and maxMembersCount are passed, and run the type checker
to confirm the error is resolved.
🌱 연관된 이슈
☘️ 작업 내용
info타입 추가🍀 참고사항
스크린샷 (선택)
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선사항