Skip to content

[FE] 대시보드 편집창 - 메뉴 분석 지표카드 추가#277

Merged
lee0jae330 merged 45 commits intodevelopfrom
feature/#262-fe-dashboard-menu-card
Feb 18, 2026
Merged

[FE] 대시보드 편집창 - 메뉴 분석 지표카드 추가#277
lee0jae330 merged 45 commits intodevelopfrom
feature/#262-fe-dashboard-menu-card

Conversation

@mskwon02
Copy link
Collaborator

@mskwon02 mskwon02 commented Feb 15, 2026

#️⃣ 변경 사항

이번 PR에서는 대시보드의 메뉴 분석 카드 컴포넌트들을 구현했습니다.

  • 메뉴 분석 카드 컴포넌트 신규 구현: 메뉴 매출 랭킹, 식재료 소진량 랭킹, 시간대별 인기 메뉴, 인기 메뉴 조합 카드의 콘텐츠 컴포넌트 구현
  • 공통 랭킹 UI 시스템 구축: 메뉴와 식재료 랭킹에서 공통으로 사용하는 DashboardRankingContentRankItem 컴포넌트 추상화
  • 데이터 모델(DTO) 최신화: MNU_03 (시간대별 메뉴 주문건수), MNU_05 (인기 메뉴 조합)의 경우 SSE 연동 및 대시보드 전용으로 변경된 백엔드 DTO 구조에 맞춰 타입 정의 및 유틸리티 함수 업데이트

#️⃣ 작업 상세 내용

  • 대시보드 메뉴 분석 카드 콘텐츠 구현
    • MenuSalesRankingCardContent: 매출액 기준 상위 메뉴 랭킹 표시 (최대 4개)
    • IngredientUsageRankingCardContent: 식재료 소진량 랭킹 표시 (최대 4개)
    • TimeSlotMenuOrderCountCardContent: 특정 시간대에 주문이 집중되는 메뉴 정보 표시
    • PopularMenuCombinationCardContent: 함께 주문되는 빈도가 높은 메뉴 조합 정보 표시
  • 공통 컴포넌트 및 UI 개선
    • DashboardRankingContent: 테이블 기반의 랭킹 레이아웃 공통화 및 스크린 리더 지원(sr-only)
    • IngredientUnregisteredContent: 식재료 사용량 랭킹 카드에서 식재료 데이터가 없는 경우의 안내 UI 구현
    • RankBadge: 중간 사이즈인 smmd(size-7) 속성 추가

추가 설명

  1. MNU_03 (시간대별 메뉴 주문건수), MNU_05 (인기 메뉴 조합)의 경우 상세분석과 대시보드에서의 response DTO 다름. 백엔드랑 합의 완

    response DTO 자세히 보기

    📊 MNU_03 – 시간대별 메뉴 주문건수

    1️⃣ DetailTimeSlotMenuOrderCountResponse
    : 상세분석에서 response

    ✅ 샘플 Response

    {
      "items": [
        {
          "timeSlot2H": 10,
          "totalOrderCount": 50,
          "menus": [
            { "menuName": "불고기 버거", "orderCount": 30 },
            { "menuName": "치즈버거", "orderCount": 20 }
          ]
        }
      ]
    }
    

    2️⃣ DashboardTimeSlotMenuOrderCountResponse
    : 대시보드에서 sse response

    ✅ 샘플 Response

    {
      "timeSlot2H": 10,
      "menuName": "불고기 버거"
    }
    

    📊 MNU_05 – 인기 메뉴 조합

    3️⃣ DetailPopularMenuCombinationResponse
    : 상세분석에서 response

    ✅ 샘플 Response

    {
      "items": [
        {
          "baseMenuName": "불고기 버거",
          "pairedMenus": [
            { "menuName": "감자튀김", "count": 80 },
            { "menuName": "콜라", "count": 70 }
          ]
        }
      ]
    }
    

    4️⃣ DashboardPopularMenuCombinationResponse
    : 대시보드에서 sse response

    ✅ 샘플 Response

    {
      "firstMenuName": "불고기 버거",
      "secondMenuName": "감자튀김"
    }
    
  2. MNU_01 (메뉴별 매출 랭킹), MNU_04 (식재료 소진량) 의 경우 겹치는 UI가 있어 공통 컴포넌트(DashboardRankingContent.tsx) 사용

    스크린샷 2026-02-16 오전 6 00 45 IngredientUsageRankingCardContent.tsx와 MenuSalesRankingCardContent.tsx는 components/menu/dashboard-menu-ranking 폴더 내에 함께 위치
  3. MNU_01 (메뉴별 매출 랭킹), MNU_04 (식재료 소진량)의 경우 response DTO로 받아온 데이터를 RankItem (랭킹 목록에서 한 행에 해당) 컴포넌트에서 사용할 수 있는 데이터로 변환하는 함수 존재

    • RankItem(랭킹 한 행)에서 사용하는 데이터 구조
        export interface DashboardRankItem {
          rank: number;
          itemName: string;
          totalAmount: number;
          unit: '원' | IngredientUnit;
        }
      
    • MNU_01 (메뉴별 매출 랭킹) 에서 dto -> DashboardRankItem 변환 함수
      • getDashboardMenuRankItems
    • MNU_04 (식재료 소진량) 에서 dto -> DashboardRankItem 변환 함수
      • getDashboardIngredientRankItems
        • 식재료의 경우 g, kg, ml, L와 같이 규모가 다른 단위 섞여 있음
          -> kg의 경우 1000g으로, L의 경우 1000ml로 변환 후 소진량 비교해 정렬
          const sortedItems = [...items].sort((a, b) => {
             const aQuantity =
               a.baseUnit === 'kg' || a.baseUnit === 'L'
                 ? a.totalQuantity * 1000
                 : a.totalQuantity;
             const bQuantity =
               b.baseUnit === 'kg' || b.baseUnit === 'L'
                 ? b.totalQuantity * 1000
                 : b.totalQuantity;
             return bQuantity - aQuantity;
           });
          

