diff --git a/apps/nowait-admin/src/assets/analytics/arrow_back.svg b/apps/nowait-admin/src/assets/analytics/arrow_back.svg new file mode 100644 index 00000000..f8fc195a --- /dev/null +++ b/apps/nowait-admin/src/assets/analytics/arrow_back.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/nowait-admin/src/assets/analytics/arrow_back_active.svg b/apps/nowait-admin/src/assets/analytics/arrow_back_active.svg new file mode 100644 index 00000000..e567d047 --- /dev/null +++ b/apps/nowait-admin/src/assets/analytics/arrow_back_active.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/nowait-admin/src/assets/analytics/arrow_forward.svg b/apps/nowait-admin/src/assets/analytics/arrow_forward.svg new file mode 100644 index 00000000..e3543fb7 --- /dev/null +++ b/apps/nowait-admin/src/assets/analytics/arrow_forward.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/nowait-admin/src/assets/analytics/arrow_forward_active.svg b/apps/nowait-admin/src/assets/analytics/arrow_forward_active.svg new file mode 100644 index 00000000..76bb55df --- /dev/null +++ b/apps/nowait-admin/src/assets/analytics/arrow_forward_active.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/nowait-admin/src/hooks/analytics/useGetPopularMenu.tsx b/apps/nowait-admin/src/hooks/analytics/useGetPopularMenu.tsx new file mode 100644 index 00000000..1fc73c10 --- /dev/null +++ b/apps/nowait-admin/src/hooks/analytics/useGetPopularMenu.tsx @@ -0,0 +1,29 @@ +import { useQuery } from "@tanstack/react-query"; +import AdminApi from "../../utils/AdminApi"; + +interface PopularMenuItem { + menuId: number; + menuName: string; + totalSalesCount: number; + boothName: string; +} + +interface PopularMenuResponse { + success: boolean; + response: PopularMenuItem[]; +} + +const fetchPopularMenu = async (): Promise => { + const res = await AdminApi.get( + "/admin/statistics/popular-menu" + ); + return res.data.response; +}; + +export const useGetPopularMenu = () => { + return useQuery({ + queryKey: ["popular-menu"], + queryFn: fetchPopularMenu, + staleTime: 1000 * 60 * 5, // 5분 캐싱 + }); +}; diff --git a/apps/nowait-admin/src/hooks/analytics/useGetSalesByDate.tsx b/apps/nowait-admin/src/hooks/analytics/useGetSalesByDate.tsx new file mode 100644 index 00000000..35a5daf4 --- /dev/null +++ b/apps/nowait-admin/src/hooks/analytics/useGetSalesByDate.tsx @@ -0,0 +1,23 @@ +import { useQuery } from "@tanstack/react-query"; +import AdminApi from "../../utils/AdminApi"; + +interface SalesResponse { + success: boolean; + response: number | string; // "해당일 매출 데이터가 없습니다." 또는 숫자형 매출값 +} + +const fetchSalesByDate = async (date: string): Promise => { + const res = await AdminApi.get(`/admin/statistics/sales`, { + params: { date }, + }); + return res.data.response; +}; + +export const useGetSalesByDate = (date: string) => { + return useQuery({ + queryKey: ["sales-by-date", date], + queryFn: () => fetchSalesByDate(date), + enabled: !!date, // 날짜 있을 때만 실행 + staleTime: 1000 * 60, // 1분 캐싱 + }); +}; diff --git a/apps/nowait-admin/src/hooks/analytics/useGetTopSalse.tsx b/apps/nowait-admin/src/hooks/analytics/useGetTopSalse.tsx new file mode 100644 index 00000000..a1f68652 --- /dev/null +++ b/apps/nowait-admin/src/hooks/analytics/useGetTopSalse.tsx @@ -0,0 +1,31 @@ +import { useQuery } from "@tanstack/react-query"; +import AdminApi from "../../utils/AdminApi"; + +interface TopSalesItem { + rank: number; + name: string; + department: string; + salesCount: number; + isCurrentBooth: boolean; + rankChange: number; +} + +interface TopSalesResponse { + success: boolean; + response: TopSalesItem[]; +} + +const fetchTopSales = async (): Promise => { + const res = await AdminApi.get( + "/admin/statistics/top-sales" + ); + return res.data.response; +}; + +export const useGetTopSales = () => { + return useQuery({ + queryKey: ["top-sales"], + queryFn: fetchTopSales, + staleTime: 1000 * 60 * 5, // 5분 캐싱 + }); +}; diff --git a/apps/nowait-admin/src/pages/AdminAnalytics/AdminAnalytics.tsx b/apps/nowait-admin/src/pages/AdminAnalytics/AdminAnalytics.tsx index 431a5343..b76bde79 100644 --- a/apps/nowait-admin/src/pages/AdminAnalytics/AdminAnalytics.tsx +++ b/apps/nowait-admin/src/pages/AdminAnalytics/AdminAnalytics.tsx @@ -55,6 +55,7 @@ const boothData: BoothRanking[] = [ ]; const AdminAnalytics = () => { + // const { data, isLoading, isError } = useGetTopSales(); return (
diff --git a/apps/nowait-admin/src/pages/AdminAnalytics/components/BoothSalesRankingCard .tsx b/apps/nowait-admin/src/pages/AdminAnalytics/components/BoothSalesRankingCard .tsx index 4165074c..4f182cd8 100644 --- a/apps/nowait-admin/src/pages/AdminAnalytics/components/BoothSalesRankingCard .tsx +++ b/apps/nowait-admin/src/pages/AdminAnalytics/components/BoothSalesRankingCard .tsx @@ -20,23 +20,15 @@ const BoothSalesRankingCard: React.FC = ({ data, }) => { return ( -
+
-

- {"통계2"} -

-
-
매출 순위
- - - -
+

부스별 판매순위

{date}
-
    +
      {data.map((item) => { const isUp = item.rankChange > 0; const isDown = item.rankChange < 0; @@ -45,15 +37,17 @@ const BoothSalesRankingCard: React.FC = ({ return (
    • -
      - - {item.rank}위 +
      + + {item.rank} -
      +
      {item.name} diff --git a/apps/nowait-admin/src/pages/AdminAnalytics/components/HeaderStatus.tsx b/apps/nowait-admin/src/pages/AdminAnalytics/components/HeaderStatus.tsx index d248cd85..4523520b 100644 --- a/apps/nowait-admin/src/pages/AdminAnalytics/components/HeaderStatus.tsx +++ b/apps/nowait-admin/src/pages/AdminAnalytics/components/HeaderStatus.tsx @@ -1,9 +1,10 @@ import React from "react"; import SalesCard from "./SalesCard"; +import TotalSalesCard from "./TotalSalesCard"; const HeaderStatus = () => { return ( -
      +
      {/* 오늘 매출 */} { percent={13.6} /> {/* 누적 매출 */} -
      {/* 인기 메뉴 TOP 5 */} -
      -
      +
      +

      인기 메뉴 TOP 5

      2025.07.18 금
      -
        +
          {[1, 2, 3, 4, 5].map((rank) => ( -
        • - {rank}위 참치마요주먹밥 - 100개 +
        • + + {rank}

          참치마요주먹밥

          +
          + 100개
        • ))}
        diff --git a/apps/nowait-admin/src/pages/AdminAnalytics/components/SalesCard.tsx b/apps/nowait-admin/src/pages/AdminAnalytics/components/SalesCard.tsx index 00a0d81f..fb7ed164 100644 --- a/apps/nowait-admin/src/pages/AdminAnalytics/components/SalesCard.tsx +++ b/apps/nowait-admin/src/pages/AdminAnalytics/components/SalesCard.tsx @@ -1,3 +1,8 @@ +import backIcon from "../../../assets/analytics/arrow_back.svg"; +import forwardIcon from "../../../assets/analytics/arrow_forward.svg"; +import activeBackIcon from "../../../assets/analytics/arrow_back_active.svg"; +import activeForwardIcon from "../../../assets/analytics/arrow_forward_active.svg"; +import { useState } from "react"; interface SalesCardProps { title: string; // 예: "오늘 매출" 또는 "누적 매출" date: string; @@ -12,23 +17,44 @@ const SalesCard: React.FC = ({ amount, diffAmount, percent, -}) => ( -
        -
        -

        {title}

        -

        {date}

        -
        -
        -
        -

        - {amount.toLocaleString()}원 +}) => { + const [isHoverBack, setIsHoverBack] = useState(false); + const [isHoverForward, setIsHoverForward] = useState(false); + return ( +

        +
        + +

        {title}

        +

        {date}

        +
        + + setIsHoverBack(true)} + onMouseLeave={() => setIsHoverBack(false)} + /> + setIsHoverForward(true)} + onMouseLeave={() => setIsHoverForward(false)} + /> + +
        + +
        +
        +

        + {amount.toLocaleString()}원 +

        + +{percent}% +
        +

        + 어제보다 {diffAmount.toLocaleString()}원 더 벌었어요!

        - +{percent}%
        -

        - 어제보다 {diffAmount.toLocaleString()}원 더 벌었어요! -

        -
        -); + ); +}; export default SalesCard; diff --git a/apps/nowait-admin/src/pages/AdminAnalytics/components/TotalSalesCard.tsx b/apps/nowait-admin/src/pages/AdminAnalytics/components/TotalSalesCard.tsx new file mode 100644 index 00000000..a6e60d58 --- /dev/null +++ b/apps/nowait-admin/src/pages/AdminAnalytics/components/TotalSalesCard.tsx @@ -0,0 +1,44 @@ +import React from "react"; + +interface TotalSalesCardProps { + title: string; // 예: "누적매출" + date: string; // 예: "2025. 07.18 - 07.19" + amount: number; // 예: 1800000 + percent: number; // 예: -0.6 +} + +const TotalSalesCard: React.FC = ({ + title, + date, + amount, + percent, +}) => { + const percentColor = + percent > 0 + ? "text-[#FF5A1F]" // 상승: 주황 + : percent < 0 + ? "text-[#3A75E5]" // 하락: 파랑 + : "text-gray-500"; // 변동 없음 + + return ( +
        + {/* 상단 제목 + 날짜 */} +
        +

        {title}

        +

        {date}

        +
        + + {/* 금액 + 퍼센트 변화 */} +
        +

        + {amount.toLocaleString()}원 +

        + + {percent > 0 ? `+${percent}%` : `${percent}%`} + +
        +
        + ); +}; + +export default TotalSalesCard;