Skip to content

Feature(client): 대시보드 배너 구현 및 개발 워크플로우 개선#84

Merged
karnelll merged 19 commits intodevelopfrom
feature/#80/banner-component
Jul 16, 2025
Merged

Feature(client): 대시보드 배너 구현 및 개발 워크플로우 개선#84
karnelll merged 19 commits intodevelopfrom
feature/#80/banner-component

Conversation

@karnelll
Copy link
Contributor

@karnelll karnelll commented Jul 15, 2025

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)


✅ 체크 리스트

  • PR 제목의 형식을 잘 작성했나요? e.g. Feat(client): 배너 컴포넌트 및 리소스 추가
  • 빌드가 성공했나요? (pnpm build)

📄 Tasks

  • 대시보드 배너 컴포넌트 구현 (7단계)
  • 배너 관련 SVG 및 리소스 추가
  • 배너 이미지/도토리/치삐 단계별 그래픽 적용
  • 관련 상수 및 유틸 분리
  • 배너 컴포넌트 리팩토링 및 코드 품질 개선
  • 중복된 이미지 파일명 로직 제거 및 BANNER_IMAGES 상수 활용
  • 배열 인덱스 범위 검사 추가로 안전성 향상
  • 매직 넘버 제거 및 위치 상수화로 유지보수성 개선
  • Timer 컴포넌트 useEffect 의존성 배열 수정
  • 메모 텍스트 3줄 초과 시 4번째 줄부터 완전히 잘리도록 CSS 강화 (line-clamp 직접 적용 및 overflow-hidden, max-height 등 추가)
  • 데일리 리마인드 카드에 썸네일 없는 테스트 케이스(mock data) 추가
  • 린트/포맷 자동수정 및 코드 스타일 일관성 유지
  • 썸네일이 없을 때 배경 박스가 보이지 않도록 UI 개선

⭐ PR Point (To Reviewer)

  • 배너 레이아웃 및 SVG 배치가 의도대로 잘 보이는지 확인 부탁드립니다.
  • 도토리 개수에 따른 배너 상태 변화가 올바르게 작동하는지 확인해주세요.
  • 배너 배경 이미지가 도토리 7개 달성 시에만 표시되는지 확인해주세요.
  • 코드 리팩토링으로 인한 기능 변경이 없는지 확인해주세요.

🔧 주요 변경사항

새로운 기능

  • 배너 컴포넌트 모듈화 (BannerHeadline 분리)
  • 도토리 7개 달성 시 특별 배경 이미지 표시

코드 품질 개선

  • Asset import 경로 통일 (@assets 별칭 사용)
  • 중복 코드 제거 및 상수 활용
  • 안전한 배열 인덱스 접근
  • 매직 넘버 제거 및 위치 상수화

버그 수정

  • FeedbackModal backdrop 클릭 처리
  • Timer 컴포넌트 useEffect 의존성 배열 수정
  • 치삐 이미지 로딩 문제 해결

🚀 개발 워크플로우 개선 안내

새로 추가된 스크립트

# 코드 포맷팅
pnpm format

# 린트 자동 수정
pnpm lint:fix

# 포맷팅 + 린트 자동 수정
pnpm fix-all

# 포맷팅 + 린트 자동 수정 + 빌드 (추천)
pnpm check-all

사용법

커밋 전 필수 실행:

pnpm check-all

이 명령어 하나로 포맷팅, 린트 자동 수정, 빌드가 순차적으로 실행됩니다.

개선된 점

  • 자동화: 수동으로 실행하던 작업들이 한 번에 처리됩니다.
  • 일관성 유지: 모든 개발자가 동일한 코드 스타일을 적용받습니다.
  • 품질 보장: 커밋 전에 코드 품질을 자동으로 검사합니다.
  • 시간 절약: 리뷰 시간 단축 및 실수 방지에 도움이 됩니다.

적용 대상

  • client, extension, landing, design-system 전 앱 공통 적용
  • Prettier + ESLint + Turbo 기준으로 안정적으로 실행됩니다

⚠️ 주의 사항

이번 작업 과정에서 develop 브랜치에 실수로 직접 푸시된 부분이 있었습니다. 해당 내용은 슬랙에 먼저 공유드렸고, 동일한 내용을 이번 PR에도 기록해두었습니다. 혼란을 드려 죄송합니다.

📷 Screenshot

스크린샷 2025-07-16 오전 3 13 46 스크린샷 2025-07-16 오전 3 13 57 스크린샷 2025-07-16 오전 4 22 52 스크린샷 2025-07-16 오전 3 14 24 스크린샷 2025-07-16 오전 3 14 38 스크린샷 2025-07-16 오전 3 14 50 스크린샷 2025-07-16 오전 3 15 01 스크린샷 2025-07-16 오전 3 15 14 스크린샷 2025-07-16 오전 3 15 44

Summary by CodeRabbit

  • New Features

    • 대시보드에 새로운 배너 컴포넌트와 BannerSection이 추가되어 도토리 개수에 따른 진행 상황을 시각적으로 확인할 수 있습니다.
    • 배너에 도토리 개수, 캐릭터 이미지, 레벨 정보, 타이머 등이 표시되며, 관련 하위 컴포넌트와 이미지 리소스도 포함됩니다.
    • 도토리 개수를 관리하고 갱신할 수 있는 훅과 API가 도입되었습니다.
  • 개선 및 변경

    • 대시보드 레이아웃이 개선되어 헤더가 고정되고 배너 및 섹션별 여백과 배치가 조정되었습니다.
    • 타이머 컴포넌트가 간소화되어 남은 시간만 표시하도록 변경되었습니다.
    • 여러 컴포넌트에서 에셋 경로가 alias(@assets) 기반으로 통일되었습니다.
    • 카드, 썸네일, 모달, 툴팁 등 UI 컴포넌트의 스타일과 동작이 일부 개선되었습니다.
    • 모달 닫기 동작이 외부 클릭으로도 가능하도록 변경되었습니다.
    • 배너 레이아웃 및 스타일링이 세분화되고, 모달 렌더링이 포탈 방식으로 변경되었습니다.
  • 버그 수정 및 리팩터

    • 텍스트 줄임 처리 방식이 개선되고, 조건부 렌더링 및 상태 관리가 최적화되었습니다.
    • 불필요한 주석과 미사용 코드가 정리되고, import 경로가 상대경로로 변경되어 코드 가독성이 향상되었습니다.
    • 일부 컴포넌트의 내부 로직과 상태 관리가 리팩터링되었습니다.
  • 기타

    • 타입 및 상수 정의가 추가되어 코드 일관성과 안정성이 향상되었습니다.
    • 목데이터가 보강되어 다양한 시나리오 테스트가 가능해졌습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jul 15, 2025

Walkthrough

대시보드의 배너 영역 및 관련 UI 컴포넌트가 대폭 추가 및 리팩토링되었습니다. 팝업 모달 제어 로직이 대시보드에서 제거되고, 배너 섹션과 관련된 상태 및 훅이 신설되었습니다. 각종 에셋 import 경로가 alias 방식으로 통일되었고, 다수의 UI 컴포넌트 및 상수, 타입이 신설되었습니다.

Changes

파일/그룹 변경 요약
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx, BannerAcornBubble.tsx, BannerChippyImage.tsx, BannerHeadline.tsx, BannerLevelInfo.tsx, BannerTimer.tsx, index.ts 배너 UI 및 하위 컴포넌트 신설, 배너 타입 및 export 구성
apps/client/src/pages/dashboard/components/sections/BannerSection.tsx, index.ts 배너 섹션 컴포넌트 추가, 섹션 export에 배너 섹션 포함
apps/client/src/pages/dashboard/constants/bannerImages.ts, index.ts, levelData.ts 배너 이미지 상수, 타입 추가 및 export, 레벨 관련 상수 추가
apps/client/src/pages/dashboard/hooks/useDashboard.ts 도토리 개수(acornCount) 상태, fetch/업데이트 함수 추가
apps/client/src/pages/dashboard/hooks/useTimer.ts 타이머 훅 신설
apps/client/src/pages/dashboard/dashboard.tsx 팝업 모달 로직 제거, 배너 섹션 도입, 레이아웃 및 상태 변경
apps/client/src/pages/dashboard/components/ui/cards/BookmarkCard.tsx, DailyReminderCard.tsx, Thumbnail.tsx, indicators/LevelInfoItem.tsx, indicators/Timer.tsx, overlays/LevelInfoModal.tsx, overlays/Tooltip.tsx, feedback/Modal.tsx, buttons/AllViewButton.tsx, common/layout/SectionContent.tsx 에셋 import 경로 alias로 통일, 일부 UI/로직 리팩토링
apps/client/src/pages/dashboard/components/ui/index.ts UI 컴포넌트 일괄 export index 파일 추가
apps/client/src/pages/dashboard/components/index.ts 주석 제거
apps/client/src/pages/dashboard/components/sections/BookmarkSection.tsx 마진 계산 함수 분리
apps/client/src/pages/dashboard/mockData.ts 목데이터 추가 및 일부 값 변경
apps/client/src/shared/apis/timerApi.ts, shared/types/api.ts 도토리 개수 API 및 타입 추가, 기존 API 리팩토링
apps/client/src/shared/components/ui/display/EmptyStatePresets.ts 에셋 경로 alias로 변경
apps/client/src/shared/utils/styleUtils.ts 카드 메모 영역 스타일 개선(3줄 말줄임 등)
apps/client/src/shared/utils/timerUtils.ts JSDoc 주석 제거
apps/client/src/main.tsx 루트 엘리먼트 존재 체크 추가
apps/client/tsconfig.json @assets alias 추가 및 주석 제거
apps/client/src/vite-env.d.ts vite/client 타입 참조 제거
apps/client/src/shared/components/ui/layout/Header.tsx 헤더 로고 이미지 변경 및 스타일 조정

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Dashboard
    participant useDashboard
    participant BannerSection
    participant Banner
    participant useTimer

    User->>Dashboard: 페이지 진입
    Dashboard->>useDashboard: 훅 호출 (acornCount 등 fetch)
    useDashboard->>Dashboard: acornCount 등 상태 반환
    Dashboard->>BannerSection: acornCount 전달
    BannerSection->>Banner: acornCount 전달
    Banner->>useTimer: 타이머 상태 요청
    useTimer-->>Banner: 남은 시간 반환
    Banner-->>BannerSection: 배너 UI 렌더링
    BannerSection-->>Dashboard: 섹션 렌더링 완료
Loading

Assessment against linked issues

