Skip to content

[Deploy] develop → main 배포 반영 - #406

Merged
jjjsun merged 70 commits into
mainfrom
develop
Aug 6, 2026
Merged

[Deploy] develop → main 배포 반영#406
jjjsun merged 70 commits into
mainfrom
develop

Conversation

@jjjsun

@jjjsun jjjsun commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

🚨 관련 이슈

N/A

✨ 변경사항

  • 🐞 BugFix Something isn't working
  • 💻 CrossBrowsing Browser compatibility
  • 🌏 Deploy Deploy
  • 🎨 Design Markup & styling
  • 📃 Docs Documentation writing and editing (README.md, etc.)
  • ✨ Feature Feature
  • 🔨 Refactor Code refactoring
  • ⚙️ Setting Development environment setup
  • ✅ Test Test related (storybook, jest, etc.)

✏️ 작업 내용

N/A

😅 미완성 작업

N/A

📢 논의 사항 및 참고 사항

N/A

💬 리뷰어 가이드 (P-Rules)
P1: 필수 반영 (Critical) - 버그 가능성, 컨벤션 위반. 해결 전 머지 불가.
P2: 적극 권장 (Recommended) - 더 나은 대안 제시. 가급적 반영 권장.
P3: 제안 (Suggestion) - 아이디어 공유. 반영 여부는 드라이버 자율.
P4: 단순 확인/칭찬 (Nit) - 사소한 오타, 칭찬 등 피드백.

Summary by CodeRabbit

  • 새 기능

    • Meta, Google, Naver 광고 캠페인의 예산을 플랫폼별로 수정할 수 있습니다.
    • 캠페인 상세 화면에서 플랫폼별 예산, 지출액, 잔여율을 확인할 수 있습니다.
    • 광고·캠페인 상태 일괄 변경과 트래킹 링크 발급을 지원합니다.
    • 광고 플랫폼 데이터 동기화 및 Naver 동기화 기간 설정을 추가했습니다.
    • AI 분석 공유 리포트 조회와 워크스페이스 소유권 양도를 지원합니다.
    • 워크스페이스 소유자 표시와 전체 멤버 조회를 추가했습니다.
  • 개선 사항

    • 광고 목록, 타임라인, 설정 및 워크스페이스 화면의 태블릿 반응형 레이아웃을 개선했습니다.
    • 초대 수락 처리와 모달 포커스·스크롤 동작을 안정화했습니다.
    • 관리자에게만 알림 연동 설정이 표시됩니다.

