diff --git a/apps/nowait-admin/src/pages/AdminOrders/AdminOrders.tsx b/apps/nowait-admin/src/pages/AdminOrders/AdminOrders.tsx index d32e1f14..b4c6af77 100644 --- a/apps/nowait-admin/src/pages/AdminOrders/AdminOrders.tsx +++ b/apps/nowait-admin/src/pages/AdminOrders/AdminOrders.tsx @@ -1,15 +1,21 @@ import { useState, useRef } from "react"; -import { PaymentCard, PaymentDetail, CookCard } from "./OrderCard"; +import { PaymentCard, PaymentDetail, CookCard, CookedCard } from "./OrderCard"; import RefreshIcon from "../../assets/refresh.svg?react"; import CookedPage from "./CookedPage"; import { useGetOrderList } from "../../hooks/useGetOrderList"; +import { useWindowWidth } from "../../hooks/useWindowWidth"; import type { Order } from "../../types/order"; const AdminOrders = () => { const [activeTab, setActiveTab] = useState<"전체" | "조리 완료">("전체"); + const [mobileActiveTab, setMobileActiveTab] = useState< + "입금 대기" | "조리 중" | "조리 완료" + >("입금 대기"); const [selectedPayment, setSelectedPayment] = useState(null); const [savedScrollPosition, setSavedScrollPosition] = useState(0); const scrollContainerRef = useRef(null); + const windowWidth = useWindowWidth(); + const isMobile = windowWidth <= 450; // API에서 주문 데이터 가져오기 const { data: orders = [], isLoading, error, refetch } = useGetOrderList(); @@ -62,30 +68,72 @@ const AdminOrders = () => { style={{ height: "calc(100vh - 2.5rem)" }} > {/* 헤더 영역 - 고정 높이 */} -
-
- {/* 최상단 버튼 */} -
setActiveTab("전체")} - > - 진행 중 {paymentWaitingData.length + cookingData.length} +
+ {/* 데스크톱 탭 버튼 (425px 초과에서만 표시) */} + {!isMobile && ( +
+
setActiveTab("전체")} + > + 진행 중 {paymentWaitingData.length + cookingData.length} +
+
setActiveTab("조리 완료")} + > + 조리 완료 {cookedData.length} +
-
setActiveTab("조리 완료")} - > - 완료 주문 {cookedData.length} + )} + + {/* 모바일 탭 버튼 (425px 이하에서만 표시) */} + {isMobile && ( +
+
setMobileActiveTab("입금 대기")} + > + 입금 대기 {paymentWaitingData.length} +
+
setMobileActiveTab("조리 중")} + > + 조리 중 {cookingData.length} +
+
setMobileActiveTab("조리 완료")} + > + 조리 완료 {cookedData.length} +
-
+ )}
{
- {activeTab === "전체" ? ( -
- {/* 입금 대기 블럭 */} -
-
-
입금 대기
-
- {paymentWaitingData.length} -
-
+ {/* 데스크톱 버전 */} + {!isMobile && ( + <> + {activeTab === "전체" ? ( +
+ {/* 입금 대기 블럭 */} +
+
+
+ 입금 대기 +
+
+ {paymentWaitingData.length} +
+
-
-
- 테이블 -
-
- 입금 내역 +
+
+ 테이블 +
+
+ 입금 내역 +
+
+
+ {isLoading ? ( +
+
+ 로딩 중... +
+
+ ) : paymentWaitingData.length > 0 ? ( + paymentWaitingData.map((payment) => ( + handlePaymentCardClick(payment)} + onSuccess={refetch} + /> + )) + ) : ( +
+
+ {error + ? "데이터를 불러오지 못했습니다." + : "아직 입금 대기 중인 주문이 없어요!"} +
+
+ )} + + {/* PaymentDetail 오버레이 */} + {selectedPayment && ( + + )} +
-
-
- {isLoading ? ( -
-
로딩 중...
+ + {/* 조리 중 블럭 */} +
+
+
+ 조리 중 +
+
+ {cookingData.length} +
- ) : paymentWaitingData.length > 0 ? ( - paymentWaitingData.map((payment) => ( - handlePaymentCardClick(payment)} - onSuccess={refetch} - /> - )) - ) : ( -
-
- {error - ? "데이터를 불러오지 못했습니다." - : "아직 입금 대기 중인 주문이 없어요!"} + +
+
테이블
+
+ 메뉴
+
수량
- )} - - {/* PaymentDetail 오버레이 */} - {selectedPayment && ( - - )} +
+ {isLoading ? ( +
+
+ 로딩 중... +
+
+ ) : cookingData.length > 0 ? ( + cookingData.map((cooking) => ( + + )) + ) : ( +
+
+ {error + ? "데이터를 불러오지 못했습니다." + : "조리 중인 주문이 없어요!"} +
+
+ )} +
+
-
+ ) : ( + + )} + + )} - {/* 조리 중 블럭 */} -
-
-
조리 중
-
- {cookingData.length} + {/* 모바일 버전 */} + {isMobile && ( + <> + {mobileActiveTab === "입금 대기" && ( +
+
+
+ 테이블 +
+
+ 입금 내역 +
-
+
+ {isLoading ? ( +
+
+ 로딩 중... +
+
+ ) : paymentWaitingData.length > 0 ? ( + paymentWaitingData.map((payment) => ( + handlePaymentCardClick(payment)} + onSuccess={refetch} + /> + )) + ) : ( +
+
+ {error + ? "데이터를 불러오지 못했습니다." + : "아직 입금 대기 중인 주문이 없어요!"} +
+
+ )} -
-
- 테이블 + {/* PaymentDetail 오버레이 */} + {selectedPayment && ( + + )}
-
- 메뉴 +
+ )} + + {mobileActiveTab === "조리 중" && ( +
+
+
테이블
+
+ 메뉴 +
+
수량
-
- 수량 +
+ {isLoading ? ( +
+
+ 로딩 중... +
+
+ ) : cookingData.length > 0 ? ( + cookingData.map((cooking) => ( + + )) + ) : ( +
+
+ {error + ? "데이터를 불러오지 못했습니다." + : "아직 조리 중인 주문이 없어요!"} +
+
+ )}
-
- {isLoading ? ( -
-
로딩 중...
-
- ) : cookingData.length > 0 ? ( - cookingData.map((cooking) => ( - - )) - ) : ( -
-
- {error - ? "데이터를 불러오지 못했습니다." - : "조리 중인 주문이 없어요!"} + )} + + {mobileActiveTab === "조리 완료" && ( +
+
+
테이블
+
금액
+
주문 시간
+
+
+ {isLoading ? ( +
+
+ 로딩 중... +
-
- )} + ) : cookedData.length > 0 ? ( + cookedData.map((cooked) => ( + + )) + ) : ( +
+
+ {error + ? "데이터를 불러오지 못했습니다." + : "조리 완료된 주문이 없어요!"} +
+
+ )} +
-
-
- ) : ( - + )} + )}
); diff --git a/apps/nowait-admin/src/pages/AdminOrders/OrderCard.tsx b/apps/nowait-admin/src/pages/AdminOrders/OrderCard.tsx index ab0fbe2e..2ca25527 100644 --- a/apps/nowait-admin/src/pages/AdminOrders/OrderCard.tsx +++ b/apps/nowait-admin/src/pages/AdminOrders/OrderCard.tsx @@ -1,8 +1,13 @@ import ArrowRight from "../../assets/arrow_back.svg?react"; import { useState } from "react"; -import { PaymentCheckModal, CookedModal } from "./OrderPageModal"; +import { + PaymentCheckModal, + CookedModal, + CookCompleteModal, +} from "./OrderPageModal"; import type { MenuNamesAndQuantities } from "../../types/order"; import { getTableBackgroundColor } from "../../utils/tableColors"; +import { useWindowWidth } from "../../hooks/useWindowWidth"; interface PaymentCardProps { orderId: number; @@ -130,7 +135,7 @@ const PaymentDetail = ({ return ( <> -
+
{/* 헤더 */}
@@ -228,50 +233,99 @@ const PaymentDetail = ({ }; interface CookCardProps { + orderId: number; tableNumber: number; menuNamesAndQuantities?: MenuNamesAndQuantities; + onSuccess?: () => void; } -const CookCard = ({ tableNumber, menuNamesAndQuantities }: CookCardProps) => { +const CookCard = ({ + orderId, + tableNumber, + menuNamesAndQuantities, + onSuccess, +}: CookCardProps) => { + const [showCookCompleteModal, setShowCookCompleteModal] = useState(false); + const windowWidth = useWindowWidth(); + const menuEntries = menuNamesAndQuantities ? Object.entries(menuNamesAndQuantities) : []; const isOneMenu = menuEntries.length === 1; + // 화면 크기에 따른 글자 수 제한 + const getMaxLength = () => { + if (windowWidth >= 1024) return 10; // lg + if (windowWidth >= 768) return 7; // md + return 7; // sm + }; + + const handleCookCompleteClick = () => { + setShowCookCompleteModal(true); + }; + + const handleCloseCookCompleteModal = () => { + setShowCookCompleteModal(false); + }; + return ( -
-
-
- {tableNumber} -
-
- {menuEntries.map(([menuName, quantity], index) => ( -
-
- {menuName.length > 10 - ? menuName.substring(0, 10) + "..." - : menuName} -
-
{quantity}
-
- ))} -
-
+ <>
- 조리 완료 +
+
+ {tableNumber} +
+
+ {menuEntries.map(([menuName, quantity], index) => ( +
+
+ {menuName.length > getMaxLength() + ? menuName.substring(0, getMaxLength()) + "..." + : menuName} +
+
{quantity}
+
+ ))} +
+
+
+ 조리 완료 +
-
+ + {/* CookCompleteModal 오버레이 */} + {showCookCompleteModal && ( +
+ +
+ )} + ); }; @@ -295,6 +349,8 @@ const CookedCard = ({ onSuccess, }: CookedCardProps) => { const [showCookedModal, setShowCookedModal] = useState(false); + const windowWidth = useWindowWidth(); + const isMobile = windowWidth <= 450; // 첫 번째 메뉴 이름과 나머지 메뉴 개수 계산 const menuEntries = menuNamesAndQuantities @@ -317,31 +373,51 @@ const CookedCard = ({ return ( <> -
-
- {tableNumber}번 -
-
- {depositorName} -
-
- {menuDisplayText} -
-
- {totalAmount.toLocaleString()}원 -
-
- {createdAt} -
-
+ {/* 모바일 버전 */} + {isMobile ? ( +
+
+
{tableNumber}번
+
+ {totalAmount.toLocaleString()}원 +
+
{createdAt}
+
주문 복구
-
+ ) : ( + /* 데스크톱 버전 */ +
+
+ {tableNumber}번 +
+
+ {depositorName} +
+
+ {menuDisplayText} +
+
+ {totalAmount.toLocaleString()}원 +
+
+ {createdAt} +
+
+
+ 주문 복구 +
+
+
+ )} {/* CookedModal 오버레이 */} {showCookedModal && ( @@ -351,10 +427,6 @@ const CookedCard = ({ > diff --git a/apps/nowait-admin/src/pages/AdminOrders/OrderPageModal.tsx b/apps/nowait-admin/src/pages/AdminOrders/OrderPageModal.tsx index 0bceba3f..c7a322dc 100644 --- a/apps/nowait-admin/src/pages/AdminOrders/OrderPageModal.tsx +++ b/apps/nowait-admin/src/pages/AdminOrders/OrderPageModal.tsx @@ -97,25 +97,31 @@ const PaymentCheckModal = ({ ); }; -// Cooked Modal -interface CookedModalProps { +// Order Status Update Modal +interface OrderStatusModalProps { orderId: number; - tableNumber: number; - depositorName: string; - totalAmount: number; - timeText: string; + title: string; + description: string; + targetStatus: "COOKING" | "COOKED"; onClose: () => void; onSuccess?: () => void; } -const CookedModal = ({ orderId, onClose, onSuccess }: CookedModalProps) => { +const OrderStatusModal = ({ + orderId, + title, + description, + targetStatus, + onClose, + onSuccess, +}: OrderStatusModalProps) => { const updateOrderStatus = useUpdateOrderStatus(); const handleConfirm = async () => { try { await updateOrderStatus.mutateAsync({ orderId, - status: "COOKING", + status: targetStatus, }); onSuccess?.(); onClose(); @@ -124,17 +130,16 @@ const CookedModal = ({ orderId, onClose, onSuccess }: CookedModalProps) => { // 에러 처리 (예: 토스트 메시지 표시) } }; + return (
e.stopPropagation()} >
-
- 주문을 복구할까요? -
+
{title}
-
주문 상태가 조리 중으로 변경됩니다.
+
{description}
@@ -158,5 +163,37 @@ const CookedModal = ({ orderId, onClose, onSuccess }: CookedModalProps) => { ); }; -export { PaymentCheckModal, CookedModal }; -export type { PaymentCheckModalProps, CookedModalProps }; +interface CookedModalProps { + orderId: number; + onClose: () => void; + onSuccess?: () => void; +} + +const CookedModal = ({ orderId, onClose, onSuccess }: CookedModalProps) => ( + +); + +const CookCompleteModal = ({ + orderId, + onClose, + onSuccess, +}: CookedModalProps) => ( + +); + +export { PaymentCheckModal, CookedModal, CookCompleteModal, OrderStatusModal }; +export type { PaymentCheckModalProps, CookedModalProps, OrderStatusModalProps };