Conversation
[Feature/#367] 로그인/회원가입 returnUrl 복귀 처리
📝 WalkthroughWalkthrough인증 반환 경로와 초대 수락 흐름을 확장했습니다. 역할별 온보딩 투어와 계정 삭제 기능을 추가했습니다. 예산 게이지를 다중 구조로 변경하고, 대시보드 차트의 높이·로딩·오류 표시를 조정했습니다. Changes인증 및 워크스페이스 흐름
온보딩과 계정 관리
예산 및 차트
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Visitor
participant InviteAcceptPage
participant Login
participant acceptInvitaton
participant Workspace
Visitor->>InviteAcceptPage: 초대 링크 접속
InviteAcceptPage->>Login: 미로그인 상태에서 returnUrl 포함 이동
Login->>InviteAcceptPage: 인증 후 초대 페이지 복귀
InviteAcceptPage->>acceptInvitaton: 초대 토큰으로 POST 요청
acceptInvitaton-->>InviteAcceptPage: 초대 수락 응답 반환
InviteAcceptPage->>Workspace: 선택된 워크스페이스 저장 후 대시보드 이동
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 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 |
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 (1)
src/components/dashboard/charts/BudgetGaugeChart.tsx (1)
70-90: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
totalBudget이 0 이하일 때 상태 배지와 초과 문구가 서로 어긋납니다.
getSpentPercentage는totalBudget <= 0이면 항상 0을 반환합니다. 반면isOverBudget은spent > totalBudget원시값으로 별도 계산됩니다.이 때문에
totalBudget이 0이고spent가 0보다 큰 경우, 다음과 같은 모순이 발생합니다.
status는spentPct(0) 기준으로 계산되어 "안정" 배지가 표시됩니다.- 동시에
isOverBudget은 true이므로 "초과 금액" 문구(빨간색)와 "예산을 초과했습니다" 인사이트 문구가 함께 표시됩니다.예산 모니터링 대시보드에서 상태 배지와 본문 텍스트가 반대 의미를 전달하면 사용자가 실제 위험도를 오판할 수 있습니다. 캠페인 예산을 0으로 낮췄지만 이전 지출 기록이 남아있는 경우 이 상황이 실제로 발생할 수 있습니다.
status계산 시isOverBudget을 우선 반영하도록 수정하세요.🐛 제안하는 수정
- const status = getBudgetStatus(spentPct, warningThreshold, dangerThreshold); + const status = isOverBudget + ? "위험" + : getBudgetStatus(spentPct, warningThreshold, dangerThreshold);🤖 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/components/dashboard/charts/BudgetGaugeChart.tsx` around lines 70 - 90, Update the status calculation in the budget gauge logic around getBudgetStatus so isOverBudget takes precedence, including when totalBudget is zero or negative. Ensure over-budget cases receive the corresponding danger/over-budget status while preserving the existing percentage-based thresholds for non-over-budget cases.
🧹 Nitpick comments (20)
src/pages/setting/Setting.tsx (1)
116-117: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff탈퇴 모달 상태를 Zustand store로 이동하세요.
isWithdrawModalOpen은 UI 상태입니다. 현재useState로 관리합니다. 팀 상태 관리 규칙에 맞게src/store/의 Zustand store에서 상태와 setter를 관리하세요.As per coding guidelines:
State management: UI state → Zustand (src/store/).🤖 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/setting/Setting.tsx` around lines 116 - 117, Move the isWithdrawModalOpen state and its setter from the Setting component’s local useState into an appropriate Zustand store under src/store/. Update Setting to read and update this state through the store while preserving the existing modal behavior.Source: Coding guidelines
src/hooks/auth/useDeleteMyAccount.ts (1)
7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value내부 import를
@/alias로 통일하세요.두 파일이 내부 모듈에 상대 경로를 사용합니다. alias 경로로 변경하세요.
src/hooks/auth/useDeleteMyAccount.ts#L7-L7:useCoreMutationimport를@/hooks/customQuery로 변경하세요.src/components/setting/WithdrawConfirmModal.tsx#L1-L2:Button과Modalimport를@/components/common/...경로로 변경하세요.As per coding guidelines:
Use@/alias for all imports.🤖 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/hooks/auth/useDeleteMyAccount.ts` at line 7, 통일된 내부 모듈 import를 위해 src/hooks/auth/useDeleteMyAccount.ts의 useCoreMutation import를 `@/hooks/customQuery로` 변경하고, src/components/setting/WithdrawConfirmModal.tsx의 Button 및 Modal import를 각각 `@/components/common/`... alias 경로로 변경하세요.Source: Coding guidelines
src/routes/AuthRoutes.tsx (1)
32-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value로딩 대체 UI가 실제 페이지 화면과 다릅니다.
InviteAcceptPage는 전체 화면 스피너와 안내 문구를 렌더링합니다. 반면 fallback은AuthFormSkeleton으로 입력 폼 형태입니다. 청크 로딩 중에는 폼 스켈레톤이 보이고, 로딩이 끝나면 스피너 화면으로 바뀝니다. 사용자에게 화면이 두 번 바뀌는 것처럼 보입니다. 초대 화면과 형태가 비슷한 대체 UI 또는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/routes/AuthRoutes.tsx` around lines 32 - 35, Update the fallback passed to InviteAcceptPage’s loadable call so it matches the page’s full-screen spinner/instruction layout, or use null instead of AuthFormSkeleton; preserve the existing lazy import and route behavior.src/api/workspace/org.ts (1)
159-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win함수명 오타와 토큰 인코딩을 함께 정리해 주세요.
두 가지가 눈에 걸립니다.
- 함수명이
acceptInvitaton으로 오타가 있습니다. 공개 API 함수이고 아직 호출부가InviteAcceptPage.tsx한 곳뿐이니 지금 바로잡는 편이 비용이 가장 낮습니다.token을 경로에 그대로 보간합니다. 토큰에/,?,#같은 문자가 포함되면 요청 경로가 깨집니다.encodeURIComponent로 감싸 주세요.♻️ 제안 수정
-export const acceptInvitaton = async ( +export const acceptInvitation = async ( token: string, ): Promise<TAcceptInvitationResponse> => { const { data } = await axiosInstance.post< ICommonResponse<TAcceptInvitationResponse> - >(`/api/org/invitations/${token}`); + >(`/api/org/invitations/${encodeURIComponent(token)}`); return data.data; };
src/pages/workspace/InviteAcceptPage.tsx의 import와useCoreMutation인자도 함께 변경해야 합니다.🤖 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/api/workspace/org.ts` around lines 159 - 166, Rename the public acceptInvitaton function to acceptInvitation and update its import and useCoreMutation reference in InviteAcceptPage.tsx. In acceptInvitation, wrap token with encodeURIComponent before interpolating it into the invitation request path.src/pages/integration/PlatformIntegrationsPage.tsx (1)
205-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value현재 투어 스텝에 대응하는 타겟이 없습니다.
useOnboardingTour의ADMIN_STEPS와MEMBER_STEPS에는tour-platform-*선택자가 없습니다. 이 속성은 지금은 사용되지 않습니다. 후속 스텝 추가를 위한 준비라면 그대로 두어도 됩니다. 계획이 없다면 제거하는 편이 명확합니다.🤖 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/integration/PlatformIntegrationsPage.tsx` at line 205, Remove the unused data-tour attribute from the integration item element in PlatformIntegrationsPage, since useOnboardingTour’s ADMIN_STEPS and MEMBER_STEPS define no matching tour-platform-* selectors. Keep the surrounding provider rendering and item behavior unchanged.src/components/common/OnboardingTooltip.tsx (1)
31-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value건너뛰기 버튼과 진행 표시를 조금 다듬어 주세요.
두 가지입니다.
- 건너뛰기
<button>에type속성이 없습니다. 기본값은submit입니다. 지금은 폼 안이 아니라 동작에 문제가 없지만, 툴팁이 폼 위에 표시되는 경우를 대비해type="button"을 지정해 주세요.{index + 1} / {size}는 스크린 리더에서 "1 슬래시 7"처럼 읽힙니다.aria-label로 의미를 보강해 주세요.♿ 제안 수정
- <span className="font-caption text-text-muted shrink-0 pt-1"> + <span + className="font-caption text-text-muted shrink-0 pt-1" + aria-label={`전체 ${size}단계 중 ${index + 1}단계`} + > {index + 1} / {size} </span><button {...skipProps} + type="button" className="font-body2 text-text-muted hover:text-text-body transition-colors duration-150" >
skipProps가type을 직접 전달하는지 확인하고, 전달한다면 스프레드 순서를 조정해 주세요.Also applies to: 42-50
🤖 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/components/common/OnboardingTooltip.tsx` around lines 31 - 33, Update the skip button in OnboardingTooltip to explicitly use type="button", ensuring skipProps cannot override it by placing the explicit type after the spread if needed. Add an aria-label to the progress indicator containing the current step and total size so screen readers announce its meaning clearly.src/hooks/common/useOnboardingTour.ts (2)
7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win온보딩 저장소 키를 공유 상수로 만들어 주세요. 두 파일이 같은
localStorage키"hasSeenOnboarding"을 각각 따로 참조합니다. 한쪽만 값을 바꾸면 쓰기와 읽기가 어긋나서 온보딩이 매번 다시 시작되거나 영구히 표시되지 않습니다.
src/hooks/common/useOnboardingTour.ts#L7-L7:ONBOARDING_KEY를export하거나 공용 상수 모듈로 옮겨 주세요.src/layout/main/MainLayout.tsx#L161-L166: 문자열 리터럴"hasSeenOnboarding"대신 공유 상수를 import 해서 사용해 주세요.🤖 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/hooks/common/useOnboardingTour.ts` at line 7, Make ONBOARDING_KEY in src/hooks/common/useOnboardingTour.ts a shared exported constant, or move it to a common constants module. In src/layout/main/MainLayout.tsx at lines 161-166, import and use that shared constant instead of the "hasSeenOnboarding" string literal so both localStorage reads and writes use the same key.
135-140: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value중복된 삼항 분기를 정리해 주세요.
myRole === "ADMIN"일 때와 그 외 기본값이 모두ADMIN_STEPS입니다. 분기가 하나 필요 없습니다.♻️ 제안 수정
- const steps = - myRole === "MEMBER" - ? MEMBER_STEPS - : myRole === "ADMIN" - ? ADMIN_STEPS - : ADMIN_STEPS; + const steps = myRole === "MEMBER" ? MEMBER_STEPS : ADMIN_STEPS;
ADMIN_STEPS와MEMBER_STEPS는 앞의 네 스텝이 거의 동일합니다. 공통 스텝을 배열로 뽑아 재사용하는 방법도 검토해 주세요.🤖 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/hooks/common/useOnboardingTour.ts` around lines 135 - 140, In the steps selection near MEMBER_STEPS and ADMIN_STEPS, remove the redundant ADMIN comparison and use a single MEMBER_STEPS-versus-ADMIN_STEPS conditional with ADMIN_STEPS as the fallback. Optionally extract the shared leading steps into a reusable array only if it can be done without expanding the change beyond this cleanup.src/pages/workspace/InviteAcceptPage.tsx (2)
29-78: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win에러 분류를 메시지 문자열 대신 코드 기준으로 정리하는 편이 안전합니다.
현재
getInviteErrorCopy는message의 한국어/영어 부분 문자열까지 검사합니다. 서버 문구가 바뀌면 분류가 조용히 어긋납니다. 예를 들어code가지금 fallback이 있어 화면이 깨지지는 않습니다. 후속 작업으로 진행해도 됩니다.
🤖 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/workspace/InviteAcceptPage.tsx` around lines 29 - 78, Update getInviteErrorCopy to classify invitation errors primarily through an explicit mapping of confirmed server error codes, removing message substring checks from the expired, already-completed, and email-mismatch branches. Retain the existing message-based text only as the final fallback for otherwise unrecognized errors, and preserve the current Korean title and description for each mapped category.
115-116: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value렌더 단계의
acceptInviteRef.current = acceptInvite;를 정리해 주세요.
mutate참조는 그대로 사용해도 됩니다. 렌더 본문에서 변경은 피해 보기 위해 ref 갱신을 관련 effect 안으로 옮기는 것만 고려하면 됩니다.♻️ 제안 수정
- const aceptInviteRef = useRef(acceptInvite); - acceptInviteRef.current = acceptInvite; + const acceptInviteRef = useRef(acceptInvite); + useEffect(() => { + acceptInviteRef.current = acceptInvite; + }, [acceptInvite]);🤖 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/workspace/InviteAcceptPage.tsx` around lines 115 - 116, Move the acceptInviteRef.current assignment out of the render body and into the relevant effect in InviteAcceptPage, while continuing to use the existing mutate reference unchanged.src/layout/main/MainLayout.tsx (1)
161-166: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value렌더 본문에서
localStorage를 직접 읽습니다.
MainLayout은headerRight,campaignDetailHeaderTitle등 로컬 상태 때문에 자주 리렌더됩니다. 그때마다localStorage.getItem이 동기로 실행됩니다. 값은 마운트 시점에 한 번만 필요하므로useState초기화 함수로 한 번만 읽는 편이 낫습니다.♻️ 제안 수정
+ const [shouldAutoStartTour] = useState( + () => !localStorage.getItem(ONBOARDING_STORAGE_KEY), + );{(myRole !== null || (workspaces !== undefined && workspaces.length === 0)) && ( - <OnboardingTour - autoStart={!localStorage.getItem("hasSeenOnboarding")} - /> + <OnboardingTour autoStart={shouldAutoStartTour} /> )}키 리터럴 중복은 별도 코멘트에서 함께 다룹니다.
🤖 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/layout/main/MainLayout.tsx` around lines 161 - 166, Update MainLayout’s onboarding state handling so localStorage.getItem is called once through a useState initializer during mount, then pass the stored value to OnboardingTour’s autoStart without reading localStorage in the render body. Keep the existing onboarding condition unchanged.src/components/dashboard/platform/SinglePlatformView.tsx (1)
210-245: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win오류 메시지에 라이브 리전 속성이 없습니다.
Line 217-219의 오류 문구는 로딩 완료 후 동적으로 나타납니다. 스크린 리더 사용자는 이 변경을 인지하지 못합니다.
role="alert"를 추가하면 상태 변경이 전달됩니다. 빈 데이터 문구(Line 242-244)에는role="status"가 적합합니다.♿ 제안 수정
) : isBudgetError ? ( - <div className="flex flex-1 items-center justify-center px-4 py-4 text-center font-body2 text-info-red"> + <div + role="alert" + className="flex flex-1 items-center justify-center px-4 py-4 text-center font-body2 text-info-red" + > 예산 데이터를 불러오지 못했습니다. </div>근거: "접근성: 시맨틱 HTML, ARIA 속성 사용 확인".
🤖 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/components/dashboard/platform/SinglePlatformView.tsx` around lines 210 - 245, Update the budget error message container in the isBudgetError branch to include role="alert", and add role="status" to the empty-data message container in the final fallback branch. Keep the existing messages, styling, and rendering conditions unchanged.Source: Path instructions
src/components/dashboard/platform/PlatformTrafficChart.tsx (3)
285-294: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win이
useEffect는 초기 렌더에서 상태를 다시 설정합니다.
chartHeight와isHeightReady의 초기값(Line 105-108)이 이미fillHeight를 반영합니다. 이 effect는 마운트 시 같은 값을 다시 설정합니다.fillHeight는SinglePlatformView에서platform으로 결정되므로 컴포넌트 수명 동안 바뀌지 않습니다. 실질적으로 동작하지 않는 effect입니다.
fillHeight변경에 대응하려면 effect보다keyprop 또는 렌더 중 파생 계산이 더 명확합니다. effect를 제거하고 초기값만 유지하는 방안을 검토해 주세요.근거: path instructions "Hook 사용: useEffect 의존성 배열 및 불필요한 사용 검토".
🤖 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/components/dashboard/platform/PlatformTrafficChart.tsx` around lines 285 - 294, Remove the fillHeight synchronization useEffect and its state-reset logic, since the initial chartHeight and isHeightReady state already derive from fillHeight and fillHeight remains stable for the component lifetime. Preserve the existing initial-state behavior and avoid replacing it with another effect.Source: Path instructions
372-410: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win오류 화면과 빈 데이터 화면에 라이브 리전 속성이 없습니다.
Line 372의 오류 화면과 Line 402의 빈 데이터 화면은 데이터 상태 변경에 따라 동적으로 나타납니다. 스크린 리더 사용자는 이 변경을 인지하지 못합니다. 차트 영역에는
role="group"과aria-label을 추가하셨습니다(Line 431-432). 같은 수준으로 상태 화면도 처리해 주세요. 오류 화면에는role="alert", 빈 데이터 화면에는role="status"가 적합합니다.Line 396의 Skeleton은 장식 요소이므로
aria-hidden="true"를 고려해 주세요.근거: path instructions "접근성: 시맨틱 HTML, ARIA 속성 사용 확인".
🤖 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/components/dashboard/platform/PlatformTrafficChart.tsx` around lines 372 - 410, Update the error state returned by the PlatformTrafficChart component to include role="alert", and update the empty timeSeriesData state to include role="status", preserving their existing labels and content. Mark the Skeleton shown while data is unavailable with aria-hidden="true" because it is decorative.Source: Path instructions
176-184: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
chartOptions와series가 매 렌더마다 새 객체로 생성됩니다.이 컴포넌트는
memo로 감싸져 있습니다. 그러나chartOptions(Line 176)와series(Line 267)는 렌더마다 새 객체입니다. 호버, 포커스, 높이 갱신 같은 내부 상태 변경이 발생하면ReactApexChart가 새 참조를 받아 차트를 다시 계산합니다.fillHeight모드에서는ResizeObserver가 상태를 갱신하므로 영향이 더 큽니다.
useMemo로 감싸면 불필요한 재계산을 줄일 수 있습니다. 의존성은platformColor,xMin,xMax,yMax,anomalyTimestamp,anomalyY,fillHeight,seriesData입니다.근거: path instructions "성능: 불필요한 리렌더링 체크. React.memo, useCallback 사용 검토".
🤖 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/components/dashboard/platform/PlatformTrafficChart.tsx` around lines 176 - 184, Memoize the chartOptions object and series value in PlatformTrafficChart using useMemo to keep stable references across renders. Include platformColor, xMin, xMax, yMax, anomalyTimestamp, anomalyY, fillHeight, and seriesData in the relevant dependency arrays, while preserving the existing chart configuration and series contents.Source: Path instructions
src/components/dashboard/platform/skeleton/PlatformSkeleton.tsx (2)
149-167: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Platform*스켈레톤을 Overview 도메인이 직접 import합니다.
src/components/dashboard/overview/skeleton/OverviewSkeleton.tsx의OverviewBudgetGaugeSkeleton이PlatformDualBudgetGaugeSkeleton을 재사용합니다. 이름은 platform 도메인 전용을 뜻하지만 실제로는 공용입니다. overview → platform 방향의 도메인 간 의존이 생깁니다.예산 게이지 스켈레톤을 공용 위치(예:
components/common/skeleton/또는components/dashboard/charts/skeleton/)로 옮기고BudgetGaugeSkeleton계열 이름을 쓰는 방식을 검토해 주세요. 동작 변경은 없습니다.근거: 코딩 가이드라인 "Reusable components (buttons, inputs, modals, cards) prioritize
components/common/. Add to domain folder only if not available."🤖 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/components/dashboard/platform/skeleton/PlatformSkeleton.tsx` around lines 149 - 167, Move the reusable PlatformDualBudgetGaugeSkeleton and related budget gauge skeleton components out of the platform-specific module into an appropriate shared skeleton location, renaming them to the BudgetGaugeSkeleton naming family. Update OverviewBudgetGaugeSkeleton and all other consumers to import the shared symbols, preserving the existing rendering and layout behavior.Source: Coding guidelines
70-109: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value두 스켈레톤의 마크업이 거의 동일합니다.
PlatformBudgetGaugeCompactSkeleton과PlatformSingleBudgetGaugeSkeleton은 헤더, 진행률 바, 양끝 수치, 인사이트 영역 구조가 같습니다. 차이는 간격 클래스(mb-3대mb-6)와 헤더 구성뿐입니다.compact플래그를 받는 단일 컴포넌트로 합치면 게이지 레이아웃 변경 시 한 곳만 수정하면 됩니다. 지금 단계에서는 선택 사항입니다.Also applies to: 112-147
🤖 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/components/dashboard/platform/skeleton/PlatformSkeleton.tsx` around lines 70 - 109, Consolidate PlatformBudgetGaugeCompactSkeleton and PlatformSingleBudgetGaugeSkeleton into one shared skeleton component with a compact option. Reuse the common header, progress bar, endpoint values, and insight markup, while preserving the existing header differences and mb-3 versus mb-6 spacing through the option. Update callers to use the unified component.src/utils/dashboard/budget.ts (1)
125-140: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
SHOW_BUDGET_GAUGE_INSIGHT상수가 사용되지 않습니다.Line 16에서
SHOW_BUDGET_GAUGE_INSIGHT를 정의하고toGaugeProps의 기본값으로 사용합니다. 그런데 Line 133은showInsight: true를 하드코딩합니다. 상수를 바꿔도 실제 동작은 변하지 않습니다. 상수를 전달하거나showInsight지정을 제거해 기본값을 사용하세요.반환 타입도 명시하면
IBudgetQueryData와의 계약 이탈을 컴파일 시점에 잡을 수 있습니다.♻️ 제안 리팩터
-export function toBudgetQueryData( - data: IBudgetResponse, - provider?: TProviderType, -) { +export function toBudgetQueryData( + data: IBudgetResponse, + provider?: TProviderType, +): IBudgetQueryData { const viewModel = mapBudgetResponseToViewModel(data, provider); const isCompact = viewModel.slices.length > 1; const gauges = viewModel.slices.map((slice) => - toGaugeProps(slice, { compact: isCompact, showInsight: true }), + toGaugeProps(slice, { + compact: isCompact, + showInsight: SHOW_BUDGET_GAUGE_INSIGHT, + }), );
IBudgetQueryData를 타입 import에 추가해 주세요.import type { IBudgetGaugeProps, IBudgetQueryData, IBudgetSlice, IBudgetViewModel, } from "`@/types/dashboard/budget`";근거: "타입 안정성: TypeScript 타입의 명확성 확인".
🤖 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/utils/dashboard/budget.ts` around lines 125 - 140, Update toBudgetQueryData to use the existing SHOW_BUDGET_GAUGE_INSIGHT constant instead of hardcoding showInsight: true, or omit the option to rely on toGaugeProps defaults. Add IBudgetQueryData to the type imports and annotate toBudgetQueryData’s return type as IBudgetQueryData.Source: Path instructions
src/types/dashboard/budget.ts (1)
1-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value라벨 유니온이 표시 문자열과 결합되어 있습니다.
TBudgetGaugeLabel은 화면에 그대로 출력되는 한국어 문자열입니다.BudgetGaugeChart.tsx의isBudgetTypeLabel도 이 문자열을 직접 비교합니다. 지금은 동작에 문제가 없습니다. 다국어 지원이나 라벨 문구 변경이 생기면 타입과 비교 로직을 함께 수정해야 합니다. 식별자 키(예:"LIFETIME" | "DAILY" | "GOOGLE_META" | "NAVER")와 표시 문자열 맵을 분리하는 방식을 검토해 주세요. 지금 단계에서는 선택 사항입니다.🤖 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/types/dashboard/budget.ts` around lines 1 - 13, 분류 식별자와 화면 표시 문자열이 결합된 TBudgetGaugeLabel을 분리해 유지보수성을 높이세요. TBudgetGaugeLabel은 안정적인 키 값만 포함하도록 변경하고, 표시 문자열은 별도의 라벨 맵에서 관리하며 BudgetGaugeChart.tsx의 isBudgetTypeLabel 비교도 해당 키를 사용하도록 갱신하세요.src/hooks/dashboard/useBudget.ts (1)
1-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
TProviderType는 정의 위치로 가져오세요.
src/hooks/dashboard/useBudget.ts와@/types/dashboard/provider가 직접 사용하는 곳에서TProviderType을@/types/dashboard/provider에서 가져오세요.overview.ts에서도 re-export하고 있지만, 정의 파일 경로로 통일해야 나중에 재내보내기를 제거할 때 import가 깨지지 않습니다.🤖 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/hooks/dashboard/useBudget.ts` around lines 1 - 11, Update the TProviderType import in useBudget.ts to use the defining "`@/types/dashboard/provider`" module instead of "`@/types/dashboard/overview`", and apply the same direct import path in any direct consumers such as overview.ts. Leave unrelated type imports unchanged.
🤖 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/components/common/OnboardingTour.tsx`:
- Around line 21-33: Update the redirect conditions in the OnboardingTour
useEffect so myRole === null is treated as a loading state and never triggers
navigation. Only redirect once the role is resolved; if empty-workspace
onboarding is required, pass an explicit prop representing an empty workspace
list and use that instead. Keep the existing ADMIN and MEMBER redirects and
ensure the dependency array includes any new prop used.
In `@src/components/dashboard/platform/PlatformTrafficChart.tsx`:
- Around line 298-322: Update the updateHeight callback in the useLayoutEffect
to validate the raw measured height before applying Math.max, so zero or
non-positive measurements do not update chart state. Consolidate the 300px
minimum currently represented by MIN_CHART_HEIGHT and fillContainerClassName’s
min-h-75 into one source of truth, and use that shared value for both the
JavaScript clamp and container styling.
In `@src/components/sidebar/Sidebar.tsx`:
- Around line 209-212: Align the role-specific tour targets with the menus
rendered by Sidebar.tsx after filterNavByRole(). Verify that
MEMBER_STEPS.tour-workspace and ADMIN_STEPS.tour-integrations reference items
actually available to their respective roles, updating the data-tour assignment
or splitting/skipping steps by role as needed.
In `@src/hooks/common/useOnboardingTour.ts`:
- Around line 123-133: Update handleEvent so EVENTS.TARGET_NOT_FOUND only stops
the tour and does not write ONBOARDING_KEY to localStorage; persist the
completion flag exclusively when status is STATUS.FINISHED or STATUS.SKIPPED.
In `@src/pages/auth/Login.tsx`:
- Around line 46-50: Update the onboarding completion check in Login.tsx (lines
46-50) and RedirectPage.tsx (lines 43-47) to use a user-specific server
completion state or storage key containing the authenticated user identifier
before choosing the /workspace redirect. Apply the same user-scoped behavior in
both login flows and preserve the existing safe return URL handling for users
who have completed onboarding.
In `@src/pages/workspace/InviteAcceptPage.tsx`:
- Around line 199-213: Update the status 안내 containers in the needLogin and
loading views to use role="status" with aria-live="polite" so screen readers
announce state changes. Keep countdown numbers from being announced every second
by applying aria-hidden to the numeric countdown portion while leaving the
surrounding 안내 text live.
In `@src/utils/dashboard/budget.ts`:
- Around line 89-105: Unify gauge-count decisions around the view model: in
src/utils/dashboard/budget.ts lines 89-105, update mapPlatformBudgetViewModel to
create the daily slice only when data.daily exists, passing data.daily directly
without the toAmountSlice fallback; in
src/components/dashboard/platform/SinglePlatformView.tsx line 91, use
budgetData.gauges.length > 1 for the rendered layout while retaining
supportsDailyBudget(platform) only for pre-data skeleton selection.
---
Outside diff comments:
In `@src/components/dashboard/charts/BudgetGaugeChart.tsx`:
- Around line 70-90: Update the status calculation in the budget gauge logic
around getBudgetStatus so isOverBudget takes precedence, including when
totalBudget is zero or negative. Ensure over-budget cases receive the
corresponding danger/over-budget status while preserving the existing
percentage-based thresholds for non-over-budget cases.
---
Nitpick comments:
In `@src/api/workspace/org.ts`:
- Around line 159-166: Rename the public acceptInvitaton function to
acceptInvitation and update its import and useCoreMutation reference in
InviteAcceptPage.tsx. In acceptInvitation, wrap token with encodeURIComponent
before interpolating it into the invitation request path.
In `@src/components/common/OnboardingTooltip.tsx`:
- Around line 31-33: Update the skip button in OnboardingTooltip to explicitly
use type="button", ensuring skipProps cannot override it by placing the explicit
type after the spread if needed. Add an aria-label to the progress indicator
containing the current step and total size so screen readers announce its
meaning clearly.
In `@src/components/dashboard/platform/PlatformTrafficChart.tsx`:
- Around line 285-294: Remove the fillHeight synchronization useEffect and its
state-reset logic, since the initial chartHeight and isHeightReady state already
derive from fillHeight and fillHeight remains stable for the component lifetime.
Preserve the existing initial-state behavior and avoid replacing it with another
effect.
- Around line 372-410: Update the error state returned by the
PlatformTrafficChart component to include role="alert", and update the empty
timeSeriesData state to include role="status", preserving their existing labels
and content. Mark the Skeleton shown while data is unavailable with
aria-hidden="true" because it is decorative.
- Around line 176-184: Memoize the chartOptions object and series value in
PlatformTrafficChart using useMemo to keep stable references across renders.
Include platformColor, xMin, xMax, yMax, anomalyTimestamp, anomalyY, fillHeight,
and seriesData in the relevant dependency arrays, while preserving the existing
chart configuration and series contents.
In `@src/components/dashboard/platform/SinglePlatformView.tsx`:
- Around line 210-245: Update the budget error message container in the
isBudgetError branch to include role="alert", and add role="status" to the
empty-data message container in the final fallback branch. Keep the existing
messages, styling, and rendering conditions unchanged.
In `@src/components/dashboard/platform/skeleton/PlatformSkeleton.tsx`:
- Around line 149-167: Move the reusable PlatformDualBudgetGaugeSkeleton and
related budget gauge skeleton components out of the platform-specific module
into an appropriate shared skeleton location, renaming them to the
BudgetGaugeSkeleton naming family. Update OverviewBudgetGaugeSkeleton and all
other consumers to import the shared symbols, preserving the existing rendering
and layout behavior.
- Around line 70-109: Consolidate PlatformBudgetGaugeCompactSkeleton and
PlatformSingleBudgetGaugeSkeleton into one shared skeleton component with a
compact option. Reuse the common header, progress bar, endpoint values, and
insight markup, while preserving the existing header differences and mb-3 versus
mb-6 spacing through the option. Update callers to use the unified component.
In `@src/hooks/auth/useDeleteMyAccount.ts`:
- Line 7: 통일된 내부 모듈 import를 위해 src/hooks/auth/useDeleteMyAccount.ts의
useCoreMutation import를 `@/hooks/customQuery로` 변경하고,
src/components/setting/WithdrawConfirmModal.tsx의 Button 및 Modal import를 각각
`@/components/common/`... alias 경로로 변경하세요.
In `@src/hooks/common/useOnboardingTour.ts`:
- Line 7: Make ONBOARDING_KEY in src/hooks/common/useOnboardingTour.ts a shared
exported constant, or move it to a common constants module. In
src/layout/main/MainLayout.tsx at lines 161-166, import and use that shared
constant instead of the "hasSeenOnboarding" string literal so both localStorage
reads and writes use the same key.
- Around line 135-140: In the steps selection near MEMBER_STEPS and ADMIN_STEPS,
remove the redundant ADMIN comparison and use a single
MEMBER_STEPS-versus-ADMIN_STEPS conditional with ADMIN_STEPS as the fallback.
Optionally extract the shared leading steps into a reusable array only if it can
be done without expanding the change beyond this cleanup.
In `@src/hooks/dashboard/useBudget.ts`:
- Around line 1-11: Update the TProviderType import in useBudget.ts to use the
defining "`@/types/dashboard/provider`" module instead of
"`@/types/dashboard/overview`", and apply the same direct import path in any
direct consumers such as overview.ts. Leave unrelated type imports unchanged.
In `@src/layout/main/MainLayout.tsx`:
- Around line 161-166: Update MainLayout’s onboarding state handling so
localStorage.getItem is called once through a useState initializer during mount,
then pass the stored value to OnboardingTour’s autoStart without reading
localStorage in the render body. Keep the existing onboarding condition
unchanged.
In `@src/pages/integration/PlatformIntegrationsPage.tsx`:
- Line 205: Remove the unused data-tour attribute from the integration item
element in PlatformIntegrationsPage, since useOnboardingTour’s ADMIN_STEPS and
MEMBER_STEPS define no matching tour-platform-* selectors. Keep the surrounding
provider rendering and item behavior unchanged.
In `@src/pages/setting/Setting.tsx`:
- Around line 116-117: Move the isWithdrawModalOpen state and its setter from
the Setting component’s local useState into an appropriate Zustand store under
src/store/. Update Setting to read and update this state through the store while
preserving the existing modal behavior.
In `@src/pages/workspace/InviteAcceptPage.tsx`:
- Around line 29-78: Update getInviteErrorCopy to classify invitation errors
primarily through an explicit mapping of confirmed server error codes, removing
message substring checks from the expired, already-completed, and email-mismatch
branches. Retain the existing message-based text only as the final fallback for
otherwise unrecognized errors, and preserve the current Korean title and
description for each mapped category.
- Around line 115-116: Move the acceptInviteRef.current assignment out of the
render body and into the relevant effect in InviteAcceptPage, while continuing
to use the existing mutate reference unchanged.
In `@src/routes/AuthRoutes.tsx`:
- Around line 32-35: Update the fallback passed to InviteAcceptPage’s loadable
call so it matches the page’s full-screen spinner/instruction layout, or use
null instead of AuthFormSkeleton; preserve the existing lazy import and route
behavior.
In `@src/types/dashboard/budget.ts`:
- Around line 1-13: 분류 식별자와 화면 표시 문자열이 결합된 TBudgetGaugeLabel을 분리해 유지보수성을 높이세요.
TBudgetGaugeLabel은 안정적인 키 값만 포함하도록 변경하고, 표시 문자열은 별도의 라벨 맵에서 관리하며
BudgetGaugeChart.tsx의 isBudgetTypeLabel 비교도 해당 키를 사용하도록 갱신하세요.
In `@src/utils/dashboard/budget.ts`:
- Around line 125-140: Update toBudgetQueryData to use the existing
SHOW_BUDGET_GAUGE_INSIGHT constant instead of hardcoding showInsight: true, or
omit the option to rely on toGaugeProps defaults. Add IBudgetQueryData to the
type imports and annotate toBudgetQueryData’s return type as IBudgetQueryData.
🪄 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 Plus
Run ID: ff53c87b-2d83-47e7-ae77-732a1a4bcf19
⛔ Files ignored due to path filters (2)
package.jsonis excluded by none and included by nonepnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yamland included by none
📒 Files selected for processing (39)
src/api/auth/auth.tssrc/api/workspace/org.tssrc/components/auth/flows/signup/ProfileSetupStep.tsxsrc/components/common/OnboardingTooltip.tsxsrc/components/common/OnboardingTour.tsxsrc/components/common/error/ErrorLayout.tsxsrc/components/dashboard/charts/BudgetGaugeChart.tsxsrc/components/dashboard/charts/TrafficChart.tsxsrc/components/dashboard/charts/trafficChart.config.tssrc/components/dashboard/overview/skeleton/OverviewSkeleton.tsxsrc/components/dashboard/platform/PlatformTrafficChart.tsxsrc/components/dashboard/platform/SinglePlatformView.tsxsrc/components/dashboard/platform/skeleton/PlatformSkeleton.tsxsrc/components/setting/WithdrawConfirmModal.tsxsrc/components/sidebar/Sidebar.tsxsrc/constants/dashboard/trafficChartHeights.tssrc/hooks/auth/useDeleteMyAccount.tssrc/hooks/auth/useSocialLogin.tssrc/hooks/common/useOnboardingTour.tssrc/hooks/dashboard/useBudget.tssrc/layout/main/MainLayout.tsxsrc/pages/auth/Login.tsxsrc/pages/auth/RedirectPage.tsxsrc/pages/auth/Signup.tsxsrc/pages/dashboard/overview/OverviewDashboard.tsxsrc/pages/dashboard/overview/sections/OverviewBudgetSection.tsxsrc/pages/dashboard/overview/sections/OverviewKpiSection.tsxsrc/pages/integration/PlatformIntegrationsPage.tsxsrc/pages/setting/Setting.tsxsrc/pages/workspace/InviteAcceptPage.tsxsrc/pages/workspace/Workspace.tsxsrc/routes/AuthRoutes.tsxsrc/routes/Router.tsxsrc/styles/utilities.csssrc/types/dashboard/budget.tssrc/types/dashboard/common.tssrc/types/workspace/workspace.tssrc/utils/auth/returnUrl.tssrc/utils/dashboard/budget.ts
💤 Files with no reviewable changes (1)
- src/pages/dashboard/overview/OverviewDashboard.tsx
🚨 관련 이슈
N/A
✨ 변경사항
✏️ 작업 내용
N/A
😅 미완성 작업
N/A
📢 논의 사항 및 참고 사항
N/A
Summary by CodeRabbit