YermIm and others added 30 commits August 3, 2026 19:40
[Feature/#384] 캠페인 상세 플랫폼별 예산·광고 모아보기 UI
[Feature/#381] 조직 소유권 양도 UI 및 API 연동
[Feature/#377] AI 분석 결과 조직 공유 조회 API 연동
YermIm and others added 24 commits August 5, 2026 16:49
[Design/#394] 타임라인/워크스페이스/설정/알림 tablet/Desktop/mobile 반응형
[Feature/#385] 캠페인 상세 플랫폼 예산 수정 및 ads 훅 패턴 정리
[Feature/#396] 랜딩 GuideTimeline을 실제 TimelineBar UI에 맞춰 갱신
[Bugfix/#397] 알림 Slack/Discord ADMIN에게만 노출
[Feature/#398] 플랫폼 연동 수동 동기화
@jjjsun jjjsun self-assigned this Aug 6, 2026
@jjjsun jjjsun added the 🌏 Deploy 배포 관련 label Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

광고 플랫폼별 예산 수정과 상태 관리, 플랫폼 광고 데이터 동기화, 공유 AI 리포트 재사용, 워크스페이스 소유권 양도, 타임라인 및 설정 화면의 반응형 UI가 추가 또는 변경되었습니다.

Changes

광고 관리 및 예산 기능

Layer / File(s) Summary
광고 예산 계약과 변환 유틸리티
src/api/ads/budget.ts, src/types/ads/*, src/utils/ads/*, src/lib/queryKeys.ts
Meta·Google·Naver 예산 API 계약과 플랫폼별 예산 검증, payload 생성, 게이지 변환, 일괄 요청 처리가 추가되었습니다.
플랫폼별 광고 및 예산 UI
src/components/ads/*, src/components/common/modal/Modal.tsx
플랫폼별 광고 섹션, 예산 게이지, 예산 편집 모달, 플랫폼 열 숨김, 모달 종료 콜백과 mousedown 처리가 추가되었습니다.
광고 조회와 상태 변경 통합
src/hooks/ads/*, src/pages/ads/list/*
광고 조회를 useCoreQuery로 전환하고 광고·캠페인 상태 변경과 추적 URL 발급을 mutation 및 쿼리 무효화 흐름으로 통합했습니다.

플랫폼 동기화와 AI 분석

Layer / File(s) Summary
플랫폼 동기화 계약과 실행 흐름
src/api/integration/*, src/types/integration/*, src/components/integration/*, src/pages/integration/*, src/utils/integration/*
Meta·Google·Naver 동기화 API, Naver 날짜 검증, 동기화 결과 토스트, 동기화 모달과 카드 상태 연결을 추가했습니다.
공유 AI 리포트 조회와 fallback
src/api/dashboard/aiAnalysis.ts, src/types/dashboard/aiAnalysis.ts, src/hooks/dashboard/useAiAnalysisReport.ts, src/components/dashboard/ai-report/*, src/constants/dashboard/overviewMetricsRange.ts
조직별 리포트 목록을 조회하고 사용 가능한 공유 리포트를 우선 사용하며, 없을 때 분석 POST 요청을 실행하도록 변경했습니다.

워크스페이스 소유권과 멤버 관리

Layer / File(s) Summary
워크스페이스 API와 멤버 계약
src/api/workspace/org.ts, src/types/workspace/workspace.ts
초대 수락 함수명을 수정하고 토큰 인코딩, 소유권 변경 API, 전체 멤버 cursor 조회, 생성자 식별자를 추가했습니다.
소유권 양도 흐름
src/pages/workspace/WorkspaceSetting.tsx, src/components/workspace/*, src/pages/workspace/MemberManagement.tsx
생성자에게 관리자 후보와 소유권 양도 UI를 표시하고 선택한 멤버에게 소유권을 양도하도록 연결했습니다.
워크스페이스와 공통 모달 상호작용
src/components/common/modal/Modal.tsx, src/components/common/select/SearchSelect.tsx, src/components/workspace/InviteMemberModal.tsx, src/pages/workspace/Workspace.tsx
모달 종료 완료 콜백, 외부 클릭 capture 처리, 검색 목록의 flow 배치와 초대 모달 반응형 스타일을 추가했습니다.

타임라인과 설정 UI

Layer / File(s) Summary
랜딩 타임라인 컴포넌트 전환
src/constants/landing/timeline.ts, src/components/landing/GuideTimeline.tsx, src/components/timeline/*
랜딩 타임라인을 공용 열·캠페인 바 데이터와 TimelineAxis, TimelineGrid, TimelineBar 기반으로 전환했습니다.
타임라인 반응형 레이아웃
src/pages/dashboard/timeline/Timeline.tsx, src/components/timeline/*
타임라인 생성, 선택기, 성능 패널, 상태 범례와 스켈레톤의 태블릿 배치를 변경했습니다.
관리자 알림과 설정 화면
src/pages/setting/Setting.tsx, src/components/setting/*, src/components/sidebar/LogoutConfirmModal.tsx
관리자에게만 Slack·Discord 설정을 표시하고 저장하도록 변경했으며 여러 설정 화면의 반응형 여백과 버튼 배치를 조정했습니다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: seojegyeong, yermim

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning 템플릿의 섹션과 배포 체크는 작성했지만, 실제 변경사항이 많은데 작업 내용이 N/A로 남아 있습니다. 조직 소유권 양도, AI 리포트, 플랫폼 예산, 동기화, 반응형 UI 등 주요 변경사항과 미완성 작업을 구체적으로 작성해 주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed 제목이 develop 브랜치의 변경사항을 main 브랜치에 배포 반영하는 PR의 목적을 간결하게 설명합니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/components/landing/GuideTimeline.tsx (1)

22-34: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

컴포넌트 내부 <style>를 공용 스크롤바 유틸리티로 옮겨 주세요.

src/components/landing/GuideTimeline.tsx:22에는 TSX 파일 내부에 인라인 CSS가 들어갑니다. custom-scrollbar 스타일을 src/styles/utilities.css에 공용 클래스로 정의하고, GuideTimeline에서는 해당 클래스만 사용하도록 변경하세요.

🤖 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/landing/GuideTimeline.tsx` around lines 22 - 34, Move the
custom-scrollbar rules from the inline style block in GuideTimeline into the
shared utilities stylesheet, defining the reusable utility class there. Remove
the component-level style block and update GuideTimeline to rely only on the
shared custom-scrollbar class.

Source: Coding guidelines

src/pages/ads/list/CampaignDetail.tsx (1)

59-68: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Number() 변환 결과가 NaN일 수 있습니다.

경로 파라미터가 숫자가 아니면 orgIdNumprojectIdNumNaN이 됩니다. NaNnull이 아닙니다. 결과는 다음과 같습니다.

  • useUpdateAdStatusorgId == null 가드를 통과합니다. NaN이 요청 URL에 그대로 들어갑니다.
  • Line 481의 orgIdNum != null 조건을 통과합니다. EditPlatformBudgetModalNaN을 받습니다.

useAdListNumber.isFinite 검사를 하므로 조회만 막힙니다. 변경 요청은 막히지 않습니다. 변환 지점에서 한 번에 정규화해 주세요.

🛠 제안 diff
-  const orgIdNum = orgId ? Number(orgId) : null;
-  const projectIdNum = projectId ? Number(projectId) : null;
+  const toPositiveId = (value?: string) => {
+    const parsed = value ? Number(value) : NaN;
+    return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
+  };
+  const orgIdNum = toPositiveId(orgId);
+  const projectIdNum = toPositiveId(projectId);
🤖 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/ads/list/CampaignDetail.tsx` around lines 59 - 68, Normalize
orgIdNum and projectIdNum at their conversion points so non-numeric path
parameters become null rather than NaN. Update the declarations near
useCampaignDetail, useAdList, and useUpdateAdStatus to use finite-number
validation, preserving valid numeric IDs and ensuring downstream orgId == null
and orgIdNum != null guards reject invalid values.
src/components/ads/AdListTable.tsx (1)

87-95: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

플랫폼별 테이블이 여러 개일 때 전체 선택 체크박스 레이블이 중복됩니다.

CampaignDetail.tsx는 플랫폼 섹션마다 AdListTable을 렌더링합니다. 각 테이블의 전체 선택 체크박스는 모두 "표시 중인 광고 전체 선택"이라는 동일한 aria-label을 가집니다. 스크린리더 사용자는 어떤 플랫폼의 전체 선택인지 구분할 수 없습니다.

섹션 구분용 레이블을 props로 받아 붙여 주세요.

🛠 제안 diff
 interface IAdsListTableProps {
   ads: IAd[];
   embedded?: boolean;
   hidePlatformColumn?: boolean;
+  /** 여러 테이블이 동시에 보일 때 접근성 레이블 구분용 */
+  selectAllLabel?: string;
   selectedAdIds: ReadonlySet<number>;
   onToggleAd: (adId: number) => void;
   onToggleSelectAllVisible: (operableIds: readonly number[]) => void;
 }
-            aria-label="표시 중인 광고 전체 선택"
+            aria-label={selectAllLabel ?? "표시 중인 광고 전체 선택"}

CampaignDetail.tsx에서는 selectAllLabel={${platform} 광고 전체 선택} 형태로 전달하면 됩니다.

As per 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/ads/AdListTable.tsx` around lines 87 - 95, Update
AdListTable’s props to accept a section-specific selectAllLabel and use it for
the select-all checkbox aria-label instead of the fixed text. In CampaignDetail,
pass selectAllLabel={`${platform} 광고 전체 선택`} for each platform-rendered
AdListTable.

Source: Path instructions

🧹 Nitpick comments (9)
src/components/common/select/SearchSelect.tsx (1)

121-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

조건부 클래스는 twMerge로 합치세요.

listPlacement 분기에서 조건부 클래스 문자열을 직접 선택하고 있습니다. 공통 클래스와 배치별 클래스를 twMerge로 합치세요. 이후 배치별 클래스가 추가되어도 충돌을 일관되게 처리할 수 있습니다.

[recommend_refactor]

As per coding guidelines: Use twMerge for conditional classes.

🤖 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/select/SearchSelect.tsx` around lines 121 - 127, Update
the list container’s className in SearchSelect to use twMerge, passing the
shared classes together with the listPlacement-specific classes instead of
selecting complete class strings directly. Preserve the existing flow and
absolute placement styles while allowing future Tailwind class conflicts to be
resolved consistently.

Source: Coding guidelines

src/components/workspace/TransferOwnerModal.tsx (1)

8-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

공용 Modal import에 @/ 별칭을 사용하세요.

상대 경로 import는 이동과 리팩터링 시 깨지기 쉽습니다. @/components/common/modal/Modal로 변경하세요.

[recommend_refactor]

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/components/workspace/TransferOwnerModal.tsx` at line 8, Update the Modal
import in TransferOwnerModal to use the project’s `@/` alias, changing the
relative reference to `@/components/common/modal/Modal` while leaving the imported
symbol and surrounding code unchanged.

Source: Coding guidelines

src/types/workspace/workspace.ts (1)

61-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

새 타입 이름을 저장소 규칙에 맞추세요.

  • src/types/workspace/workspace.ts#L61-L65: TChangeOwnerRequest, TChangeOwnerResponseIChangeOwnerRequest, IChangeOwnerResponse로 변경하세요.
  • src/components/workspace/TransferOwnerModal.tsx#L12-L19: TTransferOwnerModalPropsITransferOwnerModalProps로 변경하세요.

As per coding guidelines: API/request-response use I* and component props use I*Props.

🤖 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/workspace/workspace.ts` around lines 61 - 65, Rename the API types
TChangeOwnerRequest and TChangeOwnerResponse to IChangeOwnerRequest and
IChangeOwnerResponse in src/types/workspace/workspace.ts:61-65, updating all
references. Rename the component props type TTransferOwnerModalProps to
ITransferOwnerModalProps in
src/components/workspace/TransferOwnerModal.tsx:12-19 and update its usages.

Source: Coding guidelines

src/components/ads/CampaignPlatformSection.tsx (1)

19-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

SVG 색상 토큰을 적용해 주세요.

Line 20과 Line 22의 GoogleLogo, NaverLogo에는 text-* 색상 클래스가 없습니다. 두 SVG는 fill 또는 strokecurrentColor를 사용하고, 컴포넌트에는 text-text-title 같은 @theme 색상 토큰을 적용하세요.

수정 예시
-  google: <GoogleLogo className="h-10 w-10 shrink-0" />,
+  google: <GoogleLogo className="h-10 w-10 shrink-0 text-text-title" />,
...
-  naver: <NaverLogo className="h-10 w-10 shrink-0" />,
+  naver: <NaverLogo className="h-10 w-10 shrink-0 text-text-title" />,

As per coding guidelines, SVG icons must use fill/stroke="currentColor" with text-* color classes.

🤖 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/ads/CampaignPlatformSection.tsx` around lines 19 - 23, Update
the PLATFORM_LOGO entries for GoogleLogo and NaverLogo to include the `@theme`
text color token class text-text-title, matching MetaLogo. Preserve their
existing sizing and ensure the SVGs receive color through currentColor-based
fill or stroke behavior.

Source: Coding guidelines

src/components/ads/AdDetailContent.tsx (1)

9-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

상대 경로 import를 @/ alias로 바꿔 주세요.

코딩 가이드라인은 모든 import에 @/ alias를 사용하도록 정합니다. 이 파일은 ../common/... 상대 경로를 사용합니다. 같은 파일의 다른 import는 이미 alias를 사용하고 있어 방식이 섞여 있습니다.

♻️ 제안 수정
-import Badge from "../common/badge/Badge";
-import Button from "../common/button/Button";
-import Modal from "../common/modal/Modal";
-import ModalContent from "../common/modal/ModalContent";
+import Badge from "`@/components/common/badge/Badge`";
+import Button from "`@/components/common/button/Button`";
+import Modal from "`@/components/common/modal/Modal`";
+import ModalContent from "`@/components/common/modal/ModalContent`";

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/components/ads/AdDetailContent.tsx` around lines 9 - 12, Update the
imports in AdDetailContent.tsx to replace each ../common/... relative path with
the corresponding `@/` alias, keeping the imported Badge, Button, Modal, and
ModalContent symbols unchanged and consistent with the file’s existing import
style.

Source: Coding guidelines

src/hooks/ads/useAdList.ts (1)

20-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

쿼리 키에 non-null 단언 대신 nullable 시그니처를 쓰는 편이 안전합니다.

isValid가 false이면 orgIdprojectIdnull입니다. Line 21은 !로 단언하므로 실제 키는 ["adList", null, null]이 됩니다. enabled: false 덕분에 요청은 발생하지 않습니다. 그러나 키 타입 계약(ads(orgId: number, projectId: number))이 깨집니다. 이후 다른 곳에서 이 키를 무효화할 때 혼동이 생길 수 있습니다.

QUERY_KEYS.campaign.adsnumber | null 허용으로 바꾸거나, 다른 키 생성자들(campaign.list(orgId: number | null))과 동일한 규칙을 따르도록 맞춰 주세요.

As per path instructions, "타입 안정성: 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/hooks/ads/useAdList.ts` around lines 20 - 24, Update the
QUERY_KEYS.campaign.ads signature to accept nullable orgId and projectId,
matching the nullable-key convention used by campaign.list. Remove the non-null
assertions from the useCoreQuery call in useAdList while preserving the existing
isValid-enabled behavior and getAdList invocation.

Source: Path instructions

src/utils/ads/settleBulkRequests.ts (1)

36-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

도달 불가능한 분기를 정리하면 좋겠습니다.

Error 인스턴스는 항상 "message" in result.firstError 조건을 만족합니다. 따라서 Line 42에서 먼저 throw됩니다. Line 45-47의 instanceof Error 삼항은 절대 참이 되지 않습니다.

♻️ 제안 diff
-    throw new Error(
-      result.firstError instanceof Error
-        ? result.firstError.message
-        : "요청에 실패했습니다.",
-    );
+    throw new Error("요청에 실패했습니다.");
🤖 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/ads/settleBulkRequests.ts` around lines 36 - 49, 정리 함수의
result.firstError 처리에서 중복된 Error 분기를 제거하세요. `result.successCount === 0` 블록의 객체 및
message 검사 경로를 유지하고, 이후 Error 인스턴스를 다시 판별하는 도달 불가능한 삼항 조건은 제거해 기존의 일반 실패 메시지 처리만
남기세요.
src/hooks/ads/useUpdateAdStatus.ts (1)

33-39: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

일괄 상태 변경 훅 두 곳이 동시 요청 수를 제한하지 않습니다. 두 훅 모두 대상 id 개수만큼 PATCH 요청을 한 번에 발행합니다. 근본 원인은 settleBulkRequests가 모든 promise를 즉시 시작한다는 점입니다. 대상이 많으면 브라우저 커넥션 한도와 서버 부하가 동시에 문제가 됩니다.

  • src/hooks/ads/useUpdateAdStatus.ts#L33-L39: vars.adContentIds를 배치 단위로 나누어 순차 처리하도록 변경해 주세요.
  • src/hooks/ads/useUpdateCampaignStatus.ts#L39-L43: projectIds에 동일한 배치 처리를 적용해 주세요.

settleBulkRequests에 동시성 상한 옵션을 추가하면 두 곳을 한 번에 해결할 수 있습니다. 서버에 일괄 상태 변경 엔드포인트가 있는지도 확인해 주세요.

🤖 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/ads/useUpdateAdStatus.ts` around lines 33 - 39, Limit concurrent
PATCH requests in settleBulkRequests by adding a concurrency cap and processing
inputs in sequential batches, rather than starting every promise immediately.
Apply this shared behavior to src/hooks/ads/useUpdateAdStatus.ts lines 33-39 for
vars.adContentIds and src/hooks/ads/useUpdateCampaignStatus.ts lines 39-43 for
projectIds; verify whether an existing bulk status endpoint can be used instead.
src/components/ads/AdRow.tsx (1)

13-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

그리드 클래스 헬퍼를 별도 모듈로 분리하는 편이 좋겠습니다.

현재 AdListTable.tsxskeleton/AdsSkeleton.tsx가 레이아웃 클래스를 얻으려고 행 컴포넌트인 AdRow를 import합니다. 컴포넌트와 레이아웃 상수의 책임이 섞입니다. adListTableGrid.ts 같은 모듈로 옮기면 의존 방향이 단순해집니다.

부수적으로 Line 135는 min-w-11을, AdListTable.tsx 헤더는 min-w-[2.75rem]을 사용합니다. 값은 같습니다. 표기를 하나로 통일해 주세요.

🤖 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/ads/AdRow.tsx` around lines 13 - 49, Move the grid constants,
padding values, and helper functions currently defined in AdRow.tsx into a
dedicated adListTableGrid module, then update AdRow, AdListTable, and
skeleton/AdsSkeleton imports to use that module instead of importing the row
component for layout classes. Preserve the existing platform-column behavior and
exported class names. Also standardize the equivalent 2.75rem minimum-width
classes between AdRow and the AdListTable header, using one consistent notation.
🤖 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/api/workspace/org.ts`:
- Around line 182-198: Validate cursor advancement in getAllWorkspaceMembers
before continuing the loop: when page.hasNext is true, require page.nextCursor
to be non-null and different from the cursor used for the current request. Throw
an error when this invariant is violated; otherwise update cursor and continue
collecting members normally.

In `@src/components/ads/AdDetailContent.tsx`:
- Around line 23-29: Update the orgIdNum and projectIdNum conversion before
useCreateTrackingUrl so invalid path parameters become null instead of NaN,
allowing only finite numeric values through. Preserve valid numeric IDs and
ensure both the API request and QUERY_KEYS.campaign.ads cache invalidation
receive null for non-finite conversions.

In `@src/components/ads/EditPlatformBudgetModal.tsx`:
- Around line 115-118: Update the activeBudget-driven form initialization in
EditPlatformBudgetModal so platformBudgets refreshes do not reset user-entered
values merely because budgetEditTarget receives a new object reference. Make the
useEffect dependency stable by deriving it from the budget identity fields (such
as providerType, adCampaignId, naverCampaignId, and naverConnectionId), or
preserve the existing IPlatformProjectBudget reference when updating
budgetEditTarget after value changes.

In `@src/components/ads/PlatformBudgetItem.tsx`:
- Around line 29-31: Update the PlatformBudgetItem display around
remainingAmount so the label explicitly distinguishes the over-budget state from
the remaining-budget state: show an “over budget”/“excess amount” message when
isOverBudget is true, and retain the existing remaining-amount wording
otherwise. Ensure the text changes alongside the existing conditional amount and
styling.

In `@src/components/landing/GuideTimeline.tsx`:
- Around line 78-80: Update TimelineBar so its interactive semantics are
conditional on onBarClick: when no handler is provided, omit role="button",
tabIndex, pointer cursor styling, and keyboard event handling; preserve the
existing clickable and keyboard behavior when onBarClick is present. Ensure the
LANDING_TIMELINE_BARS usage remains non-interactive.

In `@src/constants/dashboard/overviewMetricsRange.ts`:
- Around line 4-11: overviewMetricsRange의 고정 endDate 값을 제거하고 최신 동기화 완료일을 API 응답
또는 런타임 계산으로 사용하도록 변경하세요. AI_ANALYSIS_LOOKBACK_DAYS의 14일 제한과 구간 시작일 이전으로 확장하지 않는
동작은 유지하고, 테스트 전용 고정 범위가 production 경로에 사용되지 않도록 분리하세요.

In `@src/pages/ads/list/CampaignDetail.tsx`:
- Around line 204-224: Remove the AD_PLATFORM_ORDER-based sort from the
platformSections useMemo. Return fromAds directly after adding budget-only
sections so the order from groupAdsByPlatform(data.providers) is preserved, with
new sections remaining in platformBudgets iteration order.

In `@src/pages/setting/Setting.tsx`:
- Around line 424-428: Update the change-detection logic that defines
hasOrgToggleChanges to require isAdmin, so non-admin master-notification changes
do not remain part of hasChanges. Keep shouldSaveOrg and the existing save
behavior unchanged, ensuring only administrators can detect and save
Slack/Discord organization-toggle changes.

In `@src/types/dashboard/aiAnalysis.ts`:
- Line 1: Update the import in aiAnalysis.ts to use the project's `@/` alias
instead of the relative "./provider" path, while preserving the existing
TAiAnalysisProvider and TProviderType imports.

In `@src/utils/ads/budgetEdit.ts`:
- Around line 92-111: Update the budget selection logic around declaredType so a
server-declared activeBudgetType is never replaced with the other budget type
when its corresponding data is null. If the active type’s budget data is
unavailable, block editing by returning the existing invalid/unavailable result
or otherwise preventing update payload construction; preserve DAILY and LIFETIME
data handling when present.

In `@src/utils/ads/projectBudget.ts`:
- Around line 62-76: Update the mock budget branches in the project budget
builder, including the NAVER and supportsDailyBudget paths, to set canEditBudget
to false. Ensure placeholder data returned when platformBudgets is unavailable
cannot enable budget-edit actions or trigger real update requests.

---

Outside diff comments:
In `@src/components/ads/AdListTable.tsx`:
- Around line 87-95: Update AdListTable’s props to accept a section-specific
selectAllLabel and use it for the select-all checkbox aria-label instead of the
fixed text. In CampaignDetail, pass selectAllLabel={`${platform} 광고 전체 선택`} for
each platform-rendered AdListTable.

In `@src/components/landing/GuideTimeline.tsx`:
- Around line 22-34: Move the custom-scrollbar rules from the inline style block
in GuideTimeline into the shared utilities stylesheet, defining the reusable
utility class there. Remove the component-level style block and update
GuideTimeline to rely only on the shared custom-scrollbar class.

In `@src/pages/ads/list/CampaignDetail.tsx`:
- Around line 59-68: Normalize orgIdNum and projectIdNum at their conversion
points so non-numeric path parameters become null rather than NaN. Update the
declarations near useCampaignDetail, useAdList, and useUpdateAdStatus to use
finite-number validation, preserving valid numeric IDs and ensuring downstream
orgId == null and orgIdNum != null guards reject invalid values.

---

Nitpick comments:
In `@src/components/ads/AdDetailContent.tsx`:
- Around line 9-12: Update the imports in AdDetailContent.tsx to replace each
../common/... relative path with the corresponding `@/` alias, keeping the
imported Badge, Button, Modal, and ModalContent symbols unchanged and consistent
with the file’s existing import style.

In `@src/components/ads/AdRow.tsx`:
- Around line 13-49: Move the grid constants, padding values, and helper
functions currently defined in AdRow.tsx into a dedicated adListTableGrid
module, then update AdRow, AdListTable, and skeleton/AdsSkeleton imports to use
that module instead of importing the row component for layout classes. Preserve
the existing platform-column behavior and exported class names. Also standardize
the equivalent 2.75rem minimum-width classes between AdRow and the AdListTable
header, using one consistent notation.

In `@src/components/ads/CampaignPlatformSection.tsx`:
- Around line 19-23: Update the PLATFORM_LOGO entries for GoogleLogo and
NaverLogo to include the `@theme` text color token class text-text-title, matching
MetaLogo. Preserve their existing sizing and ensure the SVGs receive color
through currentColor-based fill or stroke behavior.

In `@src/components/common/select/SearchSelect.tsx`:
- Around line 121-127: Update the list container’s className in SearchSelect to
use twMerge, passing the shared classes together with the listPlacement-specific
classes instead of selecting complete class strings directly. Preserve the
existing flow and absolute placement styles while allowing future Tailwind class
conflicts to be resolved consistently.

In `@src/components/workspace/TransferOwnerModal.tsx`:
- Line 8: Update the Modal import in TransferOwnerModal to use the project’s `@/`
alias, changing the relative reference to `@/components/common/modal/Modal` while
leaving the imported symbol and surrounding code unchanged.

In `@src/hooks/ads/useAdList.ts`:
- Around line 20-24: Update the QUERY_KEYS.campaign.ads signature to accept
nullable orgId and projectId, matching the nullable-key convention used by
campaign.list. Remove the non-null assertions from the useCoreQuery call in
useAdList while preserving the existing isValid-enabled behavior and getAdList
invocation.

In `@src/hooks/ads/useUpdateAdStatus.ts`:
- Around line 33-39: Limit concurrent PATCH requests in settleBulkRequests by
adding a concurrency cap and processing inputs in sequential batches, rather
than starting every promise immediately. Apply this shared behavior to
src/hooks/ads/useUpdateAdStatus.ts lines 33-39 for vars.adContentIds and
src/hooks/ads/useUpdateCampaignStatus.ts lines 39-43 for projectIds; verify
whether an existing bulk status endpoint can be used instead.

In `@src/types/workspace/workspace.ts`:
- Around line 61-65: Rename the API types TChangeOwnerRequest and
TChangeOwnerResponse to IChangeOwnerRequest and IChangeOwnerResponse in
src/types/workspace/workspace.ts:61-65, updating all references. Rename the
component props type TTransferOwnerModalProps to ITransferOwnerModalProps in
src/components/workspace/TransferOwnerModal.tsx:12-19 and update its usages.

In `@src/utils/ads/settleBulkRequests.ts`:
- Around line 36-49: 정리 함수의 result.firstError 처리에서 중복된 Error 분기를 제거하세요.
`result.successCount === 0` 블록의 객체 및 message 검사 경로를 유지하고, 이후 Error 인스턴스를 다시 판별하는
도달 불가능한 삼항 조건은 제거해 기존의 일반 실패 메시지 처리만 남기세요.
🪄 Autofix

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: 9f71cbd4-33ef-4890-af0a-5c583ee3ad05

📥 Commits

Reviewing files that changed from the base of the PR and between c60e477 and 1e37d1e.

⛔ Files ignored due to path filters (2)
  • src/assets/icon/common/sync.svg is excluded by !**/*.svg and included by src/**
  • tests/ai-analysis.spec.ts is excluded by none and included by none
📒 Files selected for processing (73)
  • src/api/ads/budget.ts
  • src/api/dashboard/aiAnalysis.ts
  • src/api/integration/google.ts
  • src/api/integration/meta.ts
  • src/api/integration/naver.ts
  • src/api/workspace/org.ts
  • src/components/ads/AdDetailContent.tsx
  • src/components/ads/AdListTable.tsx
  • src/components/ads/AdRow.tsx
  • src/components/ads/CampaignPlatformSection.tsx
  • src/components/ads/EditPlatformBudgetModal.tsx
  • src/components/ads/PlatformBudgetItem.tsx
  • src/components/ads/skeleton/AdsSkeleton.tsx
  • src/components/common/modal/Modal.tsx
  • src/components/common/select/SearchSelect.tsx
  • src/components/dashboard/ai-report/components/AiSummaryCard.tsx
  • src/components/dashboard/ai-report/components/DashboardAiSummarySection.tsx
  • src/components/dashboard/charts/BudgetGaugeChart.tsx
  • src/components/integration/NaverSyncModal.tsx
  • src/components/integration/PlatformIntegrationCard.tsx
  • src/components/landing/GuideTimeline.tsx
  • src/components/setting/NotificationSection.tsx
  • src/components/setting/PasswordSection.tsx
  • src/components/setting/ProfileSection.tsx
  • src/components/setting/ProfileSectionSkeleton.tsx
  • src/components/setting/WithdrawConfirmModal.tsx
  • src/components/sidebar/LogoutConfirmModal.tsx
  • src/components/timeline/TimelineBar.tsx
  • src/components/timeline/TimelineCreateModal.tsx
  • src/components/timeline/TimelineGrid.tsx
  • src/components/timeline/TimelinePerformancePanel.tsx
  • src/components/timeline/TimelinePeriodSelector.tsx
  • src/components/timeline/TimelineStatusLegend.tsx
  • src/components/timeline/skeleton/TimelineSkeleton.tsx
  • src/components/workspace/InviteMemberModal.tsx
  • src/components/workspace/MemberItem.tsx
  • src/components/workspace/MemberList.tsx
  • src/components/workspace/MemberSearchSelect.tsx
  • src/components/workspace/PermissionTable.tsx
  • src/components/workspace/TransferOwnerModal.tsx
  • src/constants/dashboard/overviewMetricsRange.ts
  • src/constants/landing/timeline.ts
  • src/hooks/ads/useAdList.ts
  • src/hooks/ads/useCreateTrackingUrl.ts
  • src/hooks/ads/useUpdateAdStatus.ts
  • src/hooks/ads/useUpdateCampaignStatus.ts
  • src/hooks/ads/useUpdatePlatformBudget.ts
  • src/hooks/auth/useDeleteMyAccount.ts
  • src/hooks/dashboard/useAiAnalysisReport.ts
  • src/lib/queryKeys.ts
  • src/pages/ads/list/AdsListPage.tsx
  • src/pages/ads/list/CampaignDetail.tsx
  • src/pages/dashboard/timeline/Timeline.tsx
  • src/pages/integration/PlatformIntegrationsPage.tsx
  • src/pages/setting/Setting.tsx
  • src/pages/workspace/InviteAcceptPage.tsx
  • src/pages/workspace/MemberManagement.tsx
  • src/pages/workspace/Workspace.tsx
  • src/pages/workspace/WorkspaceSetting.tsx
  • src/types/ads/budget.ts
  • src/types/ads/campaign.ts
  • src/types/dashboard/aiAnalysis.ts
  • src/types/dashboard/budget.ts
  • src/types/integration/platformSync.ts
  • src/types/workspace/workspace.ts
  • src/utils/ads/adPlatform.ts
  • src/utils/ads/budgetEdit.ts
  • src/utils/ads/formatBudgetInput.ts
  • src/utils/ads/projectBudget.ts
  • src/utils/ads/settleBulkRequests.ts
  • src/utils/dashboard/budget.ts
  • src/utils/integration/naverSyncSchema.ts
  • src/utils/integration/platformSync.ts

Comment thread src/api/workspace/org.ts
Comment thread src/components/ads/AdDetailContent.tsx
Comment thread src/components/ads/EditPlatformBudgetModal.tsx
Comment thread src/components/ads/PlatformBudgetItem.tsx
Comment thread src/components/landing/GuideTimeline.tsx
Comment thread src/pages/ads/list/CampaignDetail.tsx
Comment thread src/pages/setting/Setting.tsx
Comment thread src/types/dashboard/aiAnalysis.ts
Comment thread src/utils/ads/budgetEdit.ts
Comment thread src/utils/ads/projectBudget.ts
@jjjsun
jjjsun merged commit f5c1700 into main Aug 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🌏 Deploy 배포 관련

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants