diff --git a/src/components/adReport/adContainer/AdContainer.tsx b/src/components/adReport/adContainer/AdContainer.tsx index d77b599..3416535 100644 --- a/src/components/adReport/adContainer/AdContainer.tsx +++ b/src/components/adReport/adContainer/AdContainer.tsx @@ -1,22 +1,26 @@ -import Pagination from "@mui/material/Pagination"; +import { useState } from "react"; import AdChart from "../../graphs/adChart/AdChart"; import AdReportTable from "../../tables/adReportTable/AdReportTable"; -import "./_adContainer.style.scss"; +import AdFilter from "../adFilter/AdFilter"; +import styles from "./adContainer.module.scss"; +import { useAdQuery } from "./useAdQuery"; const AdContainer = () => { + const [date, setDate] = useState(""); + const [productNumber, setProductNumber] = useState(null); + + const adData = useAdQuery(date, productNumber); + return (
-

광고 보고서

-
-
+
+ +

3개월 간 광고 키워드 클릭 수 & 전환 수

- -
- -
- +
+
); diff --git a/src/components/adReport/adContainer/_adContainer.style.scss b/src/components/adReport/adContainer/adContainer.module.scss similarity index 85% rename from src/components/adReport/adContainer/_adContainer.style.scss rename to src/components/adReport/adContainer/adContainer.module.scss index 84808b8..1271b72 100644 --- a/src/components/adReport/adContainer/_adContainer.style.scss +++ b/src/components/adReport/adContainer/adContainer.module.scss @@ -5,6 +5,7 @@ display: flex; flex-direction: column; align-items: center; + padding-bottom: 175px; .chart { width: 83vw; display: flex; @@ -14,9 +15,6 @@ background: $bg_white; padding: 30px 0 10px 0; border-radius: 5px; + margin-top: 40px; } } -.pagination { - margin-top: 40px; - padding-bottom: 40px; -} diff --git a/src/components/adReport/adContainer/useAdQuery.ts b/src/components/adReport/adContainer/useAdQuery.ts new file mode 100644 index 0000000..d99cfff --- /dev/null +++ b/src/components/adReport/adContainer/useAdQuery.ts @@ -0,0 +1,12 @@ +import { useQuery } from "react-query"; +import { getAdReport } from "../../../shared/apis/api"; + +export const useAdQuery = (date: string, productNumber: string) => { + return useQuery( + ["sales", { date: date, productNumber: productNumber }], + () => getAdReport(date, productNumber), + { + enabled: !!date || !!productNumber, + } + ); +}; diff --git a/src/components/adReport/adFilter/AdFilter.tsx b/src/components/adReport/adFilter/AdFilter.tsx new file mode 100644 index 0000000..06905f5 --- /dev/null +++ b/src/components/adReport/adFilter/AdFilter.tsx @@ -0,0 +1,47 @@ +import { useEffect, useMemo, useState } from "react"; + +import styles from "./adFilter.module.scss"; +import DatePicker from "../../../elements/datePicker/DatePicker"; +import { MediumDropdown } from "../../../elements/dropdown/DropDown"; +import { useProductQuery } from "./useProductQuery"; +import { DataType, PropsType } from "./adFilter.type"; + +const AdFilter = (props: PropsType) => { + const [selected, setSelected] = useState(null); + const [selectedDate, setSelectedDate] = useState(""); + + const result = useProductQuery(selectedDate); + + const { productData } = useMemo( + () => ({ + productData: result.data?.data.products, + }), + [result] + ); + + useEffect(() => { + props.setProductNumber(selected && selected.slice(-13)); + props.setDate(selectedDate); + }, [selected, selectedDate]); + + let productList: string[] = []; + productData && + productData.map((item: DataType) => { + productList.push(`${item.p_Name} / ${item.p_No}`); + }); + + return ( +
+ + +
+ ); +}; + +export default AdFilter; diff --git a/src/components/adReport/adFilter/adFilter.module.scss b/src/components/adReport/adFilter/adFilter.module.scss new file mode 100644 index 0000000..3571f7a --- /dev/null +++ b/src/components/adReport/adFilter/adFilter.module.scss @@ -0,0 +1,11 @@ +@import "../../../styles/colors"; + +.filterWrapper { + margin-top: 16px; + width: 83vw; + background: $bg_white; + border-radius: 5px; + padding: 20px 24px; + display: flex; + gap: 32px; +} diff --git a/src/components/adReport/adFilter/adFilter.type.ts b/src/components/adReport/adFilter/adFilter.type.ts new file mode 100644 index 0000000..dcb2027 --- /dev/null +++ b/src/components/adReport/adFilter/adFilter.type.ts @@ -0,0 +1,10 @@ +export interface PropsType { + setDate: (date: string) => void; + setProductNumber: (productNumber: string) => void; +} +export interface DataType { + p_Cnt: number; + p_Name: string; + p_No: number; + p_Price: number; +} diff --git a/src/components/adReport/adFilter/useProductQuery.ts b/src/components/adReport/adFilter/useProductQuery.ts new file mode 100644 index 0000000..56e4f9e --- /dev/null +++ b/src/components/adReport/adFilter/useProductQuery.ts @@ -0,0 +1,6 @@ +import { useQuery } from "react-query"; +import { getSalesReport } from "../../../shared/apis/api"; + +export const useProductQuery = (date: string) => { + return useQuery("sales", () => getSalesReport(null, date)); +}; diff --git a/src/components/graphs/adChart/AdChart.tsx b/src/components/graphs/adChart/AdChart.tsx index 6869eb6..4df54d6 100644 --- a/src/components/graphs/adChart/AdChart.tsx +++ b/src/components/graphs/adChart/AdChart.tsx @@ -1,21 +1,25 @@ import Chart from "react-apexcharts"; -import { testData } from "./test"; -import "./_adChart.style.scss"; +import { PropsType } from "./adChart.type"; + +const AdChart = (props: PropsType) => { + const dataList = props?.dataList; -const AdChart = () => { let keywordList: string[] = []; - testData.map((x) => { - keywordList.push(x.keyword); - }); + dataList && + dataList.map((x) => { + keywordList.push(x.keyword); + }); let clickCntList: number[] = []; - testData.map((x) => { - clickCntList.push(x.k_Click); - }); + dataList && + dataList.map((x) => { + clickCntList.push(x.k_Click); + }); let conversionCntList: number[] = []; - testData.map((x) => { - conversionCntList.push(x.k_Trans); - }); + dataList && + dataList.map((x) => { + conversionCntList.push(x.k_Trans); + }); let options = { xaxis: { diff --git a/src/components/graphs/adChart/_adChart.style.scss b/src/components/graphs/adChart/_adChart.style.scss deleted file mode 100644 index 7e439d5..0000000 --- a/src/components/graphs/adChart/_adChart.style.scss +++ /dev/null @@ -1 +0,0 @@ -@import "../../../styles/colors"; diff --git a/src/components/graphs/adChart/adChart.type.ts b/src/components/graphs/adChart/adChart.type.ts new file mode 100644 index 0000000..0d1d7b5 --- /dev/null +++ b/src/components/graphs/adChart/adChart.type.ts @@ -0,0 +1,9 @@ +export interface PropsType { + dataList?: { + keyword: string; + k_Click: number; + k_Cost: number; + k_Trans: number; + p_Price: number; + }[]; +} diff --git a/src/components/prodReport/prodContainer/ProdContainer.tsx b/src/components/prodReport/prodContainer/ProdContainer.tsx index 51c8418..dacc077 100644 --- a/src/components/prodReport/prodContainer/ProdContainer.tsx +++ b/src/components/prodReport/prodContainer/ProdContainer.tsx @@ -4,16 +4,13 @@ import styles from "./prodContainer.module.scss"; import ProdReportTable from "../../tables/prodReportTable/ProdReportTable"; import ProdFilter from "../prodFilter/ProdFilter"; import { useSalesQuery } from "./useSalesQuery"; -import { useDate } from "./useDate"; const ProdContainer = () => { const [category, setCategory] = useState(null); const [date, setDate] = useState(null); - const todayDate = useDate(); - // axios GET 보고서 데이터 조회 - const salesData = useSalesQuery(category, !date ? todayDate : date); + const salesData = useSalesQuery(category, date); return (
diff --git a/src/components/prodReport/prodContainer/useDate.ts b/src/components/prodReport/prodContainer/useDate.ts deleted file mode 100644 index b4a60f6..0000000 --- a/src/components/prodReport/prodContainer/useDate.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const useDate = () => { - const today = new Date(); - const year = today.getFullYear(); - const month = ("0" + (today.getMonth() + 1)).slice(-2); - const minMonth = "0" + (today.getMonth() - 3); - const day = ("0" + today.getDate()).slice(-2); - return `[${year}-${minMonth}-${day},${year}-${month}-${day}]`; -}; diff --git a/src/components/prodReport/prodFilter/ProdFilter.tsx b/src/components/prodReport/prodFilter/ProdFilter.tsx index 1d2bbe3..14a5ff0 100644 --- a/src/components/prodReport/prodFilter/ProdFilter.tsx +++ b/src/components/prodReport/prodFilter/ProdFilter.tsx @@ -9,7 +9,7 @@ import { PropsType } from "./prodFilter.type"; const ProdFilter = (props: PropsType) => { const category = useCategory(); const [selected, setSelected] = useState("전체"); - const [selectedDate, setSelectedDate] = useState(null); + const [selectedDate, setSelectedDate] = useState(""); useEffect(() => { props.setCategory(selected === "전체" ? null : selected); diff --git a/src/components/setAd/adButton/AdButton.tsx b/src/components/setAd/adButton/AdButton.tsx index ecdca3a..b8ec4fa 100644 --- a/src/components/setAd/adButton/AdButton.tsx +++ b/src/components/setAd/adButton/AdButton.tsx @@ -1,5 +1,5 @@ import { ProdDelBtn, SmallGrayBtn } from "../../../elements/buttons/Buttons"; -import styles from "./adButton.module.scss"; +import styles from "./adbutton.module.scss"; import { SetStateAction, Dispatch } from "react"; interface PropsType { diff --git a/src/components/setParcel/parcelFilter/ParcelFilter.tsx b/src/components/setParcel/parcelFilter/ParcelFilter.tsx new file mode 100644 index 0000000..44e3707 --- /dev/null +++ b/src/components/setParcel/parcelFilter/ParcelFilter.tsx @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; + +import DatePicker from "../../../elements/datePicker/DatePicker"; +import { MediumDropdown } from "../../../elements/dropdown/DropDown"; +import styles from "./parcelFilter.module.scss"; +import { PropsType } from "./parcelFilter.type"; +import { useProductQuery } from "./useProductQuery"; + +const ParcelFilter = (props: PropsType) => { + const [selectedDate, setSelectedDate] = useState(null); + const [selectedProduct, setSelectedProduct] = useState(null); + const [selectedStatus, setSelectedStatus] = useState(null); + + useEffect(() => { + props.setDate(selectedDate); + props.setProduct(selectedProduct); + props.setStatus(selectedStatus); + }, [selectedDate, selectedProduct, selectedStatus]); + + const productList = useProductQuery(); + + return ( +
+ + + +
+ ); +}; + +export default ParcelFilter; diff --git a/src/components/setParcel/parcelFilter/parcelFilter.module.scss b/src/components/setParcel/parcelFilter/parcelFilter.module.scss new file mode 100644 index 0000000..3571f7a --- /dev/null +++ b/src/components/setParcel/parcelFilter/parcelFilter.module.scss @@ -0,0 +1,11 @@ +@import "../../../styles/colors"; + +.filterWrapper { + margin-top: 16px; + width: 83vw; + background: $bg_white; + border-radius: 5px; + padding: 20px 24px; + display: flex; + gap: 32px; +} diff --git a/src/components/setParcel/parcelFilter/parcelFilter.type.ts b/src/components/setParcel/parcelFilter/parcelFilter.type.ts new file mode 100644 index 0000000..95b3e8e --- /dev/null +++ b/src/components/setParcel/parcelFilter/parcelFilter.type.ts @@ -0,0 +1,5 @@ +export interface PropsType { + setDate: (date: string) => void; + setProduct: (product: string) => void; + setStatus: (status: string) => void; +} diff --git a/src/components/setParcel/parcelFilter/useProductQuery.ts b/src/components/setParcel/parcelFilter/useProductQuery.ts new file mode 100644 index 0000000..eab9676 --- /dev/null +++ b/src/components/setParcel/parcelFilter/useProductQuery.ts @@ -0,0 +1,11 @@ +import { useQuery } from "react-query"; +import { getAd } from "../../../shared/apis/api"; + +export const useProductQuery = () => { + const { data } = useQuery("products", () => getAd()); + let productList: string[] = []; + data?.data.productList.map(({ p_Name }: { p_No: number; p_Name: string }) => { + productList.push(p_Name); + }); + return productList; +}; diff --git a/src/components/setParcel/searchBar/SearchBar.tsx b/src/components/setParcel/searchBar/SearchBar.tsx deleted file mode 100644 index 691b67f..0000000 --- a/src/components/setParcel/searchBar/SearchBar.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import styles from "./searchBar.module.scss"; - -const SearchBar = () => { - return
; -}; - -export default SearchBar; diff --git a/src/components/setParcel/searchBar/searchBar.module.scss b/src/components/setParcel/searchBar/searchBar.module.scss deleted file mode 100644 index 7e439d5..0000000 --- a/src/components/setParcel/searchBar/searchBar.module.scss +++ /dev/null @@ -1 +0,0 @@ -@import "../../../styles/colors"; diff --git a/src/components/setParcel/serviceContainer/ServiceContainer.tsx b/src/components/setParcel/serviceContainer/ServiceContainer.tsx index 4f0d6e1..d5e4af0 100644 --- a/src/components/setParcel/serviceContainer/ServiceContainer.tsx +++ b/src/components/setParcel/serviceContainer/ServiceContainer.tsx @@ -1,49 +1,55 @@ +import { useMemo, useState } from "react"; import Pagination from "@mui/material/Pagination"; -import { CSVLink } from "react-csv"; import styles from "./serviceContainer.module.scss"; -import { ReactComponent as Download } from "../../../assets/lcon/download.svg"; import ServiceTable from "../../tables/serviceTable/ServiceTable"; -import SearchBar from "../searchBar/SearchBar"; -import { useExcel } from "./useExcel"; -import { SmallGrayBtn } from "../../../elements/buttons/Buttons"; +import { useOrderListQuery } from "./useOrderListQuery"; +import ParcelFilter from "../parcelFilter/ParcelFilter"; const ServiceContainer = () => { - const data = useExcel(); - const headers = [ - { label: "주문번호", key: "orderNo" }, - { label: "상품번호", key: "prodNo" }, - { label: "상품명", key: "prodName" }, - { label: "수량", key: "prodQty" }, - { label: "아이디", key: "userId" }, - { label: "수령인", key: "userName" }, - { label: "주소", key: "address" }, - { label: "연락처", key: "phone" }, - { label: "배송메시지", key: "comment" }, - { label: "주문일자", key: "orderDate" }, - { label: "배송상태", key: "status" }, - ]; + const [page, setPage] = useState(1); + const [date, setDate] = useState(null); + const [product, setProduct] = useState("전체"); + const [status, setStatus] = useState("전체"); + + // 배송 리스트 조회 + const orderData = useOrderListQuery( + String(page), + "10", + date, + product === "전체" ? null : product, + status === "전체" ? null : product + ); + + const { orderList, dataQty } = useMemo( + () => ({ + orderList: orderData.data?.data.list, + dataQty: orderData.data?.data.cnt, + }), + [orderData] + ); + + // 페이지네이션 + const handleChange = (event: React.ChangeEvent, value: number) => { + setPage(value); + }; + return (
- -
- 주문확정 - -
- + +
- +
diff --git a/src/components/setParcel/serviceContainer/serviceContainer.module.scss b/src/components/setParcel/serviceContainer/serviceContainer.module.scss index 9d64d69..e52647d 100644 --- a/src/components/setParcel/serviceContainer/serviceContainer.module.scss +++ b/src/components/setParcel/serviceContainer/serviceContainer.module.scss @@ -6,33 +6,7 @@ flex-direction: column; align-items: center; } -.buttonWrapper { - width: 83vw; - display: flex; - justify-content: flex-end; - align-items: center; - gap: 16px; - button { - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - &.download { - width: 130px; - height: 40px; - border: 1px solid $lc_blue400; - border-radius: 5px; - background: $bg_white; - text-decoration: none; - } - } -} .pagination { margin-top: 40px; padding-bottom: 40px; } -a { - text-decoration: none; - color: $fc_blue400; - font-weight: 500; -} diff --git a/src/components/setParcel/serviceContainer/useExcel.ts b/src/components/setParcel/serviceContainer/useExcel.ts deleted file mode 100644 index aeba5a1..0000000 --- a/src/components/setParcel/serviceContainer/useExcel.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { testData } from "../../tables/serviceTable/test"; - -interface ListType { - orderNo: Number; - prodNo: number; - prodName: string; - prodQty: number; - userId: string; - userName: string; - address: string; - phone: string; - comment: string; - orderDate: string; - status: string; -} - -export function useExcel() { - let orderList: ListType[] = []; - testData.map((list) => { - const newOrderList: ListType = { - orderNo: list.o_No, - prodNo: list.p_No, - prodName: list.p_Name, - prodQty: list.p_Cnt, - userId: list.u_Id, - userName: list.d_Name, - address: list.d_Address1 + list.d_Address2, - phone: list.d_Phone, - comment: list.d_Memo, - orderDate: list.o_Date, - status: list.o_Status, - }; - orderList.push(newOrderList); - }); - return orderList; -} diff --git a/src/components/setParcel/serviceContainer/useOrderListQuery.ts b/src/components/setParcel/serviceContainer/useOrderListQuery.ts new file mode 100644 index 0000000..f60c6fd --- /dev/null +++ b/src/components/setParcel/serviceContainer/useOrderListQuery.ts @@ -0,0 +1,24 @@ +import { useQuery } from "react-query"; +import { getOrders } from "../../../shared/apis/api"; + +export const useOrderListQuery = ( + page: string, + postQty: string, + date: string, + product: string, + status: string +) => { + return useQuery( + [ + "orders", + { + page: page, + postQty: postQty, + date: date, + product: product, + status: status, + }, + ], + () => getOrders(page, postQty, date, product, status) + ); +}; diff --git a/src/components/tables/adReportTable/AdReportTable.tsx b/src/components/tables/adReportTable/AdReportTable.tsx index 604a8f3..6e11d62 100644 --- a/src/components/tables/adReportTable/AdReportTable.tsx +++ b/src/components/tables/adReportTable/AdReportTable.tsx @@ -1,7 +1,7 @@ -import "./_adReportTable.style.scss"; -import { testData } from "../../graphs/adChart/test"; +import styles from "./adReportTable.module.scss"; +import { PropsType } from "./adReportTable.type"; -const AdReportTable = () => { +const AdReportTable = (props: PropsType) => { const tHeadList = [ "NO", "키워드", @@ -14,30 +14,54 @@ const AdReportTable = () => { "수익율", ]; return ( - - +
+ {tHeadList.map((x, idx) => { - return ; + return ( + + ); })} - - {testData.map((x) => { - return ( - - - - - - - - - - - - ); - })} + + {props.dataList && props.dataList.length ? ( + props.dataList.map((x, idx) => { + return ( + + + + + + + + + + + + ); + }) + ) : ( + + + + + + + + + + + + )}
{x} + {x} +
{x.id}{x.keyword}{x.k_Click}{Math.round(x.k_Cost / x.k_Click).toLocaleString()}원{x.k_Cost.toLocaleString()}원{x.k_Trans}{Math.round((x.k_Trans / x.k_Click) * 100)}%{x.p_Price.toLocaleString()}원{Math.round((x.p_Price / x.k_Cost) * 100)}%
{idx + 1}{x.keyword}{x.k_Click} + {Math.round(x.k_Cost / x.k_Click).toLocaleString()}원 + {x.k_Cost.toLocaleString()}원{x.k_Trans} + {Math.round((x.k_Trans / x.k_Click) * 100)}% + {x.p_Price.toLocaleString()}원 + {Math.round((x.p_Price / x.k_Cost) * 100)}% +
000000000000000000등록된 상품이 없습니다.00000000000000원0000000
); diff --git a/src/components/tables/adReportTable/_adReportTable.style.scss b/src/components/tables/adReportTable/adReportTable.module.scss similarity index 81% rename from src/components/tables/adReportTable/_adReportTable.style.scss rename to src/components/tables/adReportTable/adReportTable.module.scss index ccce40f..2349c32 100644 --- a/src/components/tables/adReportTable/_adReportTable.style.scss +++ b/src/components/tables/adReportTable/adReportTable.module.scss @@ -1,19 +1,18 @@ @import "../../../styles/colors"; -table { +.tableContainer { width: 83vw; - height: 300px; - margin-top: 16px; + margin-top: 40px; border-radius: 5px; border-collapse: collapse; background: $bg_white; } -thead { +.tHead { width: 83vw; height: 50px; font-weight: 700; background: $bg_gray300; - th { + .th { font-size: 12px; height: 50px; &:first-child { @@ -24,16 +23,19 @@ thead { } } } -tbody { - tr { +.tBody { + .tr { border-bottom: 1px solid $lc_gray300; &:last-child { border-radius: 0px 0px 5px 5px; border-bottom: none; } - td { + .td { font-size: 12px; text-align: center; + &.hidden { + color: $fc_white; + } } } } diff --git a/src/components/tables/adReportTable/adReportTable.type.ts b/src/components/tables/adReportTable/adReportTable.type.ts new file mode 100644 index 0000000..0d1d7b5 --- /dev/null +++ b/src/components/tables/adReportTable/adReportTable.type.ts @@ -0,0 +1,9 @@ +export interface PropsType { + dataList?: { + keyword: string; + k_Click: number; + k_Cost: number; + k_Trans: number; + p_Price: number; + }[]; +} diff --git a/src/components/tables/serviceTable/ServiceTable.tsx b/src/components/tables/serviceTable/ServiceTable.tsx index 456fef6..86a7c8d 100644 --- a/src/components/tables/serviceTable/ServiceTable.tsx +++ b/src/components/tables/serviceTable/ServiceTable.tsx @@ -1,20 +1,53 @@ import { useState } from "react"; import Tooltip from "@mui/material/Tooltip"; +import { CSVLink } from "react-csv"; -import "./_serviceTable.style.scss"; -import { testData } from "./test"; +import styles from "./serviceTable.module.scss"; +import { ReactComponent as Download } from "../../../assets/lcon/download.svg"; +import { SmallGrayBtn } from "../../../elements/buttons/Buttons"; +import { PropsType } from "./serviceTable.type"; +import { useHeaderList } from "./useHeaderList"; +import { useExcelData } from "./useExcelData"; +import { useExcelQuery } from "./useExcelQuery"; +import { useExcelHeaderList } from "./useExcelHeaderList"; +import { useOrderConfirmQuery } from "./useOrderConfirmQuery"; +import { putOrderConfirm } from "../../../shared/apis/api"; +import { useMutation, useQueryClient } from "react-query"; + +const ServiceTable = (props: PropsType) => { + const [confirmList, setConfirmList] = useState([]); + + // 엑셀 데이터 + const headers = useExcelHeaderList(); + const { data } = useExcelQuery( + "0", + "0", + props.date, + props.product, + props.status + ); + const excelData = useExcelData(data?.data.list); -const ServiceTable = () => { // 체크박스 상태 관리 const [checkItems, setCheckItems] = useState([]); // 체크박스 단일 선택 - const handleSingleCheck = (checked: boolean, id: number) => { + const handleSingleCheck = ( + checked: boolean, + id: number, + orderNumber: number, + productNumber: number + ) => { if (checked) { setCheckItems([...checkItems, id]); + setConfirmList([ + ...confirmList, + { o_No: orderNumber, p_No: productNumber }, + ]); } else { // 선택 해제 setCheckItems(checkItems.filter((el) => el !== id)); + setConfirmList(confirmList.filter((o_No) => o_No !== orderNumber)); } }; @@ -22,84 +55,148 @@ const ServiceTable = () => { const handleAllCheck = (checked: boolean) => { if (checked) { let idArr: number[] = []; + let confirmList: { o_No: number; p_No: number }[] = []; // 전체 체크 박스 체크 - testData.forEach((el) => idArr.push(el.id)); + props?.orderList.forEach((el) => { + idArr.push(el.id); + confirmList.push({ o_No: el.o_No, p_No: el.p_No }); + }); setCheckItems(idArr); + setConfirmList(confirmList); } // 전체 체크 박스 체크 해제 else { setCheckItems([]); + setConfirmList([]); } }; // 테이블 제목 리스트 - const tHeadList = [ - "No", - "주문번호", - "상품번호", - "상품명", - "수량", - "아이디", - "수령인", - "주소", - "연락처", - "배송메시지", - "주문일자", - "배송상태", - ]; + const headerList = useHeaderList(); + + // 주문 확정 + const queryClient = useQueryClient(); + const { mutate } = useMutation(() => putOrderConfirm(confirmList), { + onSuccess: () => { + queryClient.invalidateQueries("orders"); + setConfirmList([]); + setCheckItems([]); + }, + onError: ({ response }) => { + console.log(response.data.errorMessage); + setConfirmList([]); + setCheckItems([]); + }, + }); return ( - - - - - {tHeadList.map((x, idx) => { - return ; - })} - - - - {testData.map((x) => { - return ( - - - - - - - - - - - - - - +
+
+ 주문확정 + +
+
- handleAllCheck(e.target.checked)} - checked={checkItems.length === testData.length ? true : false} - /> - {x}
- handleSingleCheck(e.target.checked, x.id)} - checked={checkItems.includes(x.id) ? true : false} - /> - {x.id}{x.o_No}{x.p_No} - - {x.p_Name} - - {x.p_Cnt}{x.u_Id}{x.d_Name} - - {x.d_Address1} - - {x.d_Phone}{x.d_Memo}{x.o_Date}{x.o_Status}
+ + + + {headerList.map((header, idx) => { + return ; + })} + + + + {props.orderList && props.orderList.length ? ( + props.orderList.map((x) => { + return ( + + + + + + + + + + + + + + + + ); + }) + ) : ( + + + + + + + + + + + + + + - ); - })} - -
+ handleAllCheck(e.target.checked)} + checked={ + checkItems.length === + (props.orderList && props.orderList.length) + ? true + : false + } + /> + {header}
+ + handleSingleCheck( + e.target.checked, + x.id, + x.o_No, + x.p_No + ) + } + checked={checkItems.includes(x.id) ? true : false} + /> + {x.id}{x.o_No}{x.p_No} + { + return `${option[0] ? option[0] : ""}${ + option[1] ? option[1] : "" + }X${option[3]} `; + })} + arrow + > + {x.p_Name} + + {x.p_Cnt}{x.u_Id}{x.d_Name} + + {x.d_Address2} + + {x.d_Phone}{x.d_Memo}{x.o_Date}{x.o_Status}
+ )} + + +
); }; diff --git a/src/components/tables/serviceTable/_serviceTable.style.scss b/src/components/tables/serviceTable/serviceTable.module.scss similarity index 57% rename from src/components/tables/serviceTable/_serviceTable.style.scss rename to src/components/tables/serviceTable/serviceTable.module.scss index af66e2e..e41305a 100644 --- a/src/components/tables/serviceTable/_serviceTable.style.scss +++ b/src/components/tables/serviceTable/serviceTable.module.scss @@ -1,18 +1,39 @@ @import "../../../styles/colors"; -table { +.buttonWrapper { + width: 83vw; + display: flex; + justify-content: flex-end; + align-items: center; + gap: 16px; + margin-top: 40px; + button { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + &.download { + width: 130px; + height: 40px; + border: 1px solid $lc_blue400; + border-radius: 5px; + background: $bg_white; + text-decoration: none; + } + } +} +.tableContainer { width: 83vw; - height: 550px; margin-top: 16px; border-radius: 5px; border-collapse: collapse; background: $bg_white; } -thead { +.tHead { width: 83vw; font-weight: 700; background: $bg_gray300; - th { + .th { font-size: 12px; height: 50px; &:first-child { @@ -27,7 +48,7 @@ thead { } } } -tbody { +.tBody { tr { border-bottom: 1px solid $lc_gray300; &:last-child { @@ -45,3 +66,8 @@ tbody { } } } +a { + text-decoration: none; + color: $fc_blue400; + font-weight: 500; +} diff --git a/src/components/tables/serviceTable/serviceTable.type.ts b/src/components/tables/serviceTable/serviceTable.type.ts new file mode 100644 index 0000000..3d3404e --- /dev/null +++ b/src/components/tables/serviceTable/serviceTable.type.ts @@ -0,0 +1,23 @@ +export interface PropsType { + orderList: { + d_Address1: string; + d_Address2: string; + d_Address3: string; + d_Memo: string; + d_Name: string; + d_Phone: string; + id: number; + o_Date: string; + o_No: number; + o_Status: string; + p_Cnt: number; + p_Name: string; + p_No: number; + p_Option: (string | number)[][]; + p_Price: number; + u_Id: string; + }[]; + date: string; + product: string; + status: string; +} diff --git a/src/components/tables/serviceTable/test.ts b/src/components/tables/serviceTable/test.ts deleted file mode 100644 index 59f4757..0000000 --- a/src/components/tables/serviceTable/test.ts +++ /dev/null @@ -1,152 +0,0 @@ -export const testData = [ - { - id: 1, - o_No: 22092900001, - p_No: 2001010001, - p_Name: "오리지널고체치약 10정입", - p_Cnt: 10, - u_Id: "user01", - d_Name: "김지구", - d_Address1: "서울시 서대문구 연희로", - d_Address2: "123-1", - d_Phone: "010-1111-2222", - d_Memo: "문 앞에 두고 벨x", - o_Date: "2022-09-29", - o_Status: "신규주문", - }, - { - id: 2, - o_No: 22092900001, - p_No: 2001010002, - p_Name: " 실리콘비누받침", - p_Cnt: 5, - u_Id: "user01", - d_Name: "김지구", - d_Address1: "서울시 서대문구 연희로", - d_Address2: "123-1", - d_Phone: "010-1111-2222", - d_Memo: "문 앞에 두고 벨x", - o_Date: "2022-09-29", - o_Status: "신규주문", - }, - { - id: 3, - o_No: 22091700001, - p_No: 2001010004, - p_Name: "삼베수세미 3개입", - p_Cnt: 1, - u_Id: "userid", - d_Name: "박지구", - d_Address1: "서울시 은평구 불광로", - d_Address2: "80", - d_Phone: "010-2222-3333", - d_Memo: "", - o_Date: "2022-09-17", - o_Status: "배송완료", - }, - { - id: 4, - o_No: 22091700001, - p_No: 2001010003, - p_Name: "유기농배색그물장바구니", - p_Cnt: 12, - u_Id: "userid", - d_Name: "박지구", - d_Address1: "서울시 은평구 불광로", - d_Address2: "80", - d_Phone: "010-2222-3333", - d_Memo: "", - o_Date: "2022-09-17", - o_Status: "배송완료", - }, - { - id: 5, - o_No: 22091700001, - p_No: 2001010005, - p_Name: "접이식 실리콘 런치박스", - p_Cnt: 8, - u_Id: "userid", - d_Name: "박지구", - d_Address1: "서울시 은평구 불광로", - d_Address2: "80", - d_Phone: "010-2222-3333", - d_Memo: "", - o_Date: "2022-09-17", - o_Status: "배송완료", - }, - { - id: 6, - o_No: 22092300004, - p_No: 2001010006, - p_Name: "밀랍랩 비기너세트", - p_Cnt: 8, - u_Id: "userid", - d_Name: "한지구", - d_Address1: "서울시 강남구 테헤란로", - d_Address2: "222-111", - d_Phone: "010-8888-3333", - d_Memo: "경비실에 맡겨주세요", - o_Date: "2022-09-23", - o_Status: "주문취소", - }, - { - id: 7, - o_No: 22092300004, - p_No: 2001010007, - p_Name: "대나무롤 키친타올", - p_Cnt: 6, - u_Id: "userid", - d_Name: "한지구", - d_Address1: "서울시 강남구 테헤란로", - d_Address2: "222-111", - d_Phone: "010-8888-3333", - d_Memo: "경비실에 맡겨주세요", - o_Date: "2022-09-23", - o_Status: "주문취소", - }, - { - id: 8, - o_No: 22092500004, - p_No: 2001010008, - p_Name: "튜브짜개", - p_Cnt: 70, - u_Id: "dolphin", - d_Name: "우영우", - d_Address1: "서울시 서대문구 신촌로", - d_Address2: "66", - d_Phone: "010-5555-3333", - d_Memo: "", - o_Date: "2022-09-25", - o_Status: "배송완료", - }, - { - id: 9, - o_No: 22092500004, - p_No: 2001010009, - p_Name: "유기농코튼면봉", - p_Cnt: 6, - u_Id: "dolphin", - d_Name: "우영우", - d_Address1: "서울시 서대문구 신촌로", - d_Address2: "66", - d_Phone: "010-5555-3333", - d_Memo: "", - o_Date: "2022-09-25", - o_Status: "배송완료", - }, - { - id: 10, - o_No: 22092500004, - p_No: 2001010009, - p_Name: "유기농빨대파우치", - p_Cnt: 2, - u_Id: "dolphin", - d_Name: "우영우", - d_Address1: "서울시 서대문구 신촌로", - d_Address2: "66", - d_Phone: "010-5555-3333", - d_Memo: "", - o_Date: "2022-09-25", - o_Status: "배송완료", - }, -]; diff --git a/src/components/tables/serviceTable/useExcelData.ts b/src/components/tables/serviceTable/useExcelData.ts new file mode 100644 index 0000000..3d3a317 --- /dev/null +++ b/src/components/tables/serviceTable/useExcelData.ts @@ -0,0 +1,50 @@ +interface ExcelType { + orderNo: number; + prodNo: number; + prodName: string; + prodQty: number; + userId: string; + userName: string; + address: string; + phone: string; + comment: string; + orderDate: string; + status: string; +} +interface DataType { + o_No: number; + p_No: number; + p_Name: string; + p_Cnt: number; + u_Id: string; + d_Name: string; + d_Address1: string; + d_Address2: string; + d_Address3: string; + d_Phone: string; + d_Memo: string; + o_Date: string; + o_Status: string; +} + +export const useExcelData = (data: DataType[]) => { + let excelList: ExcelType[] = []; + data && + data.map((item: DataType) => { + const list = { + orderNo: item.o_No, + prodNo: item.p_No, + prodName: item.p_Name, + prodQty: item.p_Cnt, + userId: item.u_Id, + userName: item.d_Name, + address: `[${item.d_Address1}] ${item.d_Address2} ${item.d_Address3}`, + phone: item.d_Phone, + comment: item.d_Memo, + orderDate: item.o_Date, + status: item.o_Status, + }; + excelList.push(list); + }); + return excelList; +}; diff --git a/src/components/tables/serviceTable/useExcelHeaderList.ts b/src/components/tables/serviceTable/useExcelHeaderList.ts new file mode 100644 index 0000000..7e32ad7 --- /dev/null +++ b/src/components/tables/serviceTable/useExcelHeaderList.ts @@ -0,0 +1,15 @@ +export const useExcelHeaderList = () => { + return [ + { label: "주문번호", key: "orderNo" }, + { label: "상품번호", key: "prodNo" }, + { label: "상품명", key: "prodName" }, + { label: "수량", key: "prodQty" }, + { label: "아이디", key: "userId" }, + { label: "수령인", key: "userName" }, + { label: "주소", key: "address" }, + { label: "연락처", key: "phone" }, + { label: "배송메시지", key: "comment" }, + { label: "주문일자", key: "orderDate" }, + { label: "배송상태", key: "status" }, + ]; +}; diff --git a/src/components/tables/serviceTable/useExcelQuery.ts b/src/components/tables/serviceTable/useExcelQuery.ts new file mode 100644 index 0000000..d72f299 --- /dev/null +++ b/src/components/tables/serviceTable/useExcelQuery.ts @@ -0,0 +1,24 @@ +import { useQuery } from "react-query"; +import { getOrders } from "../../../shared/apis/api"; + +export const useExcelQuery = ( + page: string, + postQty: string, + date: string, + product: string, + status: string +) => { + return useQuery( + [ + "excel", + { + page: page, + postQty: postQty, + date: date, + product: product, + status: status, + }, + ], + () => getOrders(page, postQty, date, product, status) + ); +}; diff --git a/src/components/tables/serviceTable/useHeaderList.ts b/src/components/tables/serviceTable/useHeaderList.ts new file mode 100644 index 0000000..25cd334 --- /dev/null +++ b/src/components/tables/serviceTable/useHeaderList.ts @@ -0,0 +1,16 @@ +export const useHeaderList = () => { + return [ + "No", + "주문번호", + "상품번호", + "상품명", + "수량", + "아이디", + "수령인", + "주소", + "연락처", + "배송메시지", + "주문일자", + "배송상태", + ]; +}; diff --git a/src/components/tables/serviceTable/useOrderConfirmQuery.ts b/src/components/tables/serviceTable/useOrderConfirmQuery.ts new file mode 100644 index 0000000..47d2491 --- /dev/null +++ b/src/components/tables/serviceTable/useOrderConfirmQuery.ts @@ -0,0 +1,11 @@ +import { useMutation } from "react-query"; +import { putOrderConfirm } from "../../../shared/apis/api"; + +// 주문 확정 +export const useOrderConfirmQuery = ( + confirmList: { o_No: number; p_No: number }[] +) => + useMutation(() => putOrderConfirm(confirmList), { + onSuccess: () => {}, + onError: () => {}, + }); diff --git a/src/elements/datePicker/DatePicker.tsx b/src/elements/datePicker/DatePicker.tsx index ec498f5..64e1429 100644 --- a/src/elements/datePicker/DatePicker.tsx +++ b/src/elements/datePicker/DatePicker.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import styles from "./datePicker.module.scss"; import arrowRight from "../../assets/lcon/arrowRight.svg"; import { PropsType } from "./datePicker.type"; +import { useInitialDate } from "./useInitialDate"; const DatePicker = (props: PropsType) => { const date = new Date(); @@ -10,15 +11,17 @@ const DatePicker = (props: PropsType) => { const month = ("0" + (date.getMonth() + 1)).slice(-2); const minMonth = "0" + (date.getMonth() - 3); const day = ("0" + date.getDate()).slice(-2); - //최소일자 ,최대일자 , 현재일자 + + // 최소 선택 가능 일자(4개월 전) ,최대 선택 가능 일자(당일) const min = year + "-" + minMonth + "-" + day; const max = year + "-" + month + "-" + day; - const dateStr = year + "-" + month + "-" + day; - //현재 일자 - const [startVal, setStartVal] = useState(min); - const [endVal, setEndVal] = useState(dateStr); - const [disabled, setDisabled] = useState(true); + // 초기 데이터 + const initialDate = useInitialDate(); + + const [startVal, setStartVal] = useState(initialDate[0]); + const [endVal, setEndVal] = useState(initialDate[1]); + const [disabled, setDisabled] = useState(true); useEffect(() => { props.setSelectedDate(`[${startVal},${endVal}]`); diff --git a/src/elements/datePicker/useInitialDate.ts b/src/elements/datePicker/useInitialDate.ts new file mode 100644 index 0000000..3fff98f --- /dev/null +++ b/src/elements/datePicker/useInitialDate.ts @@ -0,0 +1,8 @@ +export const useInitialDate = () => { + const today = new Date(); + const year = today.getFullYear(); + const month1 = "0" + today.getMonth(); + const month2 = ("0" + (today.getMonth() + 1)).slice(-2); + const day = ("0" + today.getDate()).slice(-2); + return [`${year}-${month1}-${day}`, `${year}-${month2}-${day}`]; +}; diff --git a/src/pages/AdReport.tsx b/src/pages/AdReport.tsx index dff4394..ee6441c 100644 --- a/src/pages/AdReport.tsx +++ b/src/pages/AdReport.tsx @@ -1,8 +1,10 @@ import AdContainer from "../components/adReport/adContainer/AdContainer"; +import styles from "./styles/adReport.module.scss"; const AdReport = () => { return (
+

광고 보고서

); diff --git a/src/pages/styles/adReport.module.scss b/src/pages/styles/adReport.module.scss new file mode 100644 index 0000000..30c06d0 --- /dev/null +++ b/src/pages/styles/adReport.module.scss @@ -0,0 +1,10 @@ +@import "../../styles/colors"; + +.title { + display: inline-block; + padding: 24px 32px; + font: { + weight: 700; + size: 30px; + } +} diff --git a/src/shared/apis/api.ts b/src/shared/apis/api.ts index 48de230..e81e81f 100644 --- a/src/shared/apis/api.ts +++ b/src/shared/apis/api.ts @@ -61,23 +61,20 @@ export const putProducts = (p_No: number) => axiosInstance.put(`/admin-products/status/${p_No}`); // 배송관리 주문확정 -export const putOrderConfirm = (orderNumber: number, productNumber: number) => +export const putOrderConfirm = ( + confirmList: { o_No: number; p_No: number }[] +) => axiosInstance.put(`/order-list`, { - confirm: [ - { - o_No: orderNumber, - p_No: productNumber, - }, - ], + confirm: confirmList, }); - + // 배송관리 조회 export const getOrders = ( page: string, postQty: string, date: string, product: string, - status: "신규주문" | "배송완료" | "주문취소" + status: string ) => axiosInstance.get( `/order-list?page=${page}&maxpost=${postQty}&date=${date}&p_Name=${product}&o_Status=${status}`