#️⃣ 관련 이슈

📸 스크린샷 (선택)

2026-02-15.11.24.25.mov

mskwon02 and others added 30 commits February 13, 2026 10:04
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

대시보드 편집 화면에 메뉴 분석 관련 지표 카드를 추가하는 변경 사항입니다. 새로운 컴포넌트와 타입, 상수가 다수 추가되었으며, 일부 공통 컴포넌트가 수정되었습니다. 리뷰에서는 주로 타입 정의의 명확성, 네이밍 컨벤션, 그리고 오타 수정과 같은 코드의 일관성 및 유지보수성에 대한 개선점을 제시했습니다.

@mskwon02 mskwon02 self-assigned this Feb 15, 2026
Copy link
Collaborator

@lee0jae330 lee0jae330 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 ~ cdn assets 이름 반영해주시면 감사하겠습니다 !
또 리뷰 반영해주시면 감사하겠습니다 !

Copy link
Collaborator

Choose a reason for hiding this comment

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

p2: 오우 제 형식이랑 맞추어주셨군요 감사합니다 !

Comment on lines 44 to 51
interface IngredientUsageRankingCardContentProps extends GetIngredientUsageRankingResponseDto {
className?: string;
}

export const IngredientUsageRankingCardContent = ({
hasIngredient,
items,
}: IngredientUsageRankingCardContentProps) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3: 여기도 dto 누락되었습니다 !

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

dto 가 누락되었다는게 className이 누락되었다는 말씀이신가요??

Copy link
Collaborator

Choose a reason for hiding this comment

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

아 className입니다 ㅠㅠ

Comment on lines 35 to 42
interface MenuSalesRankingCardContentProps extends GetMenuSalesRankingResponseDto {
className?: string;
}

export const MenuSalesRankingCardContent = ({
items,
}: MenuSalesRankingCardContentProps) => {
// dto -> 대시보드의 메뉴>매출 랭킹 카드 UI 데이터 형태로 변환
Copy link
Collaborator

Choose a reason for hiding this comment

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

p3: 여기도 누락되었습니다 !

height:
Math.max(EDIT_CARD_WRAPPER.MIN_HEIGHT, computedCardHeight) * sizeY +
GRID_GAP * (sizeY - 1), // 최소 높이 147px, gap 20px
height: Math.max(EDIT_CARD_WRAPPER.MIN_HEIGHT, computedCardHeight), // 최소 높이 147px,
Copy link
Collaborator

Choose a reason for hiding this comment

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

[p2] 지금 식재료소진량 카드는 computedCardHeight가 다른 것보다 큰 것 같은데 확인 부탁 드립니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

랭킹 테이블 자체 위아래 여백이 있어서 발생한 문제였습니다. 테이블 자체 위아래 여백 제거했습니다!

<div
style={{ marginTop: `${EDIT_CARD_WRAPPER.HEADER_MAIN_GAP}px` }}
className="flex min-w-0 flex-1 items-end justify-center"
className={cn(
Copy link
Collaborator

Choose a reason for hiding this comment

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

[p3] 메뉴 판매 패턴, 인기 메뉴 조합 지표가 justify-center 때문에 중앙 정렬이 된 것 같은데 디자인 상 아래로 정렬되어야 해요. 확인 부탁드립니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

justify-center는 가로 가운데 정렬 용이어서 관련이 없는 부분입니다!
메뉴 판매 패턴, 인기 메뉴 조합 지표의 문구가 아래가 아닌 위에 뜨는 문제는 메뉴 판매 패턴, 인기 메뉴 조합 지표 컨텐츠의 height를 원래 텍스트 높이보다 더 크게 주어서 윗 부분 여백 생기게 하는 방식으로 해결했습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

2026-02-18.11.55.19.mov

Copy link
Collaborator

@lwjmcn lwjmcn left a comment

Choose a reason for hiding this comment

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

dto가 정말 많으네요 . . .
백엔드 분들이랑 협의 하시느라 고생 많으셨어요.
공통 컴포넌트도 굿굿입니다!

@lee0jae330 lee0jae330 merged commit 0cf5974 into develop Feb 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FE] 3-1-7. 대시보드 메뉴 관련 지표카드 UI

3 participants