Objective Addressed Explanation
대시보드용 large 컴포넌트 제작, 배너/팝업/북마크/리마인더 등 섹션 및 UI 제어 (#80)
북마크/리마인더 카드 더보기 버튼 → 팝업 제어 연결 (#80) 팝업 제어 로직이 오히려 제거되어 더보기 버튼과 팝업 연결이 구현되지 않음
팝업 바깥영역 클릭 시 닫기 제어 (#80) 팝업 관련 코드가 삭제되어 바깥영역 클릭 시 닫기 기능도 없음
헤더 아이콘 변경 (#80)
shadcn-ui 초기 세팅 (#25) shadcn-ui 관련 파일 및 설정은 본 PR 변경 내역에 없음 (별도 PR로 추정)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
배너 섹션 및 UI, 도토리 개수 상태/훅/상수/타입 신설 (예: BannerSection.tsx, Banner.tsx, bannerImages.ts, useDashboard.ts 등) 본 이슈(#80)는 팝업 컴포넌트 및 제어에 대한 요구만 명시되어 있으며, 배너 및 도토리 개수 관련 기능은 별도 이슈로 관리되어야 할 가능성이 높음
타이머 훅 신설 및 기존 Timer 컴포넌트 리팩토링 (useTimer.ts, Timer.tsx) 타이머 로직의 구조적 변경은 팝업/모달/더보기 버튼 제어와 직접적 관련이 없음
에셋 import 경로 alias 일괄 변경 기능상 영향은 없으나, 이슈 요구사항과 직접적 연관 없음 (다만 유지보수 목적의 리팩토링으로 볼 수 있음)

Possibly related PRs

  • Pinback-Team/pinback-client#80: 대시보드 팝업 모달 제어 및 더보기 버튼 연결 등, 본 PR과 반대 방향(팝업 제어 추가 vs. 본 PR은 제거)으로 직접적으로 연관된 PR입니다.

Suggested reviewers

  • jjangminii
  • jllee000

Poem

🥕
대시보드에 도토리 바람,
배너 속 치피가 활짝 자람.
팝업은 사라지고,
새 UI가 반짝이고!
토끼도 놀란 변화의 힘,
오늘도 코드밭에 봄이 왔지,
깡총깡총, 리뷰하러 갑니다!
🐇


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ec4af1 and 72f51eb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • apps/client/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/client/tsconfig.json

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (10)
apps/client/src/pages/dashboard/components/common/layout/SectionContent.tsx (1)

4-4: SVG alt 텍스트 의미 보강 제안

alt="정보" 보다 용도에 맞는 설명(예: "타이머 정보 툴팁" 등)을 적으면 접근성이 개선됩니다.
로직 변경은 아니므로 선택 사항입니다.

apps/client/src/pages/dashboard/constants/levelData.ts (1)

1-5: 레벨 이미지 경로 별칭 전환 OK

일관성 있게 @assets로 교체된 점 확인했습니다. CI/CD 파이프라인에서 이미지 트리쉐이킹(예: Vite + Rollup) 설정에 영향이 없는지 한 번 더 검증해 주세요.

apps/client/src/pages/dashboard/components/sections/index.ts (1)

1-5: export 구조 개선을 제안합니다.

상대 경로로의 변경과 새로운 BannerSection 추가는 좋은 변경사항입니다. 다만 export 순서를 의미적으로 그룹화하면 더 나은 구조가 될 것 같습니다.

-export { default as BannerSection } from './BannerSection';
-export { default as BookmarkSection } from './BookmarkSection';
-export { default as DailyReminderSection } from './DailyReminderSection';
-export { default as LevelInfoSection } from './LevelInfoSection';
-export { default as TestSection } from './TestSection';
+// 메인 섹션들
+export { default as BannerSection } from './BannerSection';
+export { default as BookmarkSection } from './BookmarkSection';
+export { default as DailyReminderSection } from './DailyReminderSection';
+export { default as LevelInfoSection } from './LevelInfoSection';
+// 테스트 섹션
+export { default as TestSection } from './TestSection';
apps/client/src/pages/dashboard/components/ui/overlays/LevelInfoModal.tsx (1)

6-8: Props 인터페이스 네이밍을 개선해주세요.

retrieved learning에 따르면 더 구체적이고 의미있는 이름을 사용하는 것이 좋습니다.

-interface Props extends LevelInfoModalProps {
-  className?: string;
-}
+interface LevelInfoModalWithClassNameProps extends LevelInfoModalProps {
+  className?: string;
+}
apps/client/src/pages/dashboard/components/sections/BannerSection.tsx (1)

19-19: className 처리를 더 안전하게 개선해주세요.

className ?? '' 대신 기본값 매개변수를 사용하거나 더 명확한 처리를 권장합니다.

-const BannerSection = ({
-  acornCount,
-  children,
-  showImage = true,
-  className,
-}: BannerSectionProps) => {
+const BannerSection = ({
+  acornCount,
+  children,
+  showImage = true,
+  className = '',
+}: BannerSectionProps) => {
   return (
-    <section className={className ?? ''} aria-label="배너 섹션">
+    <section className={className} aria-label="배너 섹션">
apps/client/src/pages/dashboard/dashboard.tsx (1)

57-57: 고정 너비로 인한 반응형 문제 우려

w-[144rem] (1440px) 고정 너비를 사용하면 작은 화면에서 문제가 발생할 수 있습니다. 반응형 디자인을 고려해보세요.

-        <main className="mx-auto w-[144rem]">
+        <main className="mx-auto w-full max-w-[144rem]">
apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (2)

12-28: 시간 계산 로직 개선 제안

시간 계산 로직이 정확하지만 가독성을 위해 상수를 사용하는 것을 권장합니다.

+const MILLISECONDS_PER_SECOND = 1000;
+const SECONDS_PER_MINUTE = 60;
+const MINUTES_PER_HOUR = 60;
+const MILLISECONDS_PER_MINUTE = MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE;
+const MILLISECONDS_PER_HOUR = MILLISECONDS_PER_MINUTE * MINUTES_PER_HOUR;

     const calculateTimeLeft = () => {
       const now = new Date();
       const reminderTime = new Date();
       reminderTime.setHours(reminderHour, 0, 0, 0);
       
       if (now.getHours() >= reminderHour) {
         reminderTime.setDate(reminderTime.getDate() + 1);
       }
       
       const diff = reminderTime.getTime() - now.getTime();
-      const hours = Math.floor(diff / (1000 * 60 * 60));
-      const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
-      const seconds = Math.floor((diff % (1000 * 60)) / 1000);
+      const hours = Math.floor(diff / MILLISECONDS_PER_HOUR);
+      const minutes = Math.floor((diff % MILLISECONDS_PER_HOUR) / MILLISECONDS_PER_MINUTE);
+      const seconds = Math.floor((diff % MILLISECONDS_PER_MINUTE) / MILLISECONDS_PER_SECOND);

8-8: 사용되지 않는 onComplete 콜백 매개변수

onComplete 콜백이 매개변수로 받아지지만 실제로는 사용되지 않습니다.

-const Timer = ({ onComplete: _onComplete, reminderHour = 9 }: TimerProps) => {
+const Timer = ({ reminderHour = 9 }: TimerProps) => {

또는 실제로 타이머 완료 시 호출하도록 구현하세요.

apps/client/src/pages/dashboard/hooks/useDashboard.ts (1)

7-7: 주석 내용 수정 필요

주석에서 "테스트용 도토리 개수 설정"이라고 하면서 실제 코드에서는 7로 설정되어 있어 일관성이 없습니다.

-  // 테스트용 도토리 개수 설정 (0-7 사이의 값으로 변경해서 테스트 가능)
+  // 도토리 개수 상태 (0-7 사이의 값)
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1)

31-43: 이미지 선택 로직 개선 필요

getProgressBarImage 함수에서 타입 캐스팅을 사용하고 있습니다. 더 안전한 방법을 사용하세요.

const getProgressBarImage = (acornCount: number): string => {
  const progressBarImages = {
  };
-  return progressBarImages[acornCount as keyof typeof progressBarImages] || progressBar0;
+  const validAcornCount = Math.min(Math.max(acornCount, 0), 7);
+  return progressBarImages[validAcornCount as keyof typeof progressBarImages];
};
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8cbb1 and 1c0476d.

⛔ Files ignored due to path filters (21)
  • apps/client/src/assets/banner/acorn-count-bubble.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/0_쫄쫄 굶은 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/1_쫄쫄 굶은 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/2_배고픈 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/3_출출한 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/4_출출한 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/5_출출한 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/6_출출한 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/chippy-stages/7_행복한 치삐.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-0.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-1.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-2.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-3.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-4.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-5.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-6.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/progress/progress-bar-7.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/timer_bubble.svg is excluded by !**/*.svg
  • apps/client/src/assets/banner/배너_그래픽.svg is excluded by !**/*.svg
  • apps/client/src/assets/brand/logo.svg is excluded by !**/*.svg
  • apps/client/src/assets/icons/ui/gray500.svg is excluded by !**/*.svg
📒 Files selected for processing (25)
  • apps/client/src/pages/dashboard/components/common/layout/SectionContent.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/sections/BannerSection.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/sections/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/buttons/AllViewButton.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/BookmarkCard.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/Thumbnail.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/feedback/Modal.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/indicators/LevelInfoItem.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/overlays/LevelInfoModal.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/overlays/Tooltip.tsx (1 hunks)
  • apps/client/src/pages/dashboard/constants/bannerImages.ts (1 hunks)
  • apps/client/src/pages/dashboard/constants/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/constants/levelData.ts (1 hunks)
  • apps/client/src/pages/dashboard/dashboard.tsx (3 hunks)
  • apps/client/src/pages/dashboard/hooks/useDashboard.ts (2 hunks)
  • apps/client/src/shared/apis/timerApi.ts (2 hunks)
  • apps/client/src/shared/components/ui/display/EmptyStatePresets.ts (1 hunks)
  • apps/client/src/shared/components/ui/layout/Header.tsx (1 hunks)
  • apps/client/src/shared/types/api.ts (1 hunks)
  • apps/client/tsconfig.json (1 hunks)
🧰 Additional context used
🧠 Learnings (10)
apps/client/src/pages/dashboard/components/common/layout/SectionContent.tsx (3)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
Learnt from: jjangminii
PR: Pinback-Team/pinback-client#32
File: apps/extension/src/components/textfieldPopup/TextfieldPopup.tsx:12-12
Timestamp: 2025-07-08T12:34:59.692Z
Learning: In the Pinback-Team/pinback-client project, `text-gray900` is a custom design token, not a typo. It's intentionally defined without a hyphen as part of their design system, so it shouldn't be suggested to change it to `text-gray-900`.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/cards/BookmarkCard.tsx (2)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/index.ts (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#10
File: apps/landing/src/index.css:1-1
Timestamp: 2025-07-04T10:12:01.690Z
Learning: In TailwindCSS v4, the @import "tailwindcss"; syntax is the new recommended approach that replaces the three separate @tailwind directives (@tailwind base;, @tailwind components;, @tailwind utilities;). This single import pulls in all of Tailwind's preflight, components, and utilities in one step and is used with the @tailwindcss/vite plugin.
apps/client/src/shared/components/ui/display/EmptyStatePresets.ts (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
apps/client/src/pages/dashboard/components/ui/overlays/Tooltip.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx (2)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/shared/components/ui/layout/Header.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/overlays/LevelInfoModal.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
🧬 Code Graph Analysis (4)
apps/client/src/pages/dashboard/components/sections/BannerSection.tsx (1)
apps/client/src/pages/dashboard/constants/bannerImages.ts (1)
  • BannerStage (21-21)
apps/client/src/pages/dashboard/components/ui/overlays/LevelInfoModal.tsx (1)
apps/client/src/shared/types/components.ts (1)
  • LevelInfoModalProps (70-73)
apps/client/src/shared/apis/timerApi.ts (1)
apps/client/src/shared/types/api.ts (1)
  • AcornCountResponse (53-55)
apps/client/src/pages/dashboard/hooks/useDashboard.ts (1)
apps/client/src/pages/dashboard/constants/bannerImages.ts (1)
  • BannerStage (21-21)
🔇 Additional comments (25)
apps/client/tsconfig.json (1)

16-20: 절대 경로 별칭 추가는 좋지만 Vite/Jest 설정과 동기화 필요

"@assets": ["src/assets"] 별칭을 추가해 가독성이 좋아졌습니다.
다만 Vite(예: vite.config.tsresolve.alias)·Jest(ts-jest)·ESLint import-resolver에도 동일한 별칭을 함께 추가하지 않으면 IDE 자동완성과 테스트/빌드 단계에서 경로를 찾지 못할 수 있습니다. PR 범위에 해당 설정 파일이 없으니 한 번 더 확인 부탁드립니다.

apps/client/src/pages/dashboard/components/ui/buttons/AllViewButton.tsx (1)

1-1: 경로 별칭 문제없음

@assets 별칭 적용이 잘 반영되었습니다. 추가 조치 필요 없어 보입니다.

apps/client/src/pages/dashboard/components/ui/cards/Thumbnail.tsx (1)

1-1: 별칭 적용 확인

별칭 경로로 안전하게 교체되었으며 타입/동작에 영향 없습니다.

apps/client/src/pages/dashboard/components/ui/overlays/Tooltip.tsx (1)

2-2: 별칭 변경 확인

자산 경로만 변경되었고 컴포넌트 동작에는 영향이 없습니다.

apps/client/src/shared/types/api.ts (1)

53-55: AcornCountResponse 타입 추가 확인 완료

백엔드 명세와 일치한다면 특별한 문제는 없어 보입니다. 후속 작업으로 Swagger/오픈API 스펙에도 동일한 필드명이 정의돼 있는지 한번 더 교차 확인해 주세요.

apps/client/src/shared/components/ui/display/EmptyStatePresets.ts (1)

1-2: 에셋 경로 별칭 변경 시 TS/빌드 설정도 함께 확인 필요

@assets 별칭이 새로 도입됐으니 tsconfig.json, vite.config.ts(또는 webpack)·Jest 설정까지 모두 매핑돼 있는지 검증해 주세요. 누락되면 런타임/테스트 단계에서 모듈을 찾지 못할 수 있습니다.

apps/client/src/pages/dashboard/components/ui/indicators/LevelInfoItem.tsx (1)

2-2: 에셋 별칭 적용 확인

경로만 변경된 것으로 보이며 기능상 문제는 없습니다. 별칭 경로가 테스트 환경에서도 동일하게 resolve 되는지 확인만 해 주세요.

apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx (1)

2-5: 에셋 import 경로 별칭 사용 승인

절대 경로를 @assets 별칭으로 변경한 리팩토링이 적절합니다. 이는 유지보수성을 향상시키고 프로젝트 구조 변경에 더 유연하게 대응할 수 있게 해줍니다.

apps/client/src/pages/dashboard/components/ui/cards/BookmarkCard.tsx (1)

2-3: 에셋 import 경로 일관성 개선

다른 컴포넌트들과 동일하게 @assets 별칭을 사용하여 import 경로를 표준화한 것이 좋습니다. 코드베이스 전반의 일관성을 유지하는 데 도움이 됩니다.

apps/client/src/pages/dashboard/components/ui/index.ts (1)

1-6: UI 컴포넌트 중앙화된 export 구조 개선

대시보드 UI 컴포넌트들을 중앙화된 인덱스 파일로 관리하는 것이 좋은 아키텍처 접근법입니다. 이는 컴포넌트 import를 단순화하고 모듈 구조를 개선합니다.

apps/client/src/shared/apis/timerApi.ts (2)

7-7: AcornCountResponse 타입 import 추가

새로운 도토리 개수 API 기능을 위한 타입 import가 적절합니다.


53-56: getAcornCount API 함수 구현

기존 API 함수들과 일관된 패턴을 따르며, 적절한 타입 지정과 async/await 패턴을 사용했습니다. 새로운 도토리 개수 기능 구현에 필요한 API 함수가 올바르게 작성되었습니다.

apps/client/src/pages/dashboard/components/ui/banner/index.ts (1)

1-2: 배너 컴포넌트 모듈 구조 개선

타입과 컴포넌트를 명확히 분리하여 re-export하는 표준적인 모듈 구조입니다. 새로운 배너 기능의 사용성을 향상시키는 좋은 패턴입니다.

apps/client/src/pages/dashboard/components/ui/feedback/Modal.tsx (1)

29-29: LevelInfoModal에서 Modal 사용 영향 검토 필요

apps/client/src/pages/dashboard/components/ui/overlays/LevelInfoModal.tsx에서 아래와 같이 Modal을 사용 중입니다.
리팩토링으로 Modal 내부의 고정 positioning, backdrop 스타일, backdrop 클릭 시 닫기 핸들러가 제거되어, 기존 기대 동작이 손상될 수 있습니다.

  • 확인 대상
    • backdrop dim 처리 및 클릭(onClose 호출) 동작 보장 여부
    • 모달의 위치(fixed, z-index 등) 및 레이아웃이 LevelInfoModal에서 올바르게 적용되는지

필요 시 LevelInfoModal에서 className 또는 별도 prop(e.g. backdropClassName, onBackdropClick)을 통해 스타일 및 동작을 직접 설정하거나, Modal 컴포넌트에 해당 API를 추가로 노출하는 방안을 검토해주세요.

apps/client/src/pages/dashboard/constants/index.ts (1)

1-5: 상수 export 구조 개선이 잘 되었습니다.

상대 경로로의 변경과 새로운 dashboard, bannerImages export 추가가 적절합니다. 배너 기능 추가와 일치하는 구조적 개선입니다.

apps/client/src/pages/dashboard/components/ui/overlays/LevelInfoModal.tsx (1)

11-19: React Portal 사용이 적절합니다.

조건부 렌더링과 createPortal 사용으로 모달의 DOM 트리 관리가 개선되었습니다. document.body에 렌더링하여 z-index 및 overflow 문제를 효과적으로 해결할 수 있습니다.

apps/client/src/pages/dashboard/components/sections/BannerSection.tsx (2)

5-10: Props 인터페이스가 잘 정의되었습니다.

BannerStage 타입을 사용한 acornCount prop과 선택적 props들이 적절히 정의되었습니다. 타입 안전성과 유연성을 모두 고려한 좋은 설계입니다.


27-27: memo 사용이 적절합니다.

성능 최적화를 위한 React.memo 사용이 좋습니다. 배너 섹션이 자주 리렌더링될 수 있는 대시보드 컴포넌트에서 효과적입니다.

apps/client/src/pages/dashboard/constants/bannerImages.ts (2)

21-21: 타입 정의 잘 구현됨

BannerStage 타입을 keyof typeof BANNER_IMAGES로 정의하여 타입 안전성을 보장하는 좋은 패턴입니다.


1-8: 배너 이미지 중복 설명 확인 요청

apps/client/src/assets/banner/chippy-stages 경로에 스테이지별 SVG 파일은 모두 존재하며, 숫자 프리픽스(0~7)를 포함해 파일명 자체는 고유합니다.
다만 아래 그룹별로 동일한 설명(파일명 suffix)을 공유하고 있으니, 의도된 중복인지 확인 부탁드립니다:

  • 0 & 1:
    • 0_쫄쫄 굶은 치삐.svg
    • 1_쫄쫄 굶은 치삐.svg
  • 3~6:
    • 3_출출한 치삐.svg
    • 4_출출한 치삐.svg
    • 5_출출한 치삐.svg
    • 6_출출한 치삐.svg

의도된 중복이라면 그대로 유지하셔도 되며, 필요 시 파일명이나 설명을 좀 더 구분해 주시기 바랍니다.

apps/client/src/pages/dashboard/dashboard.tsx (2)

59-61: 배너 레이아웃 구조 잘 구현됨

헤더에 바로 붙는 배너 레이아웃과 나머지 컨텐츠의 패딩 분리가 잘 설계되었습니다.


35-35: acornCount 상태 관리 잘 연결됨

useDashboard 훅에서 acornCount를 가져와 BannerSection에 전달하는 구조가 깔끔합니다.

apps/client/src/pages/dashboard/hooks/useDashboard.ts (2)

16-16: 타입 안전성 확보를 위한 범위 검증 로직 우수

Math.min(Math.max(response.acornCount, 0), 7)를 사용하여 BannerStage 타입 범위를 보장하는 것이 좋은 방법입니다.


42-44: 상태 업데이트 함수 잘 구현됨

updateAcornCount 함수가 useCallback으로 최적화되어 있고 타입 안전합니다.

apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1)

54-73: CVA 를 사용한 배리언트 시스템 잘 구현됨

class-variance-authority를 사용하여 acornCount에 따른 배경색 변화를 깔끔하게 구현했습니다.

Comment on lines 19 to 20
// 테스트용 고정값 설정
setAcornCount(7); // 2개 도토리로 설정
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 테스트 값 제거 필요

프로덕션 환경에서는 하드코딩된 테스트 값을 제거하고 실제 API 호출을 활성화해야 합니다.

-      // 테스트용 고정값 설정
-      setAcornCount(7); // 2개 도토리로 설정
+      // API 호출 활성화 시 아래 코드 사용
+      const response = await getAcornCount();
+      const bannerStage = Math.min(Math.max(response.acornCount, 0), 7) as BannerStage;
+      setAcornCount(bannerStage);
📝 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.

Suggested change
// 테스트용 고정값 설정
setAcornCount(7); // 2개 도토리로 설정
// API 호출 활성화 시 아래 코드 사용
const response = await getAcornCount();
const bannerStage = Math.min(Math.max(response.acornCount, 0), 7) as BannerStage;
setAcornCount(bannerStage);
🤖 Prompt for AI Agents
In apps/client/src/pages/dashboard/hooks/useDashboard.ts around lines 19 to 20,
remove the hardcoded test value setAcornCount(7) and replace it with a call to
the actual API that fetches the acorn count. Ensure the API call is properly
integrated and the state is updated with the real data instead of a fixed test
number.

Comment on lines 86 to 96
const chippyStageFilenames = [
'0_쫄쫄 굶은 치삐.svg',
'1_쫄쫄 굶은 치삐.svg',
'2_배고픈 치삐.svg',
'3_출출한 치삐.svg',
'4_출출한 치삐.svg',
'5_출출한 치삐.svg',
'6_출출한 치삐.svg',
'7_행복한 치삐.svg',
];
const chippyImage = `/src/assets/banner/chippy-stages/${chippyStageFilenames[acornCount ?? 0]}`;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

중복된 이미지 파일명 로직

chippyStageFilenames 배열이 bannerImages.ts의 임포트와 중복됩니다. 이미 정의된 상수를 사용하세요.

+import { BANNER_IMAGES } from '@pages/dashboard/constants';

-  const chippyStageFilenames = [
-    '0_쫄쫄 굶은 치삐.svg',
-    '1_쫄쫄 굶은 치삐.svg',
-    '2_배고픈 치삐.svg',
-    '3_출출한 치삐.svg',
-    '4_출출한 치삐.svg',
-    '5_출출한 치삐.svg',
-    '6_출출한 치삐.svg',
-    '7_행복한 치삐.svg',
-  ];
-  const chippyImage = `/src/assets/banner/chippy-stages/${chippyStageFilenames[acornCount ?? 0]}`;
+  const chippyImage = BANNER_IMAGES[acornCount ?? 0];
📝 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.

Suggested change
const chippyStageFilenames = [
'0_쫄쫄 굶은 치삐.svg',
'1_쫄쫄 굶은 치삐.svg',
'2_배고픈 치삐.svg',
'3_출출한 치삐.svg',
'4_출출한 치삐.svg',
'5_출출한 치삐.svg',
'6_출출한 치삐.svg',
'7_행복한 치삐.svg',
];
const chippyImage = `/src/assets/banner/chippy-stages/${chippyStageFilenames[acornCount ?? 0]}`;
// at the top of the file
import { BANNER_IMAGES } from '@pages/dashboard/constants';
// …other code…
// replace the old filename array + template literal with:
const chippyImage = BANNER_IMAGES[acornCount ?? 0];
🤖 Prompt for AI Agents
In apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx around lines
86 to 96, the chippyStageFilenames array duplicates image file names already
defined in bannerImages.ts. To fix this, remove the local chippyStageFilenames
array and import the existing constant from bannerImages.ts instead. Then use
the imported constant to get the image filename by acornCount index, ensuring no
duplication of image file name definitions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (2)

8-19: 에셋 임포트 경로 일관성 문제

이전 리뷰 코멘트에서 지적된 문제가 여전히 존재합니다. /src/assets/@assets 별칭이 혼재되어 사용되고 있습니다.


94-142: 컴포넌트 크기 및 구조 개선 필요

이전 리뷰 코멘트에서 지적된 대로, 컴포넌트가 너무 크고 여러 책임을 가지고 있습니다. 분리가 필요합니다.

🧹 Nitpick comments (5)
apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (1)

29-29: 하드코딩된 CSS 위치 값

절대 위치 값이 하드코딩되어 있어 유지보수성이 떨어집니다. 상수로 정의하여 관리하는 것을 고려해보세요.

+const CHIPPY_IMAGE_POSITION = "absolute left-[73.9rem] w-[59rem] h-[40rem] object-contain";

-      className="absolute left-[73.9rem] w-[59rem] h-[40rem] object-contain"
+      className={CHIPPY_IMAGE_POSITION}
apps/client/src/pages/dashboard/components/ui/banner/BannerLevelInfo.tsx (2)

44-48: 모달 위치 및 z-index 검토 필요

모달 위치가 하드코딩된 값으로 설정되어 있고, z-index 9999는 매우 높은 값입니다. 다른 UI 요소와의 충돌을 피하기 위해 검토가 필요합니다.

-              <div className="absolute top-full left-[1rem] mt-[1rem] z-[9999]">
+              <div className="absolute top-full left-[1rem] mt-[1rem] z-50">

28-32: 접근성 개선 제안

호버 상호작용만으로는 터치 기기에서 접근이 어려울 수 있습니다. 클릭 이벤트 추가를 고려해보세요.

      <div
        className="absolute left-[23.4rem] top-[29.5rem] z-20 flex items-center justify-center h-[3.7rem]"
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
+        onClick={() => setIsModalOpen(!isModalOpen)}
      >
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (2)

29-41: 타입 안전성 개선 필요

getProgressBarImage 함수에서 키 타입 단언을 사용하고 있지만, 더 안전한 접근 방법을 고려해보세요.

-  return progressBarImages[acornCount as keyof typeof progressBarImages] || progressBar0;
+  const safeKey = Math.max(0, Math.min(acornCount, 7)) as keyof typeof progressBarImages;
+  return progressBarImages[safeKey];

81-93: 불필요한 null 체크 중복

acornCount 기본값이 0으로 설정되어 있는데, 헬퍼 함수 호출 시 ?? 0이 중복 사용되고 있습니다.

-  const progressBarImage = getProgressBarImage(acornCount ?? 0);
-  const headlineText = getHeadlineText(acornCount ?? 0);
-  const levelText = getLevelText(acornCount ?? 0);
+  const progressBarImage = getProgressBarImage(acornCount);
+  const headlineText = getHeadlineText(acornCount);
+  const levelText = getLevelText(acornCount);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c0476d and 0740b32.

⛔ Files ignored due to path filters (1)
  • apps/client/src/assets/brand/logo.svg is excluded by !**/*.svg
📒 Files selected for processing (10)
  • apps/client/src/pages/dashboard/components/sections/BookmarkSection.tsx (2 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerAcornBubble.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerLevelInfo.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerTimer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/buttons/AllViewButton.tsx (2 hunks)
  • apps/client/src/pages/dashboard/dashboard.tsx (3 hunks)
  • apps/client/src/shared/components/ui/layout/Header.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • apps/client/src/pages/dashboard/components/sections/BookmarkSection.tsx
  • apps/client/src/pages/dashboard/components/ui/banner/BannerTimer.tsx
  • apps/client/src/pages/dashboard/components/ui/banner/BannerAcornBubble.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/client/src/shared/components/ui/layout/Header.tsx
  • apps/client/src/pages/dashboard/components/ui/buttons/AllViewButton.tsx
  • apps/client/src/pages/dashboard/dashboard.tsx
🧰 Additional context used
🧠 Learnings (3)
apps/client/src/pages/dashboard/components/ui/banner/BannerLevelInfo.tsx (2)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (2)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
🔇 Additional comments (1)
apps/client/src/pages/dashboard/components/ui/banner/index.ts (1)

1-6: 깔끔한 배럴 익스포트 구조

배너 컴포넌트들의 중앙 집중식 익스포트 구조가 잘 구성되어 있습니다. 타입과 컴포넌트가 적절히 분리되어 있어 가독성이 좋습니다.

@karnelll karnelll self-assigned this Jul 15, 2025
@karnelll karnelll added 🛠️ Feature 기능 개발 🪄 Refactor 코드 리팩토링 서희 labels Jul 15, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (1)

21-23: 하드코딩된 위치값 개선을 제안합니다.

현재 절대 위치값이 하드코딩되어 있어 반응형 디자인에 제약이 있을 수 있습니다.

CSS 변수나 테마 시스템을 사용하여 보다 유지보수하기 쉬운 형태로 개선할 것을 고려해보세요:

const BANNER_POSITIONS = {
-  HEADLINE: 'left-[12rem] top-[6.3rem]',
+  HEADLINE: 'left-48 top-24 md:left-[12rem] md:top-[6.3rem]',
} as const;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c5a1fb and b39760b.

📒 Files selected for processing (8)
  • apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx (2 hunks)
  • apps/client/src/pages/dashboard/components/ui/feedback/Modal.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/constants/bannerImages.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/client/src/pages/dashboard/components/ui/feedback/Modal.tsx
  • apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx
  • apps/client/src/pages/dashboard/constants/bannerImages.ts
  • apps/client/src/pages/dashboard/components/ui/banner/index.ts
  • apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx
  • apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx
🧰 Additional context used
🧠 Learnings (2)
apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
🔇 Additional comments (7)
apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (4)

1-6: import 문과 인터페이스 정의가 적절합니다.

필요한 React 훅들을 올바르게 import하고, reminderHour prop의 기본값과 주석이 명확합니다.


8-10: 사용하지 않는 콜백 매개변수 처리가 적절합니다.

_onComplete 매개변수에 언더스코어 접두사를 사용하여 의도적으로 사용하지 않음을 명확히 표시했습니다. 초기 상태 설정도 적절합니다.


11-39: 타이머 로직이 올바르게 구현되었으며 의존성 배열 문제가 해결되었습니다.

이전 리뷰에서 지적된 useEffect 의존성 배열 누락 문제가 39줄에서 [reminderHour]로 올바르게 수정되었습니다. 타이머 계산 로직도 정확하며, 메모리 누수 방지를 위한 cleanup 함수도 적절히 구현되었습니다.


41-44: 간단하고 명확한 렌더링 로직입니다.

시간 문자열을 단순한 span으로 렌더링하고 memo로 최적화한 것이 적절합니다.

apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (3)

1-3: 인터페이스 정의가 명확합니다.

BannerHeadlineProps 인터페이스가 단순하고 명확하게 정의되어 있습니다.


25-43: 컴포넌트 구조가 잘 설계되었습니다.

메인 컴포넌트의 구조와 렌더링 로직이 명확하고 직관적입니다. 조건부 텍스트 색상 적용과 동적 텍스트 렌더링이 잘 구현되어 있습니다.


5-19: 조건부 로직에서 중요한 문제를 발견했습니다.

getHeadlineText 함수의 조건부 로직이 올바르지 않습니다. 현재 구조에서는 일부 조건이 도달할 수 없는 코드가 됩니다.

다음과 같이 수정하는 것을 제안합니다:

-  if (acornCount === 0) {
-    return ['0개??', '나 도토리 맛만 보면 안 되나...'];
-  }
-  if (acornCount <= 1) {
-    return ['도토리 먹고 싶다고 했다이가', '...'];
-  }
-  if (acornCount <= 4) {
-    return ['도토리 더 안 주나...', '나 배고픈디'];
-  }
-  if (acornCount <= 6) {
-    return ['니 쫌 하는데?', '내 뽀뽀 받아라'];
-  }
+  if (acornCount === 0) {
+    return ['0개??', '나 도토리 맛만 보면 안 되나...'];
+  }
+  if (acornCount === 1) {
+    return ['도토리 먹고 싶다고 했다이가', '...'];
+  }
+  if (acornCount >= 2 && acornCount <= 4) {
+    return ['도토리 더 안 주나...', '나 배고픈디'];
+  }
+  if (acornCount >= 5 && acornCount <= 6) {
+    return ['니 쫌 하는데?', '내 뽀뽀 받아라'];
+  }

Likely an incorrect or invalid review comment.

@karnelll karnelll changed the title Feature/#80/banner component Feature(client)/#80/banner component Jul 15, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (8)
apps/client/src/pages/dashboard/components/ui/banner/core/BannerHeadline.tsx (2)

5-19: 텍스트 조건 로직 개선을 고려해보세요.

현재 조건문이 중복되는 범위를 가지고 있습니다. 더 명확하고 유지보수하기 쉽도록 개선할 수 있습니다.

-  if (acornCount === 0) {
-    return ['0개??', '나 도토리 맛만 보면 안 되나...'];
-  }
-  if (acornCount <= 1) {
-    return ['도토리 먹고 싶다고 했다이가', '...'];
-  }
-  if (acornCount <= 4) {
-    return ['도토리 더 안 주나...', '나 배고픈디'];
-  }
-  if (acornCount <= 6) {
-    return ['니 쫌 하는데?', '내 뽀뽀 받아라'];
-  }
-  return ['니 대박인데?', '내 배불러 죽겠다'];
+  if (acornCount === 0) {
+    return ['0개??', '나 도토리 맛만 보면 안 되나...'];
+  }
+  if (acornCount === 1) {
+    return ['도토리 먹고 싶다고 했다이가', '...'];
+  }
+  if (acornCount >= 2 && acornCount <= 4) {
+    return ['도토리 더 안 주나...', '나 배고픈디'];
+  }
+  if (acornCount >= 5 && acornCount <= 6) {
+    return ['니 쫌 하는데?', '내 뽀뽀 받아라'];
+  }
+  return ['니 대박인데?', '내 배불러 죽겠다'];

21-23: 하드코딩된 위치값을 상수로 분리하는 것이 좋습니다.

현재 위치값이 하드코딩되어 있어 유지보수가 어려울 수 있습니다. 다른 컴포넌트에서도 비슷한 패턴을 사용하고 있으니 공통 상수 파일로 분리하는 것을 고려해보세요.

apps/client/src/pages/dashboard/components/ui/banner/elements/BannerLevelInfo.tsx (2)

39-39: 인라인 스타일 사용을 피하는 것이 좋습니다.

Tailwind CSS 클래스로 대체할 수 있는 인라인 스타일이 있습니다.

-            style={{ display: 'inline-block', verticalAlign: 'middle' }}
+            className="ml-[0.4rem] h-[2.4rem] w-[2.4rem] object-contain inline-block align-middle"

44-44: 모달 위치 계산이 복잡합니다.

중첩된 절대 위치 지정이 복잡하고 유지보수가 어려울 수 있습니다. 위치 계산을 단순화하거나 CSS 변수를 사용하는 것을 고려해보세요.

apps/client/src/pages/dashboard/components/ui/banner/elements/BannerTimer.tsx (1)

9-9: 매직 넘버를 상수로 분리하는 것이 좋습니다.

숫자 7이 여러 컴포넌트에서 사용되는 것 같은데, 의미있는 상수로 분리하는 것을 고려해보세요.

+const MAX_ACORN_COUNT = 7;
+
 const BannerTimer = ({ acornCount }: BannerTimerProps) => {
-  if (acornCount !== 7) {
+  if (acornCount !== MAX_ACORN_COUNT) {
     return null;
   }
apps/client/src/pages/dashboard/components/ui/banner/core/Banner.tsx (3)

20-29: 포지셔닝 상수들의 유지보수성 개선을 고려해보세요

하드코딩된 포지셔닝 값들이 많아 유지보수가 어려울 수 있습니다. 반응형 디자인이나 레이아웃 변경 시 모든 값을 개별적으로 수정해야 합니다.

더 체계적인 접근 방식을 위해 다음과 같이 개선할 수 있습니다:

-const BANNER_POSITIONS = {
-  HEADLINE: 'left-[12rem] top-[6.3rem]',
-  PROGRESS_BAR: 'left-[11.3rem] top-[21.7rem]',
-  LEFT_BUBBLE: 'left-[9.8rem] top-[29.5rem]',
-  LEVEL_TEXT: 'left-[23.4rem] top-[29.5rem]',
-  RIGHT_BUBBLE: 'left-[49.6rem] top-[29.5rem]',
-  TIMER_BUBBLE: 'left-[75.3rem] top-[32.8rem]',
-  TIMER_TEXT: 'left-[77rem] top-[33.9rem]',
-  CHIPPY_IMAGE: 'left-[73.9rem]',
-} as const;
+const BANNER_LAYOUT = {
+  BASELINE: {
+    LEFT: 12, // rem
+    TOP: 6.3, // rem
+  },
+  OFFSETS: {
+    PROGRESS_BAR: { left: -0.7, top: 15.4 },
+    LEFT_BUBBLE: { left: -2.2, top: 23.2 },
+    // ... 나머지 오프셋들
+  },
+} as const;

126-126: 고정된 도토리 개수 값들을 상수로 추출하세요

BannerAcornBubble 컴포넌트에서 사용되는 고정 값들(0, 7)을 상수로 추출하면 의도가 더 명확해집니다.

+const ACORN_RANGE = {
+  MIN: 0,
+  MAX: 7,
+} as const;
+
  {/* 도토리 개수 말풍선 - 왼쪽 */}
-  <BannerAcornBubble count={0} position="left" />
+  <BannerAcornBubble count={ACORN_RANGE.MIN} position="left" />

  {/* 도토리 개수 말풍선 - 오른쪽 */}
-  <BannerAcornBubble count={7} position="right" />
+  <BannerAcornBubble count={ACORN_RANGE.MAX} position="right" />

Also applies to: 132-132


93-143: 컴포넌트의 복잡도를 줄이기 위한 분리를 고려하세요

Banner 컴포넌트가 많은 책임을 가지고 있어 복잡합니다. 레이아웃 로직을 별도의 컴포넌트로 분리하는 것을 고려해보세요.

렌더링 로직을 더 작은 컴포넌트들로 분리할 수 있습니다:

// BannerLayout.tsx
const BannerLayout = ({ acornCount, children }: BannerLayoutProps) => {
  // 레이아웃 관련 로직
};

// BannerProgressSection.tsx  
const BannerProgressSection = ({ acornCount }: BannerProgressSectionProps) => {
  // 프로그레스 바 및 말풍선 관련 로직
};

// Banner.tsx (수정된 버전)
const Banner = ({ acornCount, children, className, showImage }: BannerProps) => {
  return (
    <BannerLayout acornCount={acornCount} className={className}>
      <BannerProgressSection acornCount={acornCount} />
      <BannerChippyImage acornCount={acornCount} showImage={showImage} />
      {children}
    </BannerLayout>
  );
};
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b39760b and 83283b0.

📒 Files selected for processing (10)
  • apps/client/src/pages/dashboard/components/ui/banner/core/Banner.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/core/BannerHeadline.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/core/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/elements/BannerAcornBubble.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/elements/BannerChippyImage.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/elements/BannerLevelInfo.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/elements/BannerTimer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/elements/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • apps/client/src/pages/dashboard/components/ui/banner/core/index.ts
  • apps/client/src/pages/dashboard/components/ui/banner/elements/index.ts
  • apps/client/src/pages/dashboard/components/ui/banner/elements/BannerAcornBubble.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx
🧰 Additional context used
🧠 Learnings (4)
apps/client/src/pages/dashboard/components/ui/banner/elements/BannerChippyImage.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/banner/core/BannerHeadline.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/banner/elements/BannerLevelInfo.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/banner/core/Banner.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
🔇 Additional comments (11)
apps/client/src/pages/dashboard/components/ui/banner/index.ts (1)

1-6: 코드 구조가 명확하고 좋습니다!

배럴 내보내기 패턴을 사용하여 core와 elements 모듈을 깔끔하게 분리했습니다. 컴포넌트 라이브러리 구조에 적합한 표준 패턴입니다.

apps/client/src/pages/dashboard/components/ui/banner/core/BannerHeadline.tsx (1)

32-37: 키 속성으로 인덱스 사용이 적절합니다.

텍스트 배열이 변경되지 않으므로 인덱스를 키로 사용하는 것이 적절합니다. 조건부 렌더링과 함께 잘 구현되었습니다.

apps/client/src/pages/dashboard/components/ui/banner/elements/BannerChippyImage.tsx (3)

16-18: 조건부 렌더링 패턴이 좋습니다.

showImage prop을 통한 조건부 렌더링이 명확하고 효율적입니다.


20-24: 안전한 배열 접근 로직이 잘 구현되었습니다.

Math.max와 Math.min을 사용한 범위 제한이 배열 경계 오류를 방지합니다. 타입 단언도 적절히 사용되었습니다.


29-29: 접근성을 고려한 alt 텍스트가 좋습니다.

이미지에 대한 설명적인 alt 텍스트가 접근성을 향상시킵니다.

apps/client/src/pages/dashboard/components/ui/banner/elements/BannerLevelInfo.tsx (1)

13-23: 이벤트 핸들러가 적절하게 구현되었습니다.

마우스 hover 이벤트와 모달 닫기 핸들러가 명확하고 단순합니다.

apps/client/src/pages/dashboard/components/ui/banner/elements/BannerTimer.tsx (3)

9-11: 조건부 렌더링이 명확합니다.

acornCount가 7일 때만 렌더링하는 로직이 명확하고 적절합니다.


16-21: 이미지 접근성 설정이 좋습니다.

alt 텍스트와 draggable="false" 설정이 사용자 경험을 향상시킵니다.


24-34: 타이머 텍스트 레이아웃이 잘 구성되었습니다.

Flexbox를 사용한 레이아웃과 간격, 텍스트 스타일링이 적절합니다. Timer 컴포넌트 임베딩도 자연스럽습니다.

apps/client/src/pages/dashboard/components/ui/banner/core/Banner.tsx (2)

99-99: 안전한 기본값 처리 로직이 잘 구현되어 있습니다

null 및 undefined 값에 대한 안전한 처리가 적절히 구현되어 있어 런타임 에러를 방지합니다.


106-113: 조건부 배경 그래픽 렌더링 로직이 명확합니다

도토리 7개일 때만 특별한 배경 그래픽을 표시하는 로직이 PR 목표에 부합하고 올바르게 구현되어 있습니다.


{/* 모달 - 아이콘으로부터 적당한 거리 아래, 4rem 더 왼쪽에 직접 연결 */}
{isModalOpen && (
<div className="absolute left-[1rem] top-full z-[9999] mt-[1rem]">
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

z-index 값 관리를 일관되게 해주세요.

z-[9999]와 같은 높은 z-index 값은 전역적으로 관리하는 것이 좋습니다.

z-index 값들이 일관되게 관리되고 있는지 확인해보세요:


🏁 Script executed:

#!/bin/bash
# z-index 값 사용 현황 확인
rg -n "z-\[" --type tsx --type ts -A 1 -B 1

Length of output: 73


이전 검색에서 ‘tsx’ 파일 형식을 인식하지 못해 z-index 사용 현황이 확인되지 않았습니다. 모든 .ts/.tsx 파일을 대상으로 다시 확인해 주세요:


🏁 Script executed:

#!/bin/bash
# 모든 .ts 및 .tsx 파일에서 z-[숫자] 형태의 z-index 사용 현황 확인
rg -n "z-\[[0-9]+\]" -g "*.ts" -g "*.tsx" -A1 -B1

Length of output: 434


JS/JSX 파일 전체에서 임의의 z-index 사용과 Tailwind 설정 내 zIndex 확장 여부를 한 번에 점검해 주세요.


🏁 Script executed:

#!/bin/bash
echo "1) z-[숫자] 형태의 z-index 사용 현황 (TS/TSX/JS/JSX)"
rg -n "z-\[[0-9]+\]" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.jsx" -A1 -B1

echo
echo "2) tailwind.config.*에서 custom zIndex 설정 확인"
rg -n "zIndex" -g "tailwind.config.js" -g "tailwind.config.ts" -A5 -B5

Length of output: 722


일관된 z-index 관리 설정 필요

현재 BannerLevelInfo.tsx(44행)에서 z-[9999]를 직접 사용하고 있지만, 전역적인 z-index 관리(예: Tailwind 설정 확장)가 되어 있지 않습니다. 아래와 같이 수정해 주세요.

• tailwind.config.js/ts에 zIndex 확장 추가

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      zIndex: {
        modal: '9999',
        // tooltip: '9998',
        // dropdown: '9997',
        // … 필요에 따라 추가
      }
    }
  }
}

• BannerLevelInfo.tsx(44행)

- <div className="absolute left-[1rem] top-full z-[9999] mt-[1rem]">
+ <div className="absolute left-[1rem] top-full z-modal mt-[1rem]">

• 이후 다른 컴포넌트에서도 동일한 커스텀 zIndex 클래스(z-modal, z-tooltip 등)를 재사용해 주세요.

🤖 Prompt for AI Agents
In
apps/client/src/pages/dashboard/components/ui/banner/elements/BannerLevelInfo.tsx
at line 44, replace the hardcoded z-index class z-[9999] with a custom class
like z-modal. To do this, first extend the zIndex values in tailwind.config.js
by adding a modal key with the value '9999'. Then update the div's className to
use z-modal instead of z-[9999]. This approach ensures consistent and
maintainable z-index management across the project.

Comment on lines 48 to 62
const getLevelText = (acornCount: number): string => {
if (acornCount === 0) {
return 'LV.1 쫄쫄 굶은 치삐';
}
if (acornCount <= 2) {
return 'LV.2 배고픈 치삐';
}
if (acornCount <= 4) {
return 'LV.3 출출한 치삐';
}
if (acornCount <= 6) {
return 'LV.4 배부른 치삐';
}
return 'LV.5 행복한 치삐';
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

매직 넘버를 상수로 추출하여 가독성을 높이세요

레벨 구간을 나타내는 숫자들을 상수로 추출하면 코드의 의도가 더 명확해집니다.

+const LEVEL_THRESHOLDS = {
+  STARVING: 0,
+  HUNGRY: 2,
+  PECKISH: 4,
+  FULL: 6,
+  HAPPY: 7,
+} as const;
+
+const LEVEL_TEXTS = {
+  STARVING: 'LV.1 쫄쫄 굶은 치삐',
+  HUNGRY: 'LV.2 배고픈 치삐',
+  PECKISH: 'LV.3 출출한 치삐',
+  FULL: 'LV.4 배부른 치삐',
+  HAPPY: 'LV.5 행복한 치삐',
+} as const;
+
const getLevelText = (acornCount: number): string => {
-  if (acornCount === 0) {
-    return 'LV.1 쫄쫄 굶은 치삐';
+  if (acornCount === LEVEL_THRESHOLDS.STARVING) {
+    return LEVEL_TEXTS.STARVING;
  }
-  if (acornCount <= 2) {
-    return 'LV.2 배고픈 치삐';
+  if (acornCount <= LEVEL_THRESHOLDS.HUNGRY) {
+    return LEVEL_TEXTS.HUNGRY;
  }
-  if (acornCount <= 4) {
-    return 'LV.3 출출한 치삐';
+  if (acornCount <= LEVEL_THRESHOLDS.PECKISH) {
+    return LEVEL_TEXTS.PECKISH;
  }
-  if (acornCount <= 6) {
-    return 'LV.4 배부른 치삐';
+  if (acornCount <= LEVEL_THRESHOLDS.FULL) {
+    return LEVEL_TEXTS.FULL;
  }
-  return 'LV.5 행복한 치삐';
+  return LEVEL_TEXTS.HAPPY;
};
📝 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.

Suggested change
const getLevelText = (acornCount: number): string => {
if (acornCount === 0) {
return 'LV.1 쫄쫄 굶은 치삐';
}
if (acornCount <= 2) {
return 'LV.2 배고픈 치삐';
}
if (acornCount <= 4) {
return 'LV.3 출출한 치삐';
}
if (acornCount <= 6) {
return 'LV.4 배부른 치삐';
}
return 'LV.5 행복한 치삐';
};
const LEVEL_THRESHOLDS = {
STARVING: 0,
HUNGRY: 2,
PECKISH: 4,
FULL: 6,
HAPPY: 7,
} as const;
const LEVEL_TEXTS = {
STARVING: 'LV.1 쫄쫄 굶은 치삐',
HUNGRY: 'LV.2 배고픈 치삐',
PECKISH: 'LV.3 출출한 치삐',
FULL: 'LV.4 배부른 치삐',
HAPPY: 'LV.5 행복한 치삐',
} as const;
const getLevelText = (acornCount: number): string => {
if (acornCount === LEVEL_THRESHOLDS.STARVING) {
return LEVEL_TEXTS.STARVING;
}
if (acornCount <= LEVEL_THRESHOLDS.HUNGRY) {
return LEVEL_TEXTS.HUNGRY;
}
if (acornCount <= LEVEL_THRESHOLDS.PECKISH) {
return LEVEL_TEXTS.PECKISH;
}
if (acornCount <= LEVEL_THRESHOLDS.FULL) {
return LEVEL_TEXTS.FULL;
}
return LEVEL_TEXTS.HAPPY;
};
🤖 Prompt for AI Agents
In apps/client/src/pages/dashboard/components/ui/banner/core/Banner.tsx between
lines 48 and 62, the numeric values defining level thresholds are hardcoded
magic numbers. Extract these numbers into clearly named constants at the top of
the file or near the function to improve readability and clarify their meaning.
Replace the numeric literals in the getLevelText function with these constants.

Comment on lines 31 to 46
const getProgressBarImage = (acornCount: number): string => {
const progressBarImages = {
0: progressBar0,
1: progressBar1,
2: progressBar2,
3: progressBar3,
4: progressBar4,
5: progressBar5,
6: progressBar6,
7: progressBar7,
};
return (
progressBarImages[acornCount as keyof typeof progressBarImages] ||
progressBar0
);
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

타입 안전성을 위해 타입 단언을 개선하세요

현재 타입 단언 방식은 런타임 에러 가능성이 있습니다. 더 안전한 타입 가드를 사용하는 것이 좋습니다.

const getProgressBarImage = (acornCount: number): string => {
  const progressBarImages = {
-  };
+  } as const;
+  
+  const validAcornCount = Math.max(0, Math.min(7, Math.floor(acornCount)));
  return (
-    progressBarImages[acornCount as keyof typeof progressBarImages] ||
-    progressBar0
+    progressBarImages[validAcornCount as keyof typeof progressBarImages]
  );
};
📝 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.

Suggested change
const getProgressBarImage = (acornCount: number): string => {
const progressBarImages = {
0: progressBar0,
1: progressBar1,
2: progressBar2,
3: progressBar3,
4: progressBar4,
5: progressBar5,
6: progressBar6,
7: progressBar7,
};
return (
progressBarImages[acornCount as keyof typeof progressBarImages] ||
progressBar0
);
};
const getProgressBarImage = (acornCount: number): string => {
const progressBarImages = {
0: progressBar0,
1: progressBar1,
2: progressBar2,
3: progressBar3,
4: progressBar4,
5: progressBar5,
6: progressBar6,
7: progressBar7,
} as const;
const validAcornCount = Math.max(0, Math.min(7, Math.floor(acornCount)));
return (
progressBarImages[validAcornCount as keyof typeof progressBarImages]
);
};
🤖 Prompt for AI Agents
In apps/client/src/pages/dashboard/components/ui/banner/core/Banner.tsx lines 31
to 46, the current use of type assertion for accessing progressBarImages with
acornCount can cause runtime errors if acornCount is out of the expected range.
Replace the type assertion with a safer type guard by explicitly checking if
acornCount exists as a key in progressBarImages before accessing it, and return
progressBar0 as a fallback if it does not.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (1)

5-19: getHeadlineText 함수를 룩업 테이블로 개선하세요

현재 여러 if 문을 사용한 구조보다는 룩업 테이블을 사용하면 가독성과 유지보수성이 향상됩니다.

-const getHeadlineText = (acornCount: number): string[] => {
-  if (acornCount === 0) {
-    return ['0개??', '나 도토리 맛만 보면 안 되나...'];
-  }
-  if (acornCount <= 1) {
-    return ['도토리 먹고 싶다고 했다이가', '...'];
-  }
-  if (acornCount <= 4) {
-    return ['도토리 더 안 주나...', '나 배고픈디'];
-  }
-  if (acornCount <= 6) {
-    return ['니 쫌 하는데?', '내 뽀뽀 받아라'];
-  }
-  return ['니 대박인데?', '내 배불러 죽겠다'];
-};
+const HEADLINE_MESSAGES = {
+  0: ['0개??', '나 도토리 맛만 보면 안 되나...'],
+  1: ['도토리 먹고 싶다고 했다이가', '...'],
+  2: ['도토리 더 안 주나...', '나 배고픈디'],
+  5: ['니 쫌 하는데?', '내 뽀뽀 받아라'],
+  7: ['니 대박인데?', '내 배불러 죽겠다'],
+} as const;
+
+const getHeadlineText = (acornCount: number): string[] => {
+  if (acornCount === 0) return HEADLINE_MESSAGES[0];
+  if (acornCount <= 1) return HEADLINE_MESSAGES[1];
+  if (acornCount <= 4) return HEADLINE_MESSAGES[2];
+  if (acornCount <= 6) return HEADLINE_MESSAGES[5];
+  return HEADLINE_MESSAGES[7];
+};
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1)

9-16: 프로그레스 바 임포트를 상수 객체로 통합하세요

개별 임포트 대신 상수 객체를 사용하면 유지보수성이 향상됩니다.

-import progressBar0 from '@assets/banner/progress/progress-bar-0.svg';
-import progressBar1 from '@assets/banner/progress/progress-bar-1.svg';
-import progressBar2 from '@assets/banner/progress/progress-bar-2.svg';
-import progressBar3 from '@assets/banner/progress/progress-bar-3.svg';
-import progressBar4 from '@assets/banner/progress/progress-bar-4.svg';
-import progressBar5 from '@assets/banner/progress/progress-bar-5.svg';
-import progressBar6 from '@assets/banner/progress/progress-bar-6.svg';
-import progressBar7 from '@assets/banner/progress/progress-bar-7.svg';
+import { PROGRESS_BAR_IMAGES } from '@pages/dashboard/constants/bannerImages';

그리고 getProgressBarImage 함수를 다음과 같이 단순화할 수 있습니다:

const getProgressBarImage = (acornCount: number): string => {
-  const progressBarImages = {
-    0: progressBar0,
-    1: progressBar1,
-    2: progressBar2,
-    3: progressBar3,
-    4: progressBar4,
-    5: progressBar5,
-    6: progressBar6,
-    7: progressBar7,
-  };
-  return (
-    progressBarImages[acornCount as keyof typeof progressBarImages] ||
-    progressBar0
-  );
+  return PROGRESS_BAR_IMAGES[acornCount] || PROGRESS_BAR_IMAGES[0];
};
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83283b0 and e166cbb.

📒 Files selected for processing (20)
  • apps/client/src/pages/dashboard/components/index.ts (0 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerAcornBubble.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerLevelInfo.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerTimer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/banner/index.ts (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/BookmarkCard.tsx (2 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx (2 hunks)
  • apps/client/src/pages/dashboard/components/ui/cards/Thumbnail.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/mockData.ts (6 hunks)
  • apps/client/src/pages/onBoarding/components/MacUserNoticeStep.tsx (1 hunks)
  • apps/client/src/pages/onBoarding/components/StoryStep.tsx (1 hunks)
  • apps/client/src/pages/onBoarding/components/TimeSelectStep.tsx (1 hunks)
  • apps/client/src/pages/onBoarding/components/WelcomeStep.tsx (1 hunks)
  • apps/client/src/shared/utils/styleUtils.ts (1 hunks)
  • apps/client/src/shared/utils/timerUtils.ts (0 hunks)
  • apps/client/src/vite-env.d.ts (0 hunks)
💤 Files with no reviewable changes (3)
  • apps/client/src/vite-env.d.ts
  • apps/client/src/pages/dashboard/components/index.ts
  • apps/client/src/shared/utils/timerUtils.ts
✅ Files skipped from review due to trivial changes (6)
  • apps/client/src/pages/onBoarding/components/StoryStep.tsx
  • apps/client/src/pages/onBoarding/components/WelcomeStep.tsx
  • apps/client/src/pages/onBoarding/components/TimeSelectStep.tsx
  • apps/client/src/pages/onBoarding/components/MacUserNoticeStep.tsx
  • apps/client/src/pages/dashboard/components/ui/banner/BannerTimer.tsx
  • apps/client/src/pages/dashboard/components/ui/banner/BannerAcornBubble.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/client/src/pages/dashboard/components/ui/cards/BookmarkCard.tsx
  • apps/client/src/pages/dashboard/components/ui/cards/DailyReminderCard.tsx
  • apps/client/src/pages/dashboard/components/ui/cards/Thumbnail.tsx
  • apps/client/src/pages/dashboard/mockData.ts
  • apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx
🧰 Additional context used
🧠 Learnings (4)
apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/banner/BannerLevelInfo.tsx (2)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.
apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (1)
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#30
File: apps/extension/src/components/InfoBox/InfoBox.tsx:10-11
Timestamp: 2025-07-08T11:47:27.279Z
Learning: In React TypeScript components, prefer semantic prop names with union types over abstract version numbers. For example, use `size: 'small' | 'large'` instead of `version: 1 | 2` for better code readability, type safety, and extensibility.
🧬 Code Graph Analysis (1)
apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (1)
apps/client/src/pages/dashboard/constants/bannerImages.ts (1)
  • BANNER_IMAGES (10-19)
🔇 Additional comments (7)
apps/client/src/pages/dashboard/components/ui/banner/BannerHeadline.tsx (1)

25-41: 컴포넌트 구현이 잘 되었습니다

텍스트 렌더링 로직과 조건부 스타일링이 깔끔하게 구현되어 있습니다. 특히 map을 사용한 줄바꿈 처리가 적절합니다.

apps/client/src/pages/dashboard/components/ui/banner/BannerChippyImage.tsx (2)

20-24: 안전한 인덱스 접근 로직이 잘 구현되었습니다

이전 리뷰 의견이 잘 반영되어 배열 범위를 벗어나는 경우를 안전하게 처리하고 있습니다. Math.max와 Math.min을 사용한 clamp 로직이 적절합니다.


26-32: 이미지 렌더링 구현이 적절합니다

alt 텍스트에 의미있는 정보를 포함하고 있으며, 절대 위치 지정과 스타일링이 적절하게 구현되어 있습니다.

apps/client/src/pages/dashboard/components/ui/banner/index.ts (1)

1-7: 배럴 익스포트 패턴이 잘 구현되었습니다

모든 배너 컴포넌트와 타입이 중앙화되어 있어 개발자 경험이 향상됩니다. 임포트 경로 구조가 일관되고 명확합니다.

apps/client/src/pages/dashboard/components/ui/banner/Banner.tsx (3)

64-83: CVA 변형 정의가 잘 구현되었습니다

class-variance-authority를 사용한 변형 정의가 적절하며, 도토리 개수에 따른 배경색 변화가 체계적으로 구현되어 있습니다.


104-111: 조건부 배경 이미지 렌더링이 적절합니다

도토리 7개 달성 시에만 특별한 배경 이미지를 표시하는 로직이 PR 요구사항에 맞게 잘 구현되었습니다. 이미지 속성 설정도 적절합니다.


113-130: 모듈화된 하위 컴포넌트 구조가 훌륭합니다

이전 리뷰 의견이 잘 반영되어 Banner 컴포넌트가 적절한 크기로 분할되었습니다. 각 하위 컴포넌트(BannerHeadline, BannerAcornBubble, BannerLevelInfo, BannerTimer, BannerChippyImage)가 명확한 책임을 가지고 있어 유지보수성이 향상되었습니다.

Comment on lines +42 to +52
{isModalOpen && (
<div className="absolute left-[1rem] top-full z-[9999] mt-[1rem]">
<Modal
isOpen={isModalOpen}
onClose={handleModalClose}
className=""
>
<LevelInfoSection />
</Modal>
</div>
)}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

모달 접근성 개선이 필요합니다

모달의 키보드 접근성과 포커스 관리가 부족합니다. 또한 모달이 화면 밖으로 나갈 수 있는 위치에 배치되어 있습니다.

  {isModalOpen && (
    <div className="absolute left-[1rem] top-full z-[9999] mt-[1rem]">
      <Modal
        isOpen={isModalOpen}
        onClose={handleModalClose}
-        className=""
+        className="max-w-sm"
+        aria-labelledby="level-info-title"
+        role="dialog"
      >
        <LevelInfoSection />
      </Modal>
    </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.

Suggested change
{isModalOpen && (
<div className="absolute left-[1rem] top-full z-[9999] mt-[1rem]">
<Modal
isOpen={isModalOpen}
onClose={handleModalClose}
className=""
>
<LevelInfoSection />
</Modal>
</div>
)}
{isModalOpen && (
<div className="absolute left-[1rem] top-full z-[9999] mt-[1rem]">
<Modal
isOpen={isModalOpen}
onClose={handleModalClose}
className="max-w-sm"
aria-labelledby="level-info-title"
role="dialog"
>
<LevelInfoSection />
</Modal>
</div>
)}
🤖 Prompt for AI Agents
In apps/client/src/pages/dashboard/components/ui/banner/BannerLevelInfo.tsx
around lines 42 to 52, the modal lacks proper keyboard accessibility and focus
management, and its positioning allows it to render off-screen. To fix this,
ensure the modal traps keyboard focus while open, returns focus to the
triggering element on close, and adjust the modal's CSS or positioning logic to
keep it fully visible within the viewport.

Comment on lines +13 to +19
const handleMouseEnter = () => {
setIsModalOpen(true);
};

const handleMouseLeave = () => {
setIsModalOpen(false);
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

모달 호버 UX 개선이 필요합니다

현재 마우스가 영역을 벗어나면 바로 모달이 닫히는 구조로, 사용자가 모달 내용을 상호작용하기 어려울 수 있습니다.

  const handleMouseLeave = () => {
-    setIsModalOpen(false);
+    // 약간의 지연을 두어 사용자가 모달로 마우스를 이동할 수 있도록 함
+    setTimeout(() => {
+      setIsModalOpen(false);
+    }, 300);
  };

또는 클릭 기반 토글로 변경하는 것을 고려해보세요:

- onMouseEnter={handleMouseEnter}
- onMouseLeave={handleMouseLeave}
+ onClick={() => setIsModalOpen(!isModalOpen)}
🤖 Prompt for AI Agents
In apps/client/src/pages/dashboard/components/ui/banner/BannerLevelInfo.tsx
around lines 13 to 19, the modal closes immediately when the mouse leaves the
trigger area, making it hard for users to interact with the modal content. To
fix this, change the modal toggle from mouse enter/leave events to a click-based
toggle by replacing the handleMouseEnter and handleMouseLeave functions with a
single click handler that toggles the modal open state. This will improve UX by
allowing users to open and close the modal via clicks instead of hover.

Copy link
Contributor

@jjangminii jjangminii left a comment

Choose a reason for hiding this comment

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

2단계 텍스트 3-4단계랑 같은데 1단계랑 같게 바꿔주세요-!
image

@karnelll karnelll changed the title Feature(client)/#80/banner component Feature(client): banner component Jul 15, 2025
@github-actions github-actions bot requested a review from jjangminii July 16, 2025 09:00
jjangminii
jjangminii previously approved these changes Jul 16, 2025
Copy link
Contributor

@jjangminii jjangminii left a comment

Choose a reason for hiding this comment

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

배너 분기처리 단계가 많아서 복잡했을텐데 고생하셨습니다-! 추후 상수로 관리해서 디자인 시스템 내 컨벤션으로 정의해두면 더 일관성 있게 사용할 수 있을 것 같아요-!!

Copy link
Member

@constantly-dev constantly-dev left a comment

Choose a reason for hiding this comment

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

분기 처리 많아서 힘드셨을텐데 수고하셨습니다~ 배너 완성되니까 진짜 마무리가 되는 느낌이네요!
코드 깔끔하게 정리해주신 것 같아요! 👍

ToggleButton,
} from '@pinback/design-system/ui';
import { POP_TEXTAREA_MAX_LENGTH } from '@constants/index';
import { POP_TEXTAREA_MAX_LENGTH } from '@/constants/index';
Copy link
Member

Choose a reason for hiding this comment

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

index 파일은 경로에 포함하지 않아도 적용되지 않나요?!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

빌드 오류 해결하다가 잘못 들어간 것 같아요!! 수정할게요!

Comment on lines 54 to 55
const response = await apiRequest.get<AcornCountResponse>('/api/acorn-count');
return response.data;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const response = await apiRequest.get<AcornCountResponse>('/api/acorn-count');
return response.data;
const { data } = await apiRequest.get<AcornCountResponse>('/api/acorn-count');
return data;

data만 return해서 사용할 것이라면 바로 받을 때 구조 분해 할당도 가능해요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이번 API는 테스트 용도로 작성된 것이라, 실제 연동 시에는 응답 구조에 맞춰 구조 분해 할당 등 리팩토링하도록 하겠습니다!! PR에 미리 작성해두지 못해 죄송합니다 🙏

Comment on lines 51 to 56

const getBookmarkSectionMargin = () => {
if (hasBookmarks) {
return 'mt-[14rem]';
}
return 'mt-0';
Copy link
Member

Choose a reason for hiding this comment

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

해당 mt-0을 주는 것도 좋지만 null과 같이 아예 스타일을 주지 않는다는 의미로 작성하면 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

좋은 것 같아요! 반영하겠습니다!

Comment on lines 11 to 38
useEffect(() => {
const calculateTimeLeft = () => {
const now = new Date();
const reminderTime = new Date();
reminderTime.setHours(reminderHour, 0, 0, 0);

if (now.getHours() >= reminderHour) {
reminderTime.setDate(reminderTime.getDate() + 1);
}

const diff = reminderTime.getTime() - now.getTime();
const hours = Math.floor(diff / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);

return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
};

setTimeLeft(calculateTimeLeft());

const interval = setInterval(() => {
setTimeLeft(calculateTimeLeft());
}, 1000);

return () => clearInterval(interval);
}, [reminderHour]);

return <span>{timeLeft}</span>;
Copy link
Member

Choose a reason for hiding this comment

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

해당 로직은 custom hook으로 빼도 좋을 것 같네요! 👍 그리고 diff나 hours 계산하는 로직을 하나로 묶어서 네이밍을 줘도 가독성이 좋아질 것 같습니다 굿!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

좋은 피드백 감사합니다! 🙌
말씀 주신 대로 diff나 hours 계산 로직은 하나의 함수로 묶고, 의미 있는 네이밍을 부여해서 가독성을 높이도록 리팩토링해보겠습니다.
custom hook으로 분리하는 부분도 함께 고려해볼게요!


return <span className={`sub3-m ${getTimerColor()}`}>{getTimerText()}</span>;
const Timer = ({ onComplete: _onComplete, reminderHour = 9 }: TimerProps) => {
const [timeLeft, setTimeLeft] = useState<string>('00:00:00');
Copy link
Member

Choose a reason for hiding this comment

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

useState에 초기 값을 넣어주면 타입 추론으로 인해서 따로 제니릭을 넣어주지 않아도 괜찮습니다! 물론 가독성을 위해서 넣는 것도 방법이지만, 타입 추론을 적극적으로 사용하는 것도 좋은 방법이 될 것 같아요 👍

Suggested change
const [timeLeft, setTimeLeft] = useState<string>('00:00:00');
const [timeLeft, setTimeLeft] = useState('00:00:00');

Copy link
Contributor Author

Choose a reason for hiding this comment

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

타입 추론을 활용하도록 수정해두었습니다! 감사합니다:)

}

const BannerTimer = ({ acornCount }: BannerTimerProps) => {
if (acornCount !== 7) {
Copy link
Member

Choose a reason for hiding this comment

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

마지막 단계에 해당하는 상수 값을 만들어서 다른 곳에서도 재사용하면 좋을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

네!!

- MAX_ACORN_COUNT 상수 추가로 마지막 단계 기준값 재사용 가능
- Timer 로직을 useTimer custom hook으로 분리
- 타입 추론 활용으로 제네릭 타입 제거
- API 응답에서 구조 분해 할당 사용
- 충돌하는 max-height 스타일 수정
- 조건부 스타일링 개선
@github-actions github-actions bot requested a review from constantly-dev July 16, 2025 12:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/client/src/pages/dashboard/hooks/useTimer.ts (2)

7-8: 입력 검증 추가를 권장합니다.

reminderHour 매개변수에 대한 유효성 검사가 없습니다. 0-23 범위 밖의 값이 전달되면 예상치 못한 동작이 발생할 수 있습니다.

 export const useTimer = ({ reminderHour = 9 }: UseTimerProps = {}) => {
+  if (reminderHour < 0 || reminderHour > 23) {
+    throw new Error('reminderHour must be between 0 and 23');
+  }
+  
   const [timeLeft, setTimeLeft] = useState('00:00:00');

11-26: 시간 계산 로직이 정확합니다.

시간 차이 계산과 포맷팅이 올바르게 구현되었습니다. 현재 시간이 알림 시간을 지났을 때 다음 날로 설정하는 로직도 정확합니다.

가독성을 위해 주석을 추가하는 것을 고려해보세요:

       if (now.getHours() >= reminderHour) {
+        // If current time is past reminder hour, set for next day
         reminderTime.setDate(reminderTime.getDate() + 1);
       }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec93d91 and a950def.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/client/src/pages/dashboard/components/ui/banner/BannerTimer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx (1 hunks)
  • apps/client/src/pages/dashboard/constants/levelData.ts (1 hunks)
  • apps/client/src/pages/dashboard/dashboard.tsx (4 hunks)
  • apps/client/src/pages/dashboard/hooks/useDashboard.ts (2 hunks)
  • apps/client/src/pages/dashboard/hooks/useTimer.ts (1 hunks)
  • apps/client/src/shared/apis/timerApi.ts (2 hunks)
  • apps/client/src/shared/utils/styleUtils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/client/src/pages/dashboard/constants/levelData.ts
  • apps/client/src/pages/dashboard/components/ui/indicators/Timer.tsx
  • apps/client/src/shared/apis/timerApi.ts
  • apps/client/src/pages/dashboard/components/ui/banner/BannerTimer.tsx
  • apps/client/src/pages/dashboard/hooks/useDashboard.ts
  • apps/client/src/shared/utils/styleUtils.ts
  • apps/client/src/pages/dashboard/dashboard.tsx
🔇 Additional comments (2)
apps/client/src/pages/dashboard/hooks/useTimer.ts (2)

3-5: 인터페이스 정의가 적절합니다.

TypeScript 인터페이스가 올바르게 정의되었고, 선택적 매개변수 패턴이 적절히 사용되었습니다.


10-35: useEffect 구현이 올바릅니다.

간격 설정, 정리, 의존성 배열이 모두 정확하게 구현되었습니다. 이는 PR 요약에서 언급된 Timer 컴포넌트의 useEffect 의존성 배열 버그를 수정한 것으로 보입니다.

Copy link
Contributor

@jllee000 jllee000 left a comment

Choose a reason for hiding this comment

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

고생하셨씁니당~~

@karnelll karnelll changed the title Feature(client): banner component Feature(client): 대시보드 배너 구현 및 개발 워크플로우 개선 Jul 16, 2025
@karnelll karnelll merged commit 8d819ca into develop Jul 16, 2025
2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jul 17, 2025
2 tasks
jllee000 added a commit that referenced this pull request Jul 18, 2025
* setting: create issue template

* setting: create PR template

* setting: update PR template

* setting: create CODEOWNERS for auto reviewer

* chore: 불필요한 next apps 삭제

* setting: create landing vite + react project

* setting: create client vite + react project

* chore: extension 폴더 생성

* setting: turbo.json 파일 설정

* setting: pnpm-workspace.yaml config 폴더 추가

* setting: package manager pnpm으로 강제하도록 설정

* setting: client app tsconfig pinback 공통 패키지로 설정

* setting: eslint 공통 pinback package로 설정

* setting: eslint 공통 pinback package로 설정

* chore: 불필요한 app 관련 파일 정리

* chore: 필요 없는 파일 제거

* chore: 필요 없는 파일 제거

* setting: design-system package 구조 생성

* setting: design-system 예시 코드 추가

* setting: landing app tsconfig pinback 공통 패키지로 설정

* setting: landing package.json client에 sync

* chore: 불필요한 파일 제거

* chore: design-system test용 button 컴포넌트 추가

* setting: 증분 빌드 옵션 incremental false로 설정

* chore: html lan ko로 변경

* setting: 코드래빗 설정 파일(.coderabbit.yaml) 추가

* chore: CodeRabbit 리뷰 트리거용 주석 추가

* chore: CodeRabbit 트리거용 주석 제거

* Setting : 절대경로 설정 (#6)

* setting : client 절대경로 설정

* setting : landing 절대경로 설정

* Merge pull request #16 from Pinback-Team/docs/#15/update-primitive-readme

Docs(project): README ver 1.0 작성

* Update README.md

* Setting(project): router 설정 (#14)

* Setting : landing 라우터 설정

* Setting : client 라우터 설정

* Setting : 라우터 호출 위치 변경

* Setting(project): tailwind css setup (#10)

* Setting : client tailwindcss 설치

* Setting : landing tailwind 설정

* fix : 사소한 수정

* refactor : 안쓰는 코드 제거

* Setting(project): axios query setup (#12)

* setting : landing queryClient 설정

* setting : client  Tanstack query 설정

* Stting : client Axios 설정 (instance)

* Setting : landing Axios 설정 (instance)

* refactor : 안쓰는 코드 삭제

* refactor : 안쓰는 코드 정리

* chore: 중복코드 제거

* setting(project): 깃 액션 세팅 및 vercel 배포 (#9)

* setting : 깃 액션 세팅

* fix : labeler 파일 위치 수정

* fix : 주석 제거

* setting : 버전 수정

* setting : vercel 세팅

* Setting : 자동 리뷰어 할당 수정 (#22)

* setting : 깃 액션 세팅

* fix : labeler 파일 위치 수정

* fix : 주석 제거

* setting : 버전 수정

* setting : vercel 세팅

* fix : 리뷰어 자동할당 본인 제거

* setting : frontend 라벨링 제거

* Setting(design-system): 디자인 시스템 토큰 & icons 설정 (#20)

* setting: cn helper function 구현

* setting: .gitignore에 .tsbuildinfo 추가

* setting: cSpell pinback 및 turbo 추가

* setting: css 확장자 파일 tailwindcss로 인식되도록 설정

* setting: setting.json prettier & eslint 설정 추가

* setting: design-system styles token 설정

* setting: prettier/eslint root link

* setting: 기본 폴더구조 gitkeep 추가

* chore: font token 오타 수정

* setting: font-family base pretendard 설정

* setting: svg sprite default setting

* chore: code rabbit review 반영

* Setting(design-system): shadcn-ui default setting (#25)

* setting: shadcn/ui default setting

* chore: add pnpm-lock file

* refactor: code review 반영

* chore: update pnpm-lock file

* setting : 보일러플레이트 연동

* setting : tailwindCss 스타일 적용

* setting : custom.d.ts 타입 구체화

* setting : contents 스크립트 범위 수정

* setting : 폰트 1rem 기준 변경

* setting : extension 확장자 프로그램 초기 세팅 (#28)

* setting : 보일러플레이트 연동

* setting : tailwindCss 스타일 적용

* setting : custom.d.ts 타입 구체화

* setting : contents 스크립트 범위 수정

* setting : 폰트 1rem 기준 변경

---------

Co-authored-by: 이재림 <jellyme@ijaelim-ui-MacBookAir.local>

* !HOTFIX(design-system): tailwind ds @source 추가 (#40)

* Feature(extension): 토글 스위치 제작 (#39)

* feat: 토글 스위치 제작

* chore: 안쓰는 코드 제거

* feat: 사이즈 분기 처리 추가

* feat: 토글 thumb 디테일 수정

* chore: 안쓰는 코드 제거

* feat: 의존성, duration 수정

* chore:  design-system 패키지 export utils추가

* chore:안쓰는 코드 제거

* Feature(extension): 텍스트 필드 팝업 구현 (#32)

* chore: 파일구조 세팅

* feat: ui 구현

* feat: 업데이트된 폰트 적용

* feat: 오버레이 추가

* feat: 삭제, 수정 팝업 분기처리 추가

* feat: x버튼 아이콘 추가

* chore: 안쓰는 코드 제거

* feat:  오버레이-> 박스 섀도우로 변경

* chore: 안쓰는 코드 제거

* feat: onCancel 옵셔널처리

* Feature(extension): textarea 컴포넌트 구현 (#35)

* feat: textarea 구현

* feat: 스크롤바 디자인 커스텀

* chore: 안쓰는 코드 정리

* feat: 상대경로 절대경로로 변경

* feat: 글자수 제한 매직넘버 상수 정의

* feat: 유틸 경로 변경

* chore: 안쓰는 코드 삭제

* chore: 안쓰는 코드 제거

* feat: 유틸 경로 변경

* Feature(extension): 인포 박스 컴포넌트 제작 (#30)

* feat : 인포 박스 컴포넌트 제작

* feat : ver->union 타입으로 수정

* fix : package.json 불필요 삭제

* fix : app.tsx 수정 사항 제거

* feat : 분기 옵션 수정

* Feature(design-system): Input 컴포넌트 구현 (#52)

* feat: input 공통 컴포넌트 구현

* chore: svg.d.ts 파일 추가

* fix: error icon 오타 수정

* feat: aria-invalid 추가

* fix: design-system 내 절대 경로 추가 & tsconfig react.json으로 수정

* feat: input ref props 설정

* fix: build error 해결

* feat: input helperText error400 color로 변경

* Feature(client) useFunnel custom hook 구현 (#54)

* setting: onBoarding 기본 구조 세팅

* chore: 코드 prettier 정리

* feat: useFunnel hook 구현

* fix: build error 해결

* chore: 불필요한 gitkeep 파일 삭제

* chore: tsconfig 경로 설정 수정

* refactor: Funnel 컴포넌트 fallback null 처리 추가

* Feature(design-system): dot-indicator 공통 컴포넌트 구현 (#57)

* feat: dot-indicator 구현

* feat: input font token 미적용 이슈로 인한 tailwind 자체 value 사용

* Feature(client): dashboard-components (#43)

* refactor: 코드 리뷰 피드백 반영 및 툴팁 간격 조정

- 레벨 데이터 중복 제거 (공통 상수로 분리)
- 빈 index.ts 파일들에 실제 export 추가
- SectionTitle variant prop 구현
- Tailwind CSS 동적 클래스명 문제 해결
- 정보 아이콘과 툴팁 간격 8px로 조정
- 빌드 테스트 성공

* refactor: 대시보드 컴포넌트 리팩토링 및 코드 중복 제거

* fix: CodeRabbit 리뷰 피드백 반영

- useDashboard 훅 useCallback 의존성 배열 최적화
- AllViewButtonProps에서 사용하지 않는 children prop 제거
- styleUtils.ts CARD_CLASSES 중복 선언 문제 해결
- SectionTitle 컴포넌트 성능 최적화 및 CSS 클래스 일관성 개선
- React.memo 적용으로 불필요한 재렌더링 방지
- BookmarkCategory 컴포넌트 스타일 개선

* fix: CodeRabbit 리뷰 피드백 반영

* fix: CodeRabbit 리뷰 피드백 최종 반영

- 텍스트 색상 일관성 개선 (text-gray700 → text-gray800)
- React import 추가로 타입 오류 해결
- BookmarkCategory 완전 리팩토링 (styleUtils 함수 적용)
- 카테고리 스타일 유틸리티 함수 확장
- 인라인 스타일 제거로 코드 일관성 향상

* style: 코드 주석 제거 및 정리

* chore: .cursorrules를 gitignore에 추가

* fix: Git 추적에서 .cursorrules 파일 제거

* refactor: React.FC 제거 및 성능 최적화, 주석 정리

* fix: tailwindcss 중복 import 제거

* refactor: BookmarkSection 절대경로 적용 및 타입 정리

* refactor: Category 타입 분리 및 타입 시스템 개선

* refactor: Tooltip children prop required로 변경 및 사용처 수정

* refactor: 절대경로(@routes) 추가 및 관련 import 정리

* refactor: 절대경로 import 일괄 적용 및 경로 오류 수정

* refactor: 상대경로를 절대경로로 변경하고 React import 최적화

* feat: cva 라이브러리를 사용하여 variant 처리 개선

* refactor: tooltip 메시지를 constants 폴더로 분리

* refactor: Tooltip 컴포넌트의 CSS 변수를 Tailwind 클래스로 변경

* refactor: ThumbnailProps의 alt를 optional로 변경

* refactor: Thumbnail 컴포넌트에 cn 유틸리티 적용

* refactor: Props 타입을 컴포넌트 바로 위에 정의하는 컨벤션 적용

* refactor: DailyReminderCard 헤더 레이아웃을 absolute에서 flexbox로 개선

* refactor: 대시보드 컴포넌트 구조 정리 - features/dashboard 폴더를 pages/dashboard/components로 이동

* refactor: features 폴더 구조 개선 - dashboard 하위 폴더 제거

* refactor: CVA 라이브러리 적용으로 컴포넌트 스타일링 개선

* refactor: 인라인 스타일을 CVA로 변경 - 고정된 maxWidth 값 처리

* chore: 사용하지 않는 LAYOUT import 제거 (ts 경고 해결)

* style: 리마인드 카드 패딩 및 스타일링 개선

* style: 카드 테두리 색상을 border-gray-200으로 통일

* style: 리마인드 카드 메모 텍스트 스타일 통일 및 디자인 토큰 적용

* refactor: 컴포넌트 구조 개선 및 디자인 시스템 통합

* test: 북마크 카드 테스트 케이스 추가 및 타이틀 스타일 개선

* Feature(client): onBoarding carousel및 story step 컴포넌트 구현 (#60)

* feat: 온보딩 캐러셀 구현

* feat: funnel story step 구현

* feat: onBoarding funnel 구조 세팅 및 story step 추가

* refactor: story content 상수 분리

* refactor: story constants key에서 title로 변경

* refactor: STEP 상수 constants 폴더로 분리

* refactor: useFunnel initial step props 받도록 변경

* refactor: onboarding image alt 및 aria-label 추가

* refactor: story image depth onBoarding 추가하도록 변경

* Feature(extension): 카테고리 드롭다운 컴포넌트 구현 (#36)

* feat : 드롭다운 퍼블리싱

* feat : hover 적용 처리

* feat : 아이콘 적용

* feat: 아이콘 적용 및 드롭다운 수정

* fix : 폰트 적용 수정

* feat : 클릭 후 텍스트 반영

* feat : 예시 카테고리 추가

* feat : 배열 유효성 체크 및 인덱스 고유값

* feat : 토글 아이콘 교체

* feat : medium,large 분기 처리

* feat : 다운 아이콘 추가

* feat : 아이콘 애니메이션 제거

* fix : app.tsx 수정사항 머지 충돌 해결

* feat : 아이콘 파일 수정 및 정상 적용

* feat: 아이콘 애니메이션 적용

* fix : app.tsx 상태 리버트

* fix : 아이콘 중복 이미지 삭제 및 수정

* fix: package.json 중복 제거

* feat : medium width 값 수정

* fix : console 제거

* fix : 변수 명칭 구체화 수정

* Feature(extension): 데이트 피커 컴포넌트 (#59)

* feat : 인풋 컴포넌트 퍼블리싱

* feat : 공통화 후 분기 로직 분리

* feat : 시간 및 날짜 포맷팅 로직

* feat : 유효성 검사

* feat : 백스페이스 시 포맷팅 제거

* feat : 시간 유효성 추가

* feat : 수정

* refactor : 타입 파일 분리

* refactor : utils 파일로 리팩토링

* fix : app.tsx 초기 상태로 돌리기

* fix: 디자인시스템 package.json 모듈 타입 지정

* fix: 인터페이스 선언 위치 수정

* feat : constants로 상수 분리

* feat : constants 경로 지정

* Refactor(client): 파일 구조 변경 및 디자인 통일 (#65)

* refactor: 대시보드 전용 컴포넌트 pages/dashboard/components로 이동 및 구조 정리

- Thumbnail, LevelInfoModal, Tooltip 대시보드 전용 폴더로 이동
- 관련 import 경로 일괄 수정
- shared/ui에서 export 및 파일 제거
- 구조 명확화로 유지보수성 향상

* refactor: 파일 구조 및 스타일 수정

* refactor: 상대경로를 절대경로로 변경하여 코드 가독성 향상

* Feat(client): SVG 파일 경로 수정 및 EmptyState 텍스트 색상 변경

* chore: public 폴더 재생성 및 vite.svg 복구

* refactor: 경로 변경

* Feature(design-systems): 공통 버튼 컴포넌트 (#63)

* feat: 버튼 틀 공컴 연결

* Revert "feat: 버튼 틀 공컴 연결"

This reverts commit 386d30c.

* feat : 공통 버튼 공컴화 제작

* fix: 디자인 시스템 아이콘 상대경로로 수정

* feat : 사이즈에 따른 버튼 분기 cva

* feat : 색상 분기

* feat : props 옵셔널 정의 제거

* feat : app.tsx 기본 형태로 돌리기

* [Refactor] 팝업 관련 컴포넌트 packages 공컴으로 마이그레이션 작업 (#68)

* feat: 파일들 마이그레이션

* feat : index.ts 재정의

* fix: app.tsx 초기 상태로 수정

* Feature(extension): fcm token 발급 (#71)

* feat: 파이어베이스 설치, 파일세팅

* feat: 환경변수 설정

* feat: env jason 설정

* feat: settingFCM 임포트

* feat: 수정중

* Revert "feat: 수정중"

This reverts commit d123699.

* feat: 필요한 파일 추가, .js->.ts 변경과 설정 변경

* feat: FCM SDK로 토큰 생성 성공

* feat:  환경변수 설정

* fix: 충돌 해결

* fix: build error fix

* !HOTFIX: secret working

* Feature(client): dashboard card view 구현 (#72)

* feat: 북마크 카드 세션 구현

- 카테고리 필터링 기능 (BookmarkCategory)
- 총 카드 개수 표시 (BookmarkCount)
- 카드 그리드 4열 고정
- EmptyState 표시
- 전체보기 버튼 조건부 렌더링 (12개 이상)

* feat: 데일리 리마인드 카드 세션 구현

- 타이머 기능 포함 (Timer 컴포넌트)
- 카드 그리드 4열 고정
- 전체보기 버튼 조건부 렌더링 (12개 이상)
- 토글 기능으로 확장/축소 가능
- 도토리 스탬프 표시 기능

* feat: 대시보드 레이아웃 및 스타일링 구현

- 최대 너비 1440px, 양쪽 패딩 설정
- 헤더 fixed 포지션, 높이 6rem
- 세션 간 간격 140px, 카드 그리드 4열 고정
- 하단 패딩 36px 추가

* feat: 대시보드 데이터 및 훅 구현

- 북마크 카드 목데이터 추가 (카테고리, 읽음 상태 포함)
- useDashboard 훅에서 불필요한 import 제거
- 카테고리 필터링 및 전체보기 기능 지원

* feat: API 및 타입 정의 추가

- timerApi 함수 추가
- EmptyStatePresets 업데이트
- API 타입 정의 추가

* fix: 린트 에러 수정 및 포맷팅 적용

* fix: BookmarkCardProps의 categoryId를 필수 속성으로 변경

* fix: 코드래빗 AI 봇 피드백 반영

- 안 읽은 정보 카테고리 count 값 수정 (3 → 4)
- API limit 값 일관성 수정 (9 → 12)
- 인터페이스 일관성 개선 (BaseCard 공통 인터페이스 추가)
- 디자인 시스템 단위 일관성 수정 (px → rem)

* feat: 북마크 카드 mock 데이터 주석 해제 및 대시보드 카테고리/EmptyState 로직 개선

* refactor: 카테고리 자동 생성 로직 함수화 및 불필요한 주석 제거

* fix: lint/prettier 에러 수정 및 코드 스타일 통일

* refactor: px 단위를 rem으로 통일하여 접근성 및 일관성 개선

* refactor: 중첩 삼항 연산자를 early return 패턴으로 개선하여 가독성 향상

* refactor: 조건에 의미있는 네이밍 추가 및 some 메서드 활용으로 가독성 및 성능 개선

* style: prettier 포맷팅 및 린트 자동 수정

* refactor: setIsExpanded 토글 시 prev 패턴 적용으로 상태 변경 안전성 향상

* Feature(client): onboarding time select step 컴포넌트 구현 (#73)

* feat: timeSelect Step 기본 퍼블리싱 구현

* refactor: 버튼 역할 div button 태그로 변경

* feat: onBoarding header 연결

* feat: 리마인드 시간 아침/저녁 선택 로직 구현

* feat: max notice step & welcome step Funnel에 추가

* feat: useFunnel funnel내에서 boolean 분기처리 되도록 변경

* feat: mac window 구분용 useOSDetector 구현

* feat: 사용자 설정 dropdown open 로직 구현

* feat: header fixed로 변경

* Feature(extension): 익스텐션 팝업 컴포넌트 및 뷰 (#75)

* fix: build 에러 상대 경로 수정

* feat : 익스텐션 medium 팝업 퍼블리싱

* feat: TimePicker에 사이즈 분기 처리

* feat: 팝업 레이아웃 수정

* feat: 팝업 헤더 분리 컴포넌트 연결

* feat: 토글 시 absolutea 처리

* feat: textarea 폰트 적용 및 placeholder

* feat: 얼럿 메시지 -> 빨간 텍스트로 노출

* feat: 크롬 확장프로그램 디폴트 화면 세팅

* fix: 불필요 props 선언 제거

* feat: constants 상수 분리

* fix: firebase관련 세팅 삭제

* feat: 크롬 익스텐션 창 닫기

* feat: arial-label 추가

* feat: 아이콘 빌드파일

* Feature(client): onboarding mac notice & welcome step 컴포넌트 구현 (#77)

* feat: mac notice step 구현

* feat: onboarding welcome step 컴포넌트 구현

* feat: main logo 수정

* chore: mac 분기처리 주석 제거

* refactor: img aria-label 중복 제거

* Feature(client): 대시보드용 팝업 컴포넌트 작업 및 플로우 연결 (#80)

* feat: 대시보드 팝업 컴포넌트 퍼블리싱

* feat: 대시보드 팝업 토글 제어 props 연결

* feat: 카드 더보기 버튼 클릭 시, 팝업 핸들링

* feat: 모달 속 버튼 클릭시 닫기 제어

* feat: 북마크 카드 쪽 팝업닫기 제어

* feat: 북마크 카드 props 타입 통일

* feat: 팝업 바깥영역 클릭시 닫기 제어

* fix: 하드코딩 구간 표시

* fix: props에 옵셔널 조건 통일

* feat: 불필요 에러 메시지 함수 제거 및 팝업 상수 정의

* Setting(client): api 세팅 및 초기 토큰 발급 작업  (#88)

* feat: api 연결 세팅 및 토큰 재발급 플로우

* fix: 콘솔 제거

* feat: 익스텐션 쪽 api 연결 확인

* feat: client api 세팅 주석

* Feature(client): design FCM token management & messaging architecture (#90)

* feat: service worker register function 구현

* feat: background firebase messaging logic 구현

* setting: firebase sdk default setting

* feat: FCM token 발급 test function 구현

* feat: extension google email get method 연결

* feat: 자동화 스크립트 추가 및 코드 품질 개선

- package.json에 format, lint:fix, fix-all, check-all 스크립트 추가
- 린트 및 포맷팅 자동 수정 기능 구현
- ModalPop 컴포넌트에서 불필요한 setErrorMessage prop 제거
- 코드 품질 개선 및 설정 파일 업데이트

* Feature(client): 대시보드 배너 구현 및 개발 워크플로우 개선 (#84)

* feat: 대시보드 배너 및 타이머 기능 임시 저장

* feat: 배너 컴포넌트 및 관련 리소스 추가

* refactor: 배너 컴포넌트 분리 및 모달 위치 개선

* refactor: 배너 컴포넌트 UI 개선 및 중앙 정렬 적용

* feat: 카드 컴포넌트 UI 개선 - 썸네일 크기 통일 및 제목 말줄임 처리

* refactor: 배너 컴포넌트 코드 품질 개선 및 기능 최적화

* refactor: 배너 컴포넌트 폴더 구조 개선 및 코드 품질 향상

* fix: 리마인드 카드 썸네일 배경색 추가로 중앙 정렬 개선

* refactor: 배너 관련 파일 폴더 구조 개선

* cleanup: 중복 파일 제거 및 폴더 구조 정리

* fix: 썸네일 없을 때 배경 박스 제거 및 메모 텍스트 3줄 제한 강화

* fix: 린트 오류 해결

* fix: BannerHeadline 컴포넌트 수정

* fix: 헤더 로고 직접 import 방식으로 변경 및 스타일 수정

* refactor: 코드 품질 개선 및 최적화

- MAX_ACORN_COUNT 상수 추가로 마지막 단계 기준값 재사용 가능
- Timer 로직을 useTimer custom hook으로 분리
- 타입 추론 활용으로 제네릭 타입 제거
- API 응답에서 구조 분해 할당 사용
- 충돌하는 max-height 스타일 수정
- 조건부 스타일링 개선

* fix: TimePicker 컴포넌트에서 불필요한 setErrorMessage prop 제거

* chore: prettier 및 린트 포맷팅 반영

* Feature(client): 온보딩 ui 퍼블리싱 (#89)

* feat: 다이얼 ui 구현

* feat: border 커스텀

* feat: ui 커스텀

* feat: ui 완성

* feat: 사용자 설정 시간 저장

* chore: 안쓰는 코드 삭제

* feat: 온보딩 사용자 설정 추가

* fix : 버블링 해결

* feat: 시간 선택 포맷 개선 및 팝업 토글 함수 수정

* feat: 사용자 설정 시간 선택 기능 개선 및 팝업 상태 반영

* feat: 팝업 외부 클릭 감지 기능 추가 및 사용자 설정 시간 선택 UI 개선

* feat: 온보딩 레이아웃 정렬, 프리셋 시간 선택 기능 개선

* feat: 버튼 컴포넌트 제작

* feat: 온보딩 단계에서 네비게이션 버튼 컴포넌트 추가 및 시간 선택 버튼 개선

* feat: 사용자 설정 시간 선택 UX 수정

* feat: 시간 선택 기능 개선 및 포맷 유틸리티 추가+ 버튼 레이아웃

* feat: 팝업 z인덱스

* fix: ts env 추적 못하는 문제 해결

* chore: 필요 없는 함수 주석 처리

* refactor: color 하드 코딩된 값 수정

* chore: props type interface로 변경

* feat: mac user button 분기 처리 & 토큰 발급 로직 수정

* chore: prettier 정리..

* Setting(extension): 익스텐션 쪽 API 세팅 및 client랑 데이터 연결 작업 (#93)

* feat: api 연결 세팅 및 토큰 재발급 플로우

* fix: 콘솔 제거

* feat: 익스텐션 쪽 api 연결 확인

* feat: client api 세팅 주석

* feat: client->extension 토큰 전달 로직

* refactor: utils함수로 분리

* feat: 이메일 fetch 및 랜딩 연결

* fix: 콘솔 및 주석 제거

* Feature(extension): 북마크 저장 및 API 연결 작업 (#87)

* setting: 익스텐션 세팅 수정

* feat: 북마크 저장 로직 구현 연습

* feat: url에 따른 썸네일 추적

* feat: 북마크 저장 기능 구현

* feat: 북마크 저장 시 창 닫기

* feat: 이미지 연결

* feat: 북마크 정보 팝업에 연동

* feat: 텍스트 포맷 및 이미지 연동

* feat: 북마크 제대로 저장 수정

* fix: 주석 제거

* feat: 텍스트 없으면 익스텐션 실행 끄기

* fix: 콘솔 제거

* feat: 크롬 도메인일 경우에는 꺼지지 않게 제어

* feat: api 테스트 코드 제거

* fix: 대시보드 누락 사항 복구

* fix: 데이터 누락 추가

* Api(client): 회원 가입 api 연결 (#97)

* chore: signup 오타 수정

* feat: signup axios fetch 함수 구현

* feat: signup query 함수 구현

* api: signup api 연결

* feat: signup query onSuccess token localStorage 추가 로직 구현

* feat: next button step 변하는 로직 onSuccess로 이동

* feat: signup success시 localStorage 저장 및 send extension 로직 추가

* Feature(extension): modalpop의 카테고리 드롭다운에 popup 연결 (#102)

* feat: 버튼 공통 컴포넌트 연결

* feat: input 아이콘 컬러 변경

* feat: 버튼 비활성화

* feat: 추가하기 팝업 연결

* feat: 카테고리 수정하기 팝업 연결

* chore:  안쓰는 코드 삭제

* feat: 이벤트 전파방지 코드

* chore: 안쓰는 코드 제거

* Api(client): 대시보드 api 연결 (#101)

* feat: 대시보드 API 연동

# Conflicts:
#	apps/client/src/pages/dashboard/dashboard.tsx
#	apps/client/src/pages/onBoarding/OnBoarding.tsx

* refactor: api 타입 분리 및 import 경로 일괄 수정

# Conflicts:
#	apps/client/src/pages/dashboard/dashboard.tsx

* fix: 이메일 하드코딩 환경변수 처리 및 fallback 적용

# Conflicts:
#	apps/client/src/pages/dashboard/dashboard.tsx

* fix: 썸네일 캐싱 및 에러 처리 개선, 환경변수 적용

* refactor: 썸네일 API를 대시보드 전용 폴더로 이동 및 구조 정리

* refactor: 대시보드 코드 정리 및 최적화

- TestSection 컴포넌트 삭제
- 테스트 버튼 UI 제거
- useDashboard 훅에서 테스트 관련 함수 제거
- 도토리 개수 업데이트 로직 개선
- 배너 렌더링 디버깅 로그 추가
- 썸네일 화질 개선 (64px → 128px)
- api.allorigins.win 프록시 서비스 제거
- TypeScript 오류 수정

# Conflicts:
#	apps/client/src/pages/dashboard/dashboard.tsx

* fix: ESLint 오류 수정 및 코드 정리

- 빈 함수 오류 수정 (empty arrow function)
- non-null assertion 오류 수정
- 디버깅용 console.log 제거
- 코드 포맷팅 적용

* fix: 카드 더보기 버튼 이벤트 중첩 해결 및 토큰 관리 개선

- BookmarkCard, DailyReminderCard에서 더보기 버튼 클릭 시 e.stopPropagation() 추가
- DailyReminderCard 스탬프 오버레이에 pointer-events-none 추가
- Dashboard 모달 팝업 시 스크롤 고정 로직 추가
- axiosInstance에서 하드코딩된 토큰 발급 로직 제거
- 온보딩에서 저장된 토큰만 사용하도록 수정
- 대시보드에서 토큰이 없으면 온보딩으로 리다이렉트하는 로직 추가

* fix: 대시보드 토큰 통합 및 이벤트 처리 문제 수정

* fix: 콘솔 로그 및 errorUtils 파일 제거 및 개발자 테스트용 API 및 관련 코드 제거

* fix: api 폴더에서 썸네일 유틸리티를 utils 폴더로 분리

* fix: TypeScript 에러 수정

* fix: 주석 정리 및 간소화

* fix: axiosInstance에서 온보딩 토큰 사용하도록 수정

* fix: 주석 지우기

* fix: 주석 정리

* refactor: API 호출 함수 정리 및 썸네일 유틸리티 분리

- API 호출 함수에서 불필요한 try/catch 및 콘솔 로그 제거
- 썸네일 관련 유틸리티를 apis에서 utils로 분리
- Thumbnail 컴포넌트 import 경로 수정
- useAcornCount 훅에서 불필요한 try/catch 제거

* refactor: AxiosResponse 타입 활용 및 코드 개선

- API 함수들이 AxiosResponse 타입을 반환하도록 수정
- 훅들에서 response.data로 접근하도록 수정
- 사용하지 않는 AxiosError import 제거
- 썸네일 유틸리티에서 불필요한 Content-Type 헤더 제거

* Feature(client): dashboard popup에 추가 popup 연결 (#105)

* feat: client dashboard modalpop에 추가 popup 연결

* feat: popup overlay z-index 임시 수정

* feat: popup overlay 적용

* feat: url title & location truncate 처리

* Feature(onboarding): story 변경사항 반영 (#113)

* feat: 스토리 변경+헤더 프롭스 변경

* feat: 디테일 반영

* chore: 프리티어 적용

* Feature(landing): UI 구현과 로띠 적용 (#98)

* Feature(landing): 홈 페이지 섹션 구성 요소 추가

* feat: landing 로띠 추가

* feat: 스크롤 효과 적용

* feat: 로띠 루프 설정

* feat: 로띠 추가

* feat: 버튼 컴포넌트 추가

* chore: 스타일 수정

* feat: 코드리뷰 반영

* feat: 텍스트 추가

* feat: 최종 수정 반영

* fix: 이름 자동 생성 오류 변경

* chore: 프리티어 수정

* Api(extension): 익스텐션 북마크 아티클 저장 API 연결 (#107)

* feat: 크롬 스토리지에 저장

* feat: 모달 레이아웃 조정 및 색 적용

* feat: 카테고리 조회 및 가져오기

* fix: 드롭다운 초기값 설정

* feat: 포맷 함수+리마인드 get 연결

* feat: 북마크 아티클 api연결

* fix: 불필요 api 제거

* feat: 포맷 시간 추가

* fix: 콘솔 제거

---------

Co-authored-by: jjangminii <wjdals030328@catholic.ac.kr>

* Api (extension): 익스텐션 팝업 수정/삭제/생성 Edit 기능 API 연결 (#114)

* feat: 크롬 스토리지에 저장

* feat: 모달 레이아웃 조정 및 색 적용

* feat: 카테고리 조회 및 가져오기

* fix: 드롭다운 초기값 설정

* feat: 포맷 함수+리마인드 get 연결

* feat: 북마크 아티클 api연결

* fix: 불필요 api 제거

* feat: 포맷 시간 추가

* fix: 콘솔 제거

* feat: 카테고리 생성 api

* feat: 카테고리 수정 api

* fix: 에러 던지기

* fix: 에러 얼럿 추가

* feat: 카테고리 삭제 api 연결

* feat: 토큰 연결 및 받아오기

* fix: 콘솔제거

* fix: lint 수정

---------

Co-authored-by: jjangminii <wjdals030328@catholic.ac.kr>

* Api(client) 대쉬보드 팝업 상세 조회/카테고리 조회/삭제 API 연결 (#115)

* api: dashboard axios fetch 함수 구현

* api: dashboard query 함수 구현

* chore: query import 경로 수정

* chore: 불필요한 파일 삭제

* api: dashboard api 연결

* feat: og proxy 우회 로직 구현

* chore: 필요없는 코드 제거

* chore: pnpm lock sync

* !HOTFIX: infoBox edit

* Refactor(client): 1차 개발 QA (#118)

* chore: level modal scroll bar 덜그럭 문제 해결

* feat: 아티클 삭제 query invalidate 적용

* Refactor: 개발 2차 QA 작업 (#119)

* feat: 대시보드 카테고리 수정 및 삭제 api

* feat: 토글 버튼 zindex 제어

* feat: 아티클 수정 patch api 연결

* feat: 카테고리 생성하기

* fix: lint 수정

* fix: lint dpfj

* feat: 저장 시 모달 닫기

* fix: err lint 수정

* feat: 시간 포맷팅 수정

* feat: 모달 닫기 로직

---------

Co-authored-by: constantly-dev <rntqhdl10@naver.com>
Co-authored-by: karnelll <165611407+karnelll@users.noreply.github.com>
Co-authored-by: KIM JEONGMIN <163228804+jjangminii@users.noreply.github.com>
Co-authored-by: 이재림 <jellyme@ijaelim-ui-MacBookAir.local>
Co-authored-by: SEOHEE CHOI <itskarnel@kyonggi.ac.kr>
Co-authored-by: jjangminii <wjdals030328@catholic.ac.kr>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 자동화 스크립트 추가 및 코드 품질 개선

4 participants