diff --git a/frontend/src/components/dashboard/dashboard-edit/EditCardContent.tsx b/frontend/src/components/dashboard/dashboard-edit/EditCardContent.tsx
index 8e52d0605..eef9343ab 100644
--- a/frontend/src/components/dashboard/dashboard-edit/EditCardContent.tsx
+++ b/frontend/src/components/dashboard/dashboard-edit/EditCardContent.tsx
@@ -1,3 +1,9 @@
+import {
+ IngredientUsageRankingCardContent,
+ MenuSalesRankingCardContent,
+ PopularMenuCombinationCardContent,
+ TimeSlotMenuOrderCountCardContent,
+} from '@/components/menu';
import {
AveragePriceContent,
OrderCountContent,
@@ -10,6 +16,12 @@ import {
SalesTypeContent,
} from '@/components/sales';
import type { MetricCardCode } from '@/constants/dashboard';
+import {
+ INGREDIENT_USAGE_RANKING,
+ MENU_SALES_RANKING,
+ ORDER_COUNT as MENU_ORDER_COUNT,
+ POPULAR_MENU_COMBINATION,
+} from '@/constants/menu';
import {
AVERAGE_PRICE,
ORDER_COUNT,
@@ -66,6 +78,12 @@ const {
EXAMPLE_IS_SIGNIFICANT: SALES_BY_DAY_EXAMPLE_IS_SIGNIFICANT,
} = SALES_BY_DAY;
const { EXAMPLE_DATA: SALES_TREND_EXAMPLE_DATA } = SALES_TREND;
+const { EXAMPLE_HAS_INGREDIENT, EXAMPLE_INGREDIENT_USAGE_RANKING_ITEMS } =
+ INGREDIENT_USAGE_RANKING;
+const { EXAMPLE_MENU_SALES_RANKING_ITEMS } = MENU_SALES_RANKING;
+const { EXAMPLE_TIME_SLOT_2H, EXAMPLE_MENU_NAME } = MENU_ORDER_COUNT;
+const { EXAMPLE_FIRST_MENU_NAME, EXAMPLE_SECOND_MENU_NAME } =
+ POPULAR_MENU_COMBINATION;
export const EditCardContent = ({ cardCode }: EditCardContentProps) => {
switch (cardCode) {
@@ -163,6 +181,33 @@ export const EditCardContent = ({ cardCode }: EditCardContentProps) => {
isSignificant={SALES_BY_DAY_EXAMPLE_IS_SIGNIFICANT}
/>
);
+ case 'MNU_01_01':
+ case 'MNU_01_04':
+ case 'MNU_01_05':
+ return (
+
+ );
+ case 'MNU_03_01':
+ return (
+
+ );
+ case 'MNU_04_01':
+ return (
+
+ );
+ case 'MNU_05_04':
+ return (
+
+ );
default:
return null;
}
diff --git a/frontend/src/components/menu/dashboard-menu-combination/PopularMenuCombinationCardContent.tsx b/frontend/src/components/menu/dashboard-menu-combination/PopularMenuCombinationCardContent.tsx
new file mode 100644
index 000000000..2e36f77cb
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-combination/PopularMenuCombinationCardContent.tsx
@@ -0,0 +1,24 @@
+import { POPULAR_MENU_COMBINATION } from '@/constants/menu';
+import type { GetDashboardPopularMenuCombinationResponseDto } from '@/types/menu';
+
+import { PopularMenuCombinationContent } from './PopularMenuCombinationContent';
+
+const { EXAMPLE_FIRST_MENU_NAME, EXAMPLE_SECOND_MENU_NAME } =
+ POPULAR_MENU_COMBINATION;
+interface PopularMenuCombinationCardContentProps extends GetDashboardPopularMenuCombinationResponseDto {
+ className?: string;
+}
+
+export const PopularMenuCombinationCardContent = ({
+ firstMenuName = EXAMPLE_FIRST_MENU_NAME,
+ secondMenuName = EXAMPLE_SECOND_MENU_NAME,
+ className,
+}: PopularMenuCombinationCardContentProps) => {
+ return (
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-combination/PopularMenuCombinationContent.tsx b/frontend/src/components/menu/dashboard-menu-combination/PopularMenuCombinationContent.tsx
new file mode 100644
index 000000000..9b27e2e0f
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-combination/PopularMenuCombinationContent.tsx
@@ -0,0 +1,33 @@
+import { cn } from '@/utils/shared';
+
+interface PopularMenuCombinationContentProps {
+ className?: string;
+ baseMenuName?: string;
+ pairedMenu?: string;
+}
+
+export const PopularMenuCombinationContent = ({
+ className,
+ baseMenuName,
+ pairedMenu,
+}: PopularMenuCombinationContentProps) => {
+ return (
+
+ 최고 인기 조합은
+
+ {baseMenuName}
+
+
+
+ &{pairedMenu}
+
+ 입니다
+
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-combination/index.ts b/frontend/src/components/menu/dashboard-menu-combination/index.ts
new file mode 100644
index 000000000..1f9a4e98b
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-combination/index.ts
@@ -0,0 +1 @@
+export { PopularMenuCombinationCardContent } from './PopularMenuCombinationCardContent';
diff --git a/frontend/src/components/menu/dashboard-menu-order/TimeSlotMenuOrderCountCardContent.tsx b/frontend/src/components/menu/dashboard-menu-order/TimeSlotMenuOrderCountCardContent.tsx
new file mode 100644
index 000000000..db2e4a7a6
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-order/TimeSlotMenuOrderCountCardContent.tsx
@@ -0,0 +1,24 @@
+import { ORDER_COUNT } from '@/constants/menu';
+import type { GetDashboardTimeSlotMenuOrderCountResponseDto } from '@/types/menu';
+
+import { TimeSlotMenuOrderCountContent } from './TimeSlotMenuOrderCountContent';
+
+const { EXAMPLE_TIME_SLOT_2H, EXAMPLE_MENU_NAME } = ORDER_COUNT;
+// 현재 주문건수가 가장 많은 메뉴 카드
+interface TimeSlotMenuOrderCountCardContentProps extends GetDashboardTimeSlotMenuOrderCountResponseDto {
+ className?: string;
+}
+
+export const TimeSlotMenuOrderCountCardContent = ({
+ timeSlot2H = EXAMPLE_TIME_SLOT_2H,
+ menuName = EXAMPLE_MENU_NAME,
+ className,
+}: TimeSlotMenuOrderCountCardContentProps) => {
+ return (
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-order/TimeSlotMenuOrderCountContent.tsx b/frontend/src/components/menu/dashboard-menu-order/TimeSlotMenuOrderCountContent.tsx
new file mode 100644
index 000000000..feeb5963d
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-order/TimeSlotMenuOrderCountContent.tsx
@@ -0,0 +1,28 @@
+import { cn, getNextHour } from '@/utils/shared';
+
+interface TimeSlotMenuOrderCountContentProps {
+ className?: string;
+ timeSlot2H: number;
+ menuName: string;
+}
+// 현재 주문건수가 가장 많은 메뉴 카드
+export const TimeSlotMenuOrderCountContent = ({
+ className,
+ timeSlot2H,
+ menuName,
+}: TimeSlotMenuOrderCountContentProps) => {
+ return (
+
+
+ {menuName}
+ {`는 ${timeSlot2H}~${getNextHour(timeSlot2H)}시`}
+
+ 주문이 가장 많아요
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-order/index.ts b/frontend/src/components/menu/dashboard-menu-order/index.ts
new file mode 100644
index 000000000..a52e7baaa
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-order/index.ts
@@ -0,0 +1 @@
+export { TimeSlotMenuOrderCountCardContent } from './TimeSlotMenuOrderCountCardContent';
diff --git a/frontend/src/components/menu/dashboard-menu-ranking/DashboardRankingContent.tsx b/frontend/src/components/menu/dashboard-menu-ranking/DashboardRankingContent.tsx
new file mode 100644
index 000000000..38f43b7ae
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-ranking/DashboardRankingContent.tsx
@@ -0,0 +1,61 @@
+import type { ReactNode } from 'react';
+
+import type { DashboardRankItem } from '@/types/menu';
+import { cn } from '@/utils/shared';
+
+import { RankItem } from './RankItem';
+
+interface DashboardRankingContentProps {
+ className?: string;
+ children?: ReactNode;
+ tHeadLabels?: string[];
+}
+
+// 메뉴분석에서 '메뉴 매출 랭킹', '식자재 소진량 랭킹' 카드에서 공통으로 사용하는 순위 컴포넌트
+export const DashboardRankingContent = ({
+ className,
+ children,
+ tHeadLabels = ['순위', '메뉴명', '매출액'], // 테이블 각 열의 이름, sr-only 클래스로 화면에서는 보이지 않지만 스크린 리더로 읽을 수 있도록 함
+}: DashboardRankingContentProps) => {
+ return (
+
+
+
+
+
+
+
+ {/* 테이블 각 열의 이름 지정*/}
+
+ {tHeadLabels.map((label) => (
+ | {label} |
+ ))}
+
+
+ {children}
+
+ );
+};
+
+interface DashboardRankingContentTableBodyProps {
+ rankItems: DashboardRankItem[];
+}
+const DashboardRankingContentTableBody = ({
+ rankItems,
+}: DashboardRankingContentTableBodyProps) => {
+ return (
+
+ {rankItems.map(({ rank, itemName, totalAmount, unit }) => (
+
+ ))}
+
+ );
+};
+
+DashboardRankingContent.TableBody = DashboardRankingContentTableBody;
diff --git a/frontend/src/components/menu/dashboard-menu-ranking/IngredientUnregisteredContent.tsx b/frontend/src/components/menu/dashboard-menu-ranking/IngredientUnregisteredContent.tsx
new file mode 100644
index 000000000..77a75d681
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-ranking/IngredientUnregisteredContent.tsx
@@ -0,0 +1,28 @@
+import { CDN_BASE_URL } from '@/constants/shared';
+import { cn } from '@/utils/shared';
+
+interface IngredientUnregisteredContentProps {
+ className?: string;
+}
+
+// 등록된 식재료가 없는 경우 화면에 보여질 컴포넌트
+export const IngredientUnregisteredContent = ({
+ className,
+}: IngredientUnregisteredContentProps) => {
+ return (
+
+
+

+
식재료 미등록
+
+ 자동으로 식재료 파악하고,
+
간편하게 매장을 운영하세요.
+
+
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-ranking/IngredientUsageRankingCardContent.tsx b/frontend/src/components/menu/dashboard-menu-ranking/IngredientUsageRankingCardContent.tsx
new file mode 100644
index 000000000..7a0950cd5
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-ranking/IngredientUsageRankingCardContent.tsx
@@ -0,0 +1,58 @@
+// 대시보드>메뉴분석에서 식재료별 소진량 랭킹 카드
+import { useMemo } from 'react';
+
+import { DASHBOARD_RANKING } from '@/constants/menu';
+import type { DashboardRankItem } from '@/types/menu';
+import type {
+ GetIngredientUsageRankingResponseDto,
+ IngredientUsage,
+} from '@/types/menu';
+
+import { DashboardRankingContent } from './DashboardRankingContent';
+import { IngredientUnregisteredContent } from './IngredientUnregisteredContent';
+
+// dto를 대시보드의 식재료 소진량 랭킹 카드 UI에서 사용하는 데이터 형태로 변환
+interface GetDashboardIngredientRankItemsParams {
+ items: IngredientUsage[];
+}
+const getDashboardIngredientRankItems = ({
+ items,
+}: GetDashboardIngredientRankItemsParams): DashboardRankItem[] => {
+ return items
+ .map((item, index) => ({
+ rank: index + 1,
+ itemName: item.ingredientName,
+ totalAmount: item.totalQuantity,
+ unit: item.baseUnit as DashboardRankItem['unit'],
+ }))
+ .slice(0, DASHBOARD_RANKING.MAX_DISPLAYED_RANK_ITEMS); // 최대 4등까지만 보여줌
+};
+
+interface IngredientUsageRankingCardContentProps extends GetIngredientUsageRankingResponseDto {
+ className?: string;
+}
+
+export const IngredientUsageRankingCardContent = ({
+ hasIngredient,
+ items,
+ className,
+}: IngredientUsageRankingCardContentProps) => {
+ // dto -> 대시보드의 메뉴>식재료 소진량 랭킹 카드 UI 데이터 형태로 변환
+ const ingredientRankItems = useMemo(
+ () => getDashboardIngredientRankItems({ items }),
+ [items],
+ );
+ // 등록된 식재료가 없는 경우 카드 내용
+ if (!hasIngredient) {
+ return ;
+ }
+ // tHeadLabels를 통해 테이블 각 열의 이름을 지정
+ return (
+
+
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-ranking/MenuSalesRankingCardContent.tsx b/frontend/src/components/menu/dashboard-menu-ranking/MenuSalesRankingCardContent.tsx
new file mode 100644
index 000000000..32e02f45d
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-ranking/MenuSalesRankingCardContent.tsx
@@ -0,0 +1,50 @@
+// 대시보드>메뉴분석에서 메뉴별 매출 랭킹 카드
+import { useMemo } from 'react';
+
+import { DASHBOARD_RANKING } from '@/constants/menu';
+import type { DashboardRankItem } from '@/types//menu';
+import type { GetMenuSalesRankingResponseDto, MenuSales } from '@/types/menu';
+
+import { DashboardRankingContent } from './DashboardRankingContent';
+
+// dto를 대시보드의 메뉴 매출 랭킹 카드 UI에서 사용하는 데이터 형태로 변환
+interface GetDashboardMenuRankItemsParams {
+ items: MenuSales[];
+}
+const getDashboardMenuRankItems = ({
+ items,
+}: GetDashboardMenuRankItemsParams): DashboardRankItem[] => {
+ return items
+ .map((item, index) => ({
+ rank: index + 1,
+ itemName: item.menuName,
+ totalAmount: item.totalSalesAmount,
+ unit: '원' as const,
+ }))
+ .slice(0, DASHBOARD_RANKING.MAX_DISPLAYED_RANK_ITEMS);
+ // 최대 4등까지만 보여줌
+};
+
+interface MenuSalesRankingCardContentProps extends GetMenuSalesRankingResponseDto {
+ className?: string;
+}
+
+export const MenuSalesRankingCardContent = ({
+ items,
+ className,
+}: MenuSalesRankingCardContentProps) => {
+ // dto -> 대시보드의 메뉴>매출 랭킹 카드 UI 데이터 형태로 변환
+ const menuRankItems = useMemo(
+ () => getDashboardMenuRankItems({ items }),
+ [items],
+ );
+ // tHeadLabels를 통해 테이블 각 열의 이름을 지정
+ return (
+
+
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-ranking/RankItem.tsx b/frontend/src/components/menu/dashboard-menu-ranking/RankItem.tsx
new file mode 100644
index 000000000..c05d6ec31
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-ranking/RankItem.tsx
@@ -0,0 +1,44 @@
+import { RankBadge } from '@/components/shared';
+import type { IngredientUnit } from '@/constants/ingredient';
+import { DASHBOARD_RANKING } from '@/constants/menu';
+import { formatNumber } from '@/utils/shared';
+
+// 대시보드의 매출 랭킹, 식자재 랭킹 테이블에서 사용하는 각 행 아이템 컴포넌트
+interface RankItemProps {
+ rank: number;
+ itemName: string;
+ totalAmount: number;
+ unit: '원' | IngredientUnit;
+}
+
+export const RankItem = ({
+ rank,
+ itemName,
+ totalAmount,
+ unit,
+}: RankItemProps) => {
+ const isHighlight = rank === DASHBOARD_RANKING.HIGHLIGHT_RANKING; // 1등만 하이라이트
+
+ return (
+
+ |
+
+ |
+
+
+ {itemName}
+
+ |
+
+
+ {formatNumber(totalAmount)}
+ {unit}
+ |
+
+ );
+};
diff --git a/frontend/src/components/menu/dashboard-menu-ranking/index.ts b/frontend/src/components/menu/dashboard-menu-ranking/index.ts
new file mode 100644
index 000000000..b8a4a5120
--- /dev/null
+++ b/frontend/src/components/menu/dashboard-menu-ranking/index.ts
@@ -0,0 +1,2 @@
+export { IngredientUsageRankingCardContent } from './IngredientUsageRankingCardContent';
+export { MenuSalesRankingCardContent } from './MenuSalesRankingCardContent';
diff --git a/frontend/src/components/menu/index.ts b/frontend/src/components/menu/index.ts
index 8753b4017..f01b60ef9 100644
--- a/frontend/src/components/menu/index.ts
+++ b/frontend/src/components/menu/index.ts
@@ -3,3 +3,9 @@ export { IngredientConsumptionOverview } from './ingredient-consumption';
export { MenuSalesPatternOverview } from './menu-sales-pattern';
export { MenuCombinationOverview } from './menu-combination';
export { CategoryRevenueChartLegend } from './CategoryRevenueChartLegend';
+export {
+ IngredientUsageRankingCardContent,
+ MenuSalesRankingCardContent,
+} from './dashboard-menu-ranking';
+export { TimeSlotMenuOrderCountCardContent } from './dashboard-menu-order';
+export { PopularMenuCombinationCardContent } from './dashboard-menu-combination';
diff --git a/frontend/src/components/menu/ingredient-consumption/IngredientConsumptionEmptyView.tsx b/frontend/src/components/menu/ingredient-consumption/IngredientConsumptionEmptyView.tsx
index 4a8a8f27c..4209934f9 100644
--- a/frontend/src/components/menu/ingredient-consumption/IngredientConsumptionEmptyView.tsx
+++ b/frontend/src/components/menu/ingredient-consumption/IngredientConsumptionEmptyView.tsx
@@ -12,7 +12,7 @@ export const IngredientConsumptionEmptyView = () => {
>

diff --git a/frontend/src/components/shared/edit-card-wrapper/EditCardWrapper.tsx b/frontend/src/components/shared/edit-card-wrapper/EditCardWrapper.tsx
index 2e86b1cb6..4d29124dd 100644
--- a/frontend/src/components/shared/edit-card-wrapper/EditCardWrapper.tsx
+++ b/frontend/src/components/shared/edit-card-wrapper/EditCardWrapper.tsx
@@ -57,7 +57,7 @@ export const EditCardWrapper = ({
GRID_GAP * (sizeY - 1), // 최소 높이 147px,
}}
className={cn(
- 'bg-special-card-bg rounded-400 border-grey-300 relative flex flex-col overflow-hidden border p-3',
+ 'bg-special-card-bg rounded-400 border-grey-300 relative flex flex-col overflow-hidden border p-2.5',
className,
)}
>
diff --git a/frontend/src/components/shared/images/images.stories.tsx b/frontend/src/components/shared/images/images.stories.tsx
index 6fef70d28..14efdf0a1 100644
--- a/frontend/src/components/shared/images/images.stories.tsx
+++ b/frontend/src/components/shared/images/images.stories.tsx
@@ -20,7 +20,7 @@ const images = [
'congratulation.svg',
'warning.svg',
'empty_dashboard.png',
- 'empty_ingridient.svg',
+ 'empty_ingredient.svg',
'up.svg',
'down.svg',
'graph_down.svg',
diff --git a/frontend/src/components/shared/rank-badge/RankBadge.stories.tsx b/frontend/src/components/shared/rank-badge/RankBadge.stories.tsx
index 2d8bd246e..2b0bb8d81 100644
--- a/frontend/src/components/shared/rank-badge/RankBadge.stories.tsx
+++ b/frontend/src/components/shared/rank-badge/RankBadge.stories.tsx
@@ -12,7 +12,7 @@ const meta = {
argTypes: {
rank: { control: 'number' },
variant: { control: 'select', options: ['default', 'highlight'] },
- size: { control: 'select', options: ['sm', 'md'] },
+ size: { control: 'select', options: ['sm', 'md', 'lg'] },
},
} satisfies Meta
;
diff --git a/frontend/src/components/shared/rank-badge/RankBadgeVariant.ts b/frontend/src/components/shared/rank-badge/RankBadgeVariant.ts
index f780557e2..94b35a53d 100644
--- a/frontend/src/components/shared/rank-badge/RankBadgeVariant.ts
+++ b/frontend/src/components/shared/rank-badge/RankBadgeVariant.ts
@@ -10,7 +10,8 @@ export const rankBadgeVariants = cva(
},
size: {
sm: 'size-6 body-small-bold',
- md: 'size-8 body-medium-bold',
+ md: 'size-7 body-small-bold',
+ lg: 'size-8 body-medium-bold',
},
},
defaultVariants: {
diff --git a/frontend/src/constants/menu/dashboard-menu-combination/index.ts b/frontend/src/constants/menu/dashboard-menu-combination/index.ts
new file mode 100644
index 000000000..7f6da46c8
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-combination/index.ts
@@ -0,0 +1 @@
+export { POPULAR_MENU_COMBINATION } from './popularMenuCombination';
diff --git a/frontend/src/constants/menu/dashboard-menu-combination/popularMenuCombination.ts b/frontend/src/constants/menu/dashboard-menu-combination/popularMenuCombination.ts
new file mode 100644
index 000000000..1541c4c1c
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-combination/popularMenuCombination.ts
@@ -0,0 +1,5 @@
+export const POPULAR_MENU_COMBINATION = {
+ // 편집 패널에서 보여질 데이터
+ EXAMPLE_FIRST_MENU_NAME: '아메리카노(ICE)',
+ EXAMPLE_SECOND_MENU_NAME: '휘낭시에',
+} as const;
diff --git a/frontend/src/constants/menu/dashboard-menu-order/index.ts b/frontend/src/constants/menu/dashboard-menu-order/index.ts
new file mode 100644
index 000000000..d3ef8a489
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-order/index.ts
@@ -0,0 +1 @@
+export { ORDER_COUNT } from './orderCount';
diff --git a/frontend/src/constants/menu/dashboard-menu-order/orderCount.ts b/frontend/src/constants/menu/dashboard-menu-order/orderCount.ts
new file mode 100644
index 000000000..054e79406
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-order/orderCount.ts
@@ -0,0 +1,5 @@
+export const ORDER_COUNT = {
+ // 편집 패널에서 보여질 데이터
+ EXAMPLE_TIME_SLOT_2H: 9,
+ EXAMPLE_MENU_NAME: '아메리카노(ICE)',
+} as const;
diff --git a/frontend/src/constants/menu/dashboard-menu-ranking/dashboardRanking.ts b/frontend/src/constants/menu/dashboard-menu-ranking/dashboardRanking.ts
new file mode 100644
index 000000000..60c9e984a
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-ranking/dashboardRanking.ts
@@ -0,0 +1,4 @@
+export const DASHBOARD_RANKING = {
+ HIGHLIGHT_RANKING: 1, // 강조할 순위
+ MAX_DISPLAYED_RANK_ITEMS: 4, //화면에 보여질 최대 순위 개수
+} as const;
diff --git a/frontend/src/constants/menu/dashboard-menu-ranking/index.ts b/frontend/src/constants/menu/dashboard-menu-ranking/index.ts
new file mode 100644
index 000000000..96f70b978
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-ranking/index.ts
@@ -0,0 +1,3 @@
+export { MENU_SALES_RANKING } from './menuSalesRanking';
+export { INGREDIENT_USAGE_RANKING } from './ingredientUsageRanking';
+export { DASHBOARD_RANKING } from './dashboardRanking';
diff --git a/frontend/src/constants/menu/dashboard-menu-ranking/ingredientUsageRanking.ts b/frontend/src/constants/menu/dashboard-menu-ranking/ingredientUsageRanking.ts
new file mode 100644
index 000000000..b910cba42
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-ranking/ingredientUsageRanking.ts
@@ -0,0 +1,28 @@
+import type { GetIngredientUsageRankingResponseDto } from '@/types/menu';
+
+// 편집 패널에서 보여질 데이터 -> 메뉴별 매출 랭킹 카드
+export const INGREDIENT_USAGE_RANKING = {
+ EXAMPLE_HAS_INGREDIENT: true,
+ EXAMPLE_INGREDIENT_USAGE_RANKING_ITEMS: [
+ {
+ ingredientName: '우유',
+ totalQuantity: 3921,
+ baseUnit: 'ml',
+ },
+ {
+ ingredientName: '케냐산 원두',
+ totalQuantity: 1200,
+ baseUnit: 'g',
+ },
+ {
+ ingredientName: '생딸기',
+ totalQuantity: 840,
+ baseUnit: 'g',
+ },
+ {
+ ingredientName: '딸기 시럽',
+ totalQuantity: 10,
+ baseUnit: 'ml',
+ },
+ ] as const satisfies GetIngredientUsageRankingResponseDto['items'],
+} as const;
diff --git a/frontend/src/constants/menu/dashboard-menu-ranking/menuSalesRanking.ts b/frontend/src/constants/menu/dashboard-menu-ranking/menuSalesRanking.ts
new file mode 100644
index 000000000..6df5fb632
--- /dev/null
+++ b/frontend/src/constants/menu/dashboard-menu-ranking/menuSalesRanking.ts
@@ -0,0 +1,32 @@
+import type { GetMenuSalesRankingResponseDto } from '@/types/menu';
+
+// 편집 패널에서 보여질 데이터 -> 메뉴별 매출 랭킹 카드
+export const MENU_SALES_RANKING = {
+ EXAMPLE_MENU_SALES_RANKING_ITEMS: [
+ {
+ menuName: '아메리카노(ICE)',
+ totalSalesAmount: 1500000,
+ orderCount: 120,
+ },
+ {
+ menuName: '카페라떼(HOT)',
+ totalSalesAmount: 1200000,
+ orderCount: 100,
+ },
+ {
+ menuName: '카페모카(ICE)',
+ totalSalesAmount: 900000,
+ orderCount: 80,
+ },
+ {
+ menuName: '바닐라 라떼(HOT)',
+ totalSalesAmount: 600000,
+ orderCount: 60,
+ },
+ {
+ menuName: '카라멜 마끼아또(ICE)',
+ totalSalesAmount: 400000,
+ orderCount: 90,
+ },
+ ] as const satisfies GetMenuSalesRankingResponseDto['items'],
+} as const;
diff --git a/frontend/src/constants/menu/index.ts b/frontend/src/constants/menu/index.ts
index a71e1a9e7..f8b4eb8b9 100644
--- a/frontend/src/constants/menu/index.ts
+++ b/frontend/src/constants/menu/index.ts
@@ -1,3 +1,10 @@
-export { MENU_SALES_RANK } from './menuSalesRank';
export { INGREDIENT_CONSUMPTION_RANK } from './ingredientConsumption';
export { MENU_COMBINATION } from './menuCombination';
+export {
+ MENU_SALES_RANKING,
+ INGREDIENT_USAGE_RANKING,
+ DASHBOARD_RANKING,
+} from './dashboard-menu-ranking';
+export { ORDER_COUNT } from './dashboard-menu-order';
+export { POPULAR_MENU_COMBINATION } from './dashboard-menu-combination';
+export { MENU_SALES_RANK } from './menuSalesRank';
diff --git a/frontend/src/constants/shared/edit-card-wrapper/editCardWrapper.ts b/frontend/src/constants/shared/edit-card-wrapper/editCardWrapper.ts
index c2c2c8459..b220eaa52 100644
--- a/frontend/src/constants/shared/edit-card-wrapper/editCardWrapper.ts
+++ b/frontend/src/constants/shared/edit-card-wrapper/editCardWrapper.ts
@@ -1,8 +1,8 @@
export const EDIT_CARD_WRAPPER = {
CHANGE_SCALE: 0.65, // 65% 축소
- PADDING_SIZE: 12, // 패딩 박스 내 여백 사이즈(12px)
+ PADDING_SIZE: 10, // 패딩 박스 내 여백 사이즈(12px)
HEADER_HEIGHT: 26, // 헤더 높이(위에 있는 버튼들 높이 : 26px)
HEADER_MAIN_GAP: 16, // 헤더와 내용 사이 간격(16px)
MIN_WIDTH: 220, // 최소 너비 220px
- MIN_HEIGHT: 147, // 최소 높이 147px
+ MIN_HEIGHT: 150, // 최소 높이 150px
};
diff --git a/frontend/src/pages/dashboard-page/DashboradPage.tsx b/frontend/src/pages/dashboard-page/DashboardPage.tsx
similarity index 100%
rename from frontend/src/pages/dashboard-page/DashboradPage.tsx
rename to frontend/src/pages/dashboard-page/DashboardPage.tsx
diff --git a/frontend/src/pages/dashboard-page/index.ts b/frontend/src/pages/dashboard-page/index.ts
index ab90e9b1d..d1e7bf228 100644
--- a/frontend/src/pages/dashboard-page/index.ts
+++ b/frontend/src/pages/dashboard-page/index.ts
@@ -1 +1 @@
-export { DashboardPage } from './DashboradPage';
+export { DashboardPage } from './DashboardPage';
diff --git a/frontend/src/types/menu/dashboard-menu-ranking/dashboardRankItem.ts b/frontend/src/types/menu/dashboard-menu-ranking/dashboardRankItem.ts
new file mode 100644
index 000000000..37fb8c4a1
--- /dev/null
+++ b/frontend/src/types/menu/dashboard-menu-ranking/dashboardRankItem.ts
@@ -0,0 +1,9 @@
+import type { IngredientUnit } from '@/constants/ingredient';
+
+// 대시보드 메뉴 파트에서 메뉴/식자재 랭킹에서 사용되는 데이터 형태
+export interface DashboardRankItem {
+ rank: number;
+ itemName: string;
+ totalAmount: number;
+ unit: '원' | IngredientUnit;
+}
diff --git a/frontend/src/types/menu/dashboard-menu-ranking/index.ts b/frontend/src/types/menu/dashboard-menu-ranking/index.ts
new file mode 100644
index 000000000..a29df2498
--- /dev/null
+++ b/frontend/src/types/menu/dashboard-menu-ranking/index.ts
@@ -0,0 +1 @@
+export type { DashboardRankItem } from './dashboardRankItem';
diff --git a/frontend/src/types/menu/dto/getDashboardPopularMenuCombinationDto.ts b/frontend/src/types/menu/dto/getDashboardPopularMenuCombinationDto.ts
new file mode 100644
index 000000000..1b4d760df
--- /dev/null
+++ b/frontend/src/types/menu/dto/getDashboardPopularMenuCombinationDto.ts
@@ -0,0 +1,13 @@
+// MNU_05 – 인기 메뉴 조합 카드
+// 대시보드 용 응답 DTO
+
+// GetDashboardPopularMenuCombinationResponseDto 예시
+// {
+// "firstMenuName": "불고기 버거",
+// "secondMenuName": "감자튀김"
+// }
+
+export interface GetDashboardPopularMenuCombinationResponseDto {
+ firstMenuName: string;
+ secondMenuName: string;
+}
diff --git a/frontend/src/types/menu/dto/getDashboardTimeSlotMenuOrderCountDto.ts b/frontend/src/types/menu/dto/getDashboardTimeSlotMenuOrderCountDto.ts
new file mode 100644
index 000000000..31c38d932
--- /dev/null
+++ b/frontend/src/types/menu/dto/getDashboardTimeSlotMenuOrderCountDto.ts
@@ -0,0 +1,9 @@
+// MNU_03 – 시간대별 메뉴 주문건수 카드
+// 대시보드 용 응답 DTO
+// GetDashboardTimeSlotMenuOrderCountDto 예시
+// {"timeSlot2H":10,"menuName":"불고기 버거"}
+
+export interface GetDashboardTimeSlotMenuOrderCountResponseDto {
+ timeSlot2H: number;
+ menuName: string;
+}
diff --git a/frontend/src/types/menu/dto/getIngredientUsageRankingDto.ts b/frontend/src/types/menu/dto/getIngredientUsageRankingDto.ts
new file mode 100644
index 000000000..b792c8875
--- /dev/null
+++ b/frontend/src/types/menu/dto/getIngredientUsageRankingDto.ts
@@ -0,0 +1,30 @@
+// IngredientUsage 예시
+// { "ingredientName": "소고기", "totalQuantity": 30000, "baseUnit": "g" },
+
+export interface IngredientUsage {
+ ingredientName: string;
+ totalQuantity: number;
+ baseUnit: string;
+}
+// GetIngredientUsageRankingResponseDto 예시
+// {
+// "hasIngredient": true,
+// "items": [
+// {
+// "ingredientName": "소고기",
+// "totalQuantity": 30000,
+// "baseUnit": "g"
+// },
+// {
+// "ingredientName": "치즈",
+// "totalQuantity": 5000,
+// "baseUnit": "g"
+// }
+// ]
+// }
+
+// 백엔드에서 보내주는 식자재별 소진량 DTO -> 식자재별 소진량 랭킹에 사용
+export interface GetIngredientUsageRankingResponseDto {
+ hasIngredient: boolean; // 식자재 등록 여부
+ items: IngredientUsage[];
+}
diff --git a/frontend/src/types/menu/dto/getMenuSalesRankingDto.ts b/frontend/src/types/menu/dto/getMenuSalesRankingDto.ts
new file mode 100644
index 000000000..4f1fb12be
--- /dev/null
+++ b/frontend/src/types/menu/dto/getMenuSalesRankingDto.ts
@@ -0,0 +1,17 @@
+// MenuSales 예시
+// {
+// "menuName": "불고기 버거",
+// "totalSalesAmount": 1500000,
+// "orderCount": 120
+// },
+
+export interface MenuSales {
+ menuName: string;
+ totalSalesAmount: number;
+ orderCount: number;
+}
+
+// 백엔드에서 보내주는 메뉴별 매출, 주문건 수 DTO -> 매출별 랭킹에 사용
+export interface GetMenuSalesRankingResponseDto {
+ items: MenuSales[];
+}
diff --git a/frontend/src/types/menu/dto/getPopularMenuCombinationDto.ts b/frontend/src/types/menu/dto/getPopularMenuCombinationDto.ts
new file mode 100644
index 000000000..fe305066b
--- /dev/null
+++ b/frontend/src/types/menu/dto/getPopularMenuCombinationDto.ts
@@ -0,0 +1,20 @@
+export interface PairedMenu {
+ menuName: string;
+ count: number;
+}
+// PoplularMenuCombination 예시
+// {
+// "baseMenuName": "불고기 버거",
+// "pairedMenus": [
+// { "menuName": "감자튀김", "count": 80 },
+// { "menuName": "콜라", "count": 70 }
+// ]
+// }
+export interface PoplularMenuCombination {
+ baseMenuName: string;
+ pairedMenus: PairedMenu[];
+}
+
+export interface GetPopularMenuCombinationResponseDto {
+ items: PoplularMenuCombination[];
+}
diff --git a/frontend/src/types/menu/dto/index.ts b/frontend/src/types/menu/dto/index.ts
new file mode 100644
index 000000000..b67587a98
--- /dev/null
+++ b/frontend/src/types/menu/dto/index.ts
@@ -0,0 +1,14 @@
+export type {
+ GetMenuSalesRankingResponseDto,
+ MenuSales,
+} from './getMenuSalesRankingDto';
+export type {
+ GetIngredientUsageRankingResponseDto,
+ IngredientUsage,
+} from './getIngredientUsageRankingDto';
+export type {
+ GetPopularMenuCombinationResponseDto,
+ PoplularMenuCombination,
+} from './getPopularMenuCombinationDto';
+export type { GetDashboardTimeSlotMenuOrderCountResponseDto } from './getDashboardTimeSlotMenuOrderCountDto';
+export type { GetDashboardPopularMenuCombinationResponseDto } from './getDashboardPopularMenuCombinationDto';
diff --git a/frontend/src/types/menu/index.ts b/frontend/src/types/menu/index.ts
index cb2102bcc..2caf694bb 100644
--- a/frontend/src/types/menu/index.ts
+++ b/frontend/src/types/menu/index.ts
@@ -2,3 +2,12 @@ export type { IngredientConsumptionRank } from './ingredientConsumptionRank';
export type { MenuCombinationRank } from './menuCombinationRank';
export type { MenuSalesRank } from './menuSalesRank';
export type { CategoriesRevenue } from './categoriesRevenue';
+export type { DashboardRankItem } from './dashboard-menu-ranking';
+export type {
+ GetDashboardPopularMenuCombinationResponseDto,
+ GetDashboardTimeSlotMenuOrderCountResponseDto,
+ GetMenuSalesRankingResponseDto,
+ GetIngredientUsageRankingResponseDto,
+ MenuSales,
+ IngredientUsage,
+} from './dto';
diff --git a/frontend/src/utils/shared/getNextHour.ts b/frontend/src/utils/shared/getNextHour.ts
new file mode 100644
index 000000000..e154f33f5
--- /dev/null
+++ b/frontend/src/utils/shared/getNextHour.ts
@@ -0,0 +1,5 @@
+// 시간이 들어오면 다음 시간대 (예: 9 -> 10, 23 -> 24, 24 -> 1)를 반환하는 함수
+// 24가 들어오면 1 반환해야함
+export const getNextHour = (hour: number): number => {
+ return hour === 24 ? 1 : hour + 1;
+};
diff --git a/frontend/src/utils/shared/index.ts b/frontend/src/utils/shared/index.ts
index 87edaff21..df454ca42 100644
--- a/frontend/src/utils/shared/index.ts
+++ b/frontend/src/utils/shared/index.ts
@@ -38,6 +38,7 @@ export {
} from './doughnut-chart';
export { createPeriodTypeProvider } from './period-select';
+export { getNextHour } from './getNextHour';
export { assertNever } from './assertNever';
export { getCoordinate } from './getCoordinate';
export {