From c3dfcba7c5c70c4604f6840686415b5212cff5c4 Mon Sep 17 00:00:00 2001 From: TaeSeung Yoo <59465914+gudusol@users.noreply.github.com> Date: Fri, 25 Oct 2024 01:47:55 +0900 Subject: [PATCH] =?UTF-8?q?[Feat/#65]=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: [관리자] 주문 정보 복사 기능 추가 * fix: [관리자] 주문 정보 복사 버튼 위치 수정 * feat: [관리자] 엑셀 다운로드 개수 확인 모달 출력 * fix: [태블릿] 텍스트 라이팅 수정 --- public/svg/ic_copy.svg | 8 ++ src/assets/svg/IcCopy.tsx | 30 +++++ src/assets/svg/index.ts | 1 + .../components/OrderTable/OrderTable.style.ts | 40 ++++++ .../components/OrderTable/OrderTable.tsx | 121 +++++++++++++++++- .../OrderInfoSection/OrderInfoSection.tsx | 2 +- 6 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 public/svg/ic_copy.svg create mode 100644 src/assets/svg/IcCopy.tsx diff --git a/public/svg/ic_copy.svg b/public/svg/ic_copy.svg new file mode 100644 index 0000000..9778629 --- /dev/null +++ b/public/svg/ic_copy.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/svg/IcCopy.tsx b/src/assets/svg/IcCopy.tsx new file mode 100644 index 0000000..c31d847 --- /dev/null +++ b/src/assets/svg/IcCopy.tsx @@ -0,0 +1,30 @@ +import type { SVGProps } from "react"; +const SvgIcCopy = (props: SVGProps) => ( + + + + + + + + +); +export default SvgIcCopy; diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts index 5fd0b10..32d5911 100644 --- a/src/assets/svg/index.ts +++ b/src/assets/svg/index.ts @@ -6,6 +6,7 @@ export { default as IcCheckedTrue } from "./IcCheckedTrue"; export { default as IcClipboardCopy } from "./IcClipboardCopy"; export { default as IcClose } from "./IcClose"; export { default as IcComplete } from "./IcComplete"; +export { default as IcCopy } from "./IcCopy"; export { default as IcDelete } from "./IcDelete"; export { default as IcDownload } from "./IcDownload"; export { default as IcFix } from "./IcFix"; diff --git a/src/pages/Admin/components/OrderTable/OrderTable.style.ts b/src/pages/Admin/components/OrderTable/OrderTable.style.ts index 964915c..a02396d 100644 --- a/src/pages/Admin/components/OrderTable/OrderTable.style.ts +++ b/src/pages/Admin/components/OrderTable/OrderTable.style.ts @@ -74,3 +74,43 @@ export const checkboxStyle = css` width: 1.6rem; height: 1.6rem; `; + +export const numberText = css` + ${flexGenerator()}; +`; + +export const copyIconStyle = (theme: Theme) => css` + display: inline-block; + width: 2.4rem; + height: 2.4rem; + padding: 0.2rem; + cursor: pointer; + border-radius: 0.4rem; + + &:hover { + background-color: ${theme.color.lightgray3}; + } +`; + +export const confrimModal = css` + ${flexGenerator("column")}; + padding: 2rem; + gap: 2rem; + width: 35rem; + + & hr { + width: 100%; + } +`; + +export const modalTitle = (theme: Theme) => css` + ${theme.font["head02-b-20"]}; +`; + +export const productText = (theme: Theme) => css` + ${theme.font["head06-b-16"]}; +`; + +export const modalNotice = (theme: Theme) => css` + ${theme.font["head06-b-16"]}; +`; diff --git a/src/pages/Admin/components/OrderTable/OrderTable.tsx b/src/pages/Admin/components/OrderTable/OrderTable.tsx index 7fd8431..5851138 100644 --- a/src/pages/Admin/components/OrderTable/OrderTable.tsx +++ b/src/pages/Admin/components/OrderTable/OrderTable.tsx @@ -2,7 +2,13 @@ import { useState } from "react"; import { buttonContainer, checkboxStyle, + confrimModal, + copyIconStyle, iconStyle, + modalNotice, + modalTitle, + numberText, + productText, sectionStyle, sectionTitle, tableHeader, @@ -11,9 +17,10 @@ import { } from "./OrderTable.style"; import { Order } from "@types"; import { usePatchDeliveryShipped } from "@apis/domains/admin/usePatchDeliveryShipped"; -import { Button } from "@components"; -import { IcCheckedTrue, IcDownload } from "@svg"; +import { Button, Modal, Toast } from "@components"; +import { IcCheckedTrue, IcCopy, IcDownload } from "@svg"; import * as XLSX from "xlsx"; +import useToast from "src/hooks/useToast"; interface OrderTableProps { orders: Order[]; @@ -22,12 +29,51 @@ interface OrderTableProps { const OrderTable = ({ orders }: OrderTableProps) => { const [selectedOrders, setSelectedOrders] = useState([]); + const [isModalOpen, setIsModalOpen] = useState(false); + + const { showToast, isToastVisible } = useToast(); + const [toastMessage, setToastMessage] = useState(""); + + const [productCount, setProductCount] = useState>({}); + const { mutate } = usePatchDeliveryShipped(); const handleShippedClick = () => { mutate(selectedOrders); }; + const handleModalClose = () => { + setIsModalOpen(false); + }; + + const handleExcelClick = () => { + if (selectedOrders.length === 0) { + setToastMessage("주문을 선택해주세요."); + showToast(); + return; + } + const selectedData = orders.filter((order) => + selectedOrders.includes(order.deliveryId) + ); + + const count = selectedData + .map((order) => order.productList) + .flat() + .reduce((acc, product) => { + const match = product.match(/(.+)\s(\d+)EA$/); + if (match) { + const productName = match[1].trim(); + const quantity = parseInt(match[2], 10); + + acc[productName] = (acc[productName] || 0) + quantity; + } + return acc; + }, {} as Record); + + setIsModalOpen(true); + setProductCount(count); + }; + const exportToExcel = () => { const selectedData = orders.filter((order) => selectedOrders.includes(order.deliveryId) @@ -75,6 +121,8 @@ const OrderTable = ({ orders }: OrderTableProps) => { link.href = url; link.download = "export.xlsx"; link.click(); + + handleModalClose(); }; const handleCheckboxChange = (id: number) => { @@ -98,6 +146,37 @@ const OrderTable = ({ orders }: OrderTableProps) => { } }; + const handleCopyClick = async (order: Order) => { + const orderInfoData = `[접수날짜] +${order.orderReceivedDate} + +[보내는 분] +${order.senderName} +${order.senderPhone} + +[받는 분] +${order.recipientName} +${order.recipientPhone} +${order.recipientAddress} ${order.recipientAddressDetail} + +[상품] +${order.productList.join(", ")}`; + + if (navigator.clipboard) { + try { + await navigator.clipboard.writeText(orderInfoData); + setToastMessage("주문내역이 복사되었어요."); + showToast(); + } catch { + setToastMessage("클립보드 복사 실패"); + showToast(); + } + } else { + setToastMessage("클립보드에 복사할 수 없습니다."); + showToast(); + } + }; + const isAllSelected = selectedOrders.length > 0 && selectedOrders.length === @@ -111,7 +190,7 @@ const OrderTable = ({ orders }: OrderTableProps) => { 선택 발송완료 - @@ -158,7 +237,17 @@ const OrderTable = ({ orders }: OrderTableProps) => { /> {order.orderReceivedDate} - {order.orderNumber} + +
+ {order.orderNumber} + handleCopyClick(order)} + > + + +
+ {order.productList.map((product) => { return
{product}
; @@ -176,6 +265,30 @@ const OrderTable = ({ orders }: OrderTableProps) => { + {isModalOpen && ( + +
+

{`총 ${selectedOrders.length}개의 주문을 선택했습니다.`}

+
+ {Object.entries(productCount).map(([productName, count]) => ( +
+ {`${productName}: `} + {`${count} 개`} +
+ ))} +
+

이대로 엑셀을 다운로드 하시겠습니까?

+ +
+
+ )} + + {toastMessage} + ); }; diff --git a/src/pages/orderCheck/components/OrderInfoSection/OrderInfoSection.tsx b/src/pages/orderCheck/components/OrderInfoSection/OrderInfoSection.tsx index d869760..04ad392 100644 --- a/src/pages/orderCheck/components/OrderInfoSection/OrderInfoSection.tsx +++ b/src/pages/orderCheck/components/OrderInfoSection/OrderInfoSection.tsx @@ -63,7 +63,7 @@ const OrderInfoSection = () => { {orderInfo?.senderName}
- {`상품 (${orderCount}건의 주문)`} + {`상품 (총 ${orderCount}개)`} {(mergedOrders || []).map((order, i) => (