From d859669c5f79809da28c6bb0157b24fafad0ef36 Mon Sep 17 00:00:00 2001 From: "Mr. David" Date: Sun, 11 Aug 2024 20:21:43 +0200 Subject: [PATCH 1/3] allow seller export datat --- package-lock.json | 122 +++++++++++++++++++++++++++ package.json | 3 + src/assets/styles/SellerLayout.scss | 40 ++++++--- src/pages/seller/SellerDashboard.tsx | 23 +++-- src/utils/excel/exportToCSV.ts | 48 +++++++++++ src/utils/excel/exportToExcel.ts | 42 +++++++++ 6 files changed, 259 insertions(+), 19 deletions(-) create mode 100644 src/utils/excel/exportToCSV.ts create mode 100644 src/utils/excel/exportToExcel.ts diff --git a/package-lock.json b/package-lock.json index e837c0f3..96a962d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "html-webpack-plugin": "^5.6.0", "jwt-decode": "^4.0.0", "mini-css-extract-plugin": "^2.9.0", + "papaparse": "^5.4.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-helmet": "^6.1.0", @@ -38,6 +39,7 @@ "recharts": "^2.12.7", "save-dev": "0.0.1-security", "socket.io-client": "^4.7.5", + "xlsx": "^0.18.5", "yup": "^1.4.0" }, "devDependencies": { @@ -65,6 +67,7 @@ "@types/jest": "^29.5.12", "@types/mini-css-extract-plugin": "^2.5.1", "@types/node": "^20.14.9", + "@types/papaparse": "^5.3.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@types/react-helmet": "^6.1.11", @@ -7904,6 +7907,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/papaparse": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz", + "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.2", "license": "MIT" @@ -8682,6 +8695,15 @@ "node": ">= 10.0.0" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/agent-base": { "version": "6.0.2", "dev": true, @@ -10604,6 +10626,19 @@ "node": ">=4" } }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chai": { "version": "4.4.1", "dev": true, @@ -10961,6 +10996,15 @@ "node": ">= 0.12.0" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "dev": true, @@ -11300,6 +11344,18 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-jest": { "version": "29.7.0", "dev": true, @@ -15247,6 +15303,15 @@ "node": ">= 0.6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fresh": { "version": "0.5.2", "dev": true, @@ -21454,6 +21519,12 @@ "dev": true, "license": "MIT" }, + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "license": "MIT" + }, "node_modules/param-case": { "version": "3.0.4", "license": "MIT", @@ -24519,6 +24590,18 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/sshpk": { "version": "1.18.0", "dev": true, @@ -26951,6 +27034,24 @@ "dev": true, "license": "MIT" }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "dev": true, @@ -27116,6 +27217,27 @@ } } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "dev": true, diff --git a/package.json b/package.json index d13b0be5..fee28865 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "html-webpack-plugin": "^5.6.0", "jwt-decode": "^4.0.0", "mini-css-extract-plugin": "^2.9.0", + "papaparse": "^5.4.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-helmet": "^6.1.0", @@ -40,6 +41,7 @@ "recharts": "^2.12.7", "save-dev": "0.0.1-security", "socket.io-client": "^4.7.5", + "xlsx": "^0.18.5", "yup": "^1.4.0" }, "devDependencies": { @@ -67,6 +69,7 @@ "@types/jest": "^29.5.12", "@types/mini-css-extract-plugin": "^2.5.1", "@types/node": "^20.14.9", + "@types/papaparse": "^5.3.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@types/react-helmet": "^6.1.11", diff --git a/src/assets/styles/SellerLayout.scss b/src/assets/styles/SellerLayout.scss index e81e0cfe..7074f724 100644 --- a/src/assets/styles/SellerLayout.scss +++ b/src/assets/styles/SellerLayout.scss @@ -341,21 +341,37 @@ } } - .monthDropDown { - .dropdown-button { - background-color: #ff7300; - color: #fff; - border: none; - padding: 5px 10px; - border-radius: 5px; - cursor: pointer; - font-size: 14px; - - .arrow-down { - margin-left: 5px; + .export-section{ + display: flex; + gap: 10px; + .monthDropDown { + .dropdown-button { + background-color: #ff7300; + color: #fff; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + + .arrow-down { + margin-left: 5px; + } } } + .export-btn{ + background-color: #0f8615; + color: #fff; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + display: flex; + gap: 4px; + } } + } } } diff --git a/src/pages/seller/SellerDashboard.tsx b/src/pages/seller/SellerDashboard.tsx index 5a3525e1..16d7be09 100644 --- a/src/pages/seller/SellerDashboard.tsx +++ b/src/pages/seller/SellerDashboard.tsx @@ -18,6 +18,8 @@ import { } from "../../store/features/product/sellerCollectionProductsSlice"; import { useNavigate } from "react-router-dom"; import productSlice from "../../store/features/product/productSlice"; +import { FaFileExcel } from "react-icons/fa"; +import exportToCSV from "../../utils/excel/exportToCSV"; const SellerDashboard = () => { const { OrderHistory, message, data, isError } = useAppSelector( @@ -52,7 +54,7 @@ const SellerDashboard = () => { const [revenue, setRevenue] = useState(0); const [percentage, setPercentage] = useState(0); const [orderPercentage, setOrderPercentage] = useState(0); - const [productPercentage,SetProductPercentage] = useState(0); + const [productPercentage, SetProductPercentage] = useState(0); useEffect(() => { dispatch(sellerGetOrderHistory()); @@ -88,7 +90,7 @@ const SellerDashboard = () => { setOrderPercentage(80) SetProductPercentage(76) } - if ( isError && OrderHistory == null && message === "No shop found"){ + if (isError && OrderHistory == null && message === "No shop found") { setNumberOfOrders(0); setNumberOfProducts(0); setOrderStats(predefinedMonths); @@ -103,7 +105,7 @@ const SellerDashboard = () => { } catch (error) { console.error("Failed to fetch data:", error); } - }, [OrderHistory,isError,message]); + }, [OrderHistory, isError, message]); const MonthDropDown = () => { const [isOpen, setIsOpen] = useState(false); @@ -158,8 +160,12 @@ const SellerDashboard = () => { selectedMonth === "All" ? orderStats : orderStats.filter( - (stat) => stat.name === selectedMonth.substring(0, 3) - ); + (stat) => stat.name === selectedMonth.substring(0, 3) + ); + + const exportStats = () => { + exportToCSV(OrderHistory) + } return ( <> @@ -174,7 +180,7 @@ const SellerDashboard = () => { { Cancelled - +
+ + +
{ + const orders = orderHistory.order; + const rows = []; + rows.push([ + 'Order ID', + 'Order Date', + 'Expected Delivery Date', + 'Order Status', + 'Payment Method', + 'Products', + 'Total Order Revenue (RWF)', + 'Shipping Status' + ]); + orders.forEach(order => { + const products = JSON.parse(order.products); + const productSummary = products.map(p => `${p.name} (${p.quantity})`).join(', '); + const totalRevenue = products.reduce((sum, p) => sum + parseFloat(p.totalPrice), 0).toFixed(2); + + rows.push([ + order.id, + new Date(order.orderDate).toLocaleString(), + new Date(order.expectedDeliveryDate).toLocaleString(), + order.status, + order.paymentMethodId, + productSummary, + totalRevenue, + order.shippingProcess + ]); + }); + + const csv = Papa.unparse(rows); + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); + const link = document.createElement('a'); + if (link.download !== undefined) { + const url = URL.createObjectURL(blob); + link.setAttribute('href', url); + link.setAttribute('download', 'seller_orders.csv'); + link.style.visibility = 'hidden'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } +}; + +export default exportToCSV; \ No newline at end of file diff --git a/src/utils/excel/exportToExcel.ts b/src/utils/excel/exportToExcel.ts new file mode 100644 index 00000000..b9a9fa64 --- /dev/null +++ b/src/utils/excel/exportToExcel.ts @@ -0,0 +1,42 @@ +/* eslint-disable */ +import * as XLSX from 'xlsx'; + +const exportToExcel = (orderHistory) => { + const orderData = orderHistory.order.map(order => { + // Parse the products string to JSON + const products = JSON.parse(order.products); + + return products.map(product => ({ + orderId: order.id, + cartId: order.cartId, + shopId: order.shopId, + orderDate: order.orderDate, + paymentMethodId: order.paymentMethodId, + status: order.status, + shippingProcess: order.shippingProcess, + expectedDeliveryDate: order.expectedDeliveryDate, + createdAt: order.createdAt, + updatedAt: order.updatedAt, + productId: product.id, + productName: product.name, + productPrice: product.price, + productDiscount: product.discount, + productQuantity: product.quantity, + productTotalPrice: product.totalPrice, + productDescription: product.description, + productImage: product.image, + })); + }).flat(); // Flatten the array of arrays + + // Convert the JSON data to a worksheet + const worksheet = XLSX.utils.json_to_sheet(orderData); + + // Create a new workbook and append the worksheet + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, 'OrderHistory'); + + // Export the workbook to an Excel file + XLSX.writeFile(workbook, 'OrderHistory.xlsx'); +}; + +export default exportToExcel; \ No newline at end of file From aa03576b09fbc9ac43d0f25bd2d51236b6e3bfd2 Mon Sep 17 00:00:00 2001 From: "Mr. David" Date: Sun, 11 Aug 2024 20:21:56 +0200 Subject: [PATCH 2/3] allow seller export data --- src/utils/excel/exportToCSV.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/excel/exportToCSV.ts b/src/utils/excel/exportToCSV.ts index 684d703f..bfdca6d1 100644 --- a/src/utils/excel/exportToCSV.ts +++ b/src/utils/excel/exportToCSV.ts @@ -42,6 +42,7 @@ const exportToCSV = (orderHistory) => { document.body.appendChild(link); link.click(); document.body.removeChild(link); + } }; From 20bf105d8bc864da8116447b60d809db6634931a Mon Sep 17 00:00:00 2001 From: "Mr. David" Date: Tue, 13 Aug 2024 11:57:42 +0200 Subject: [PATCH 3/3] added excel export --- src/pages/seller/SellerDashboard.tsx | 3 +- src/utils/excel/exportToExcel.ts | 59 +++++++++++++++------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/pages/seller/SellerDashboard.tsx b/src/pages/seller/SellerDashboard.tsx index 16d7be09..2854d26e 100644 --- a/src/pages/seller/SellerDashboard.tsx +++ b/src/pages/seller/SellerDashboard.tsx @@ -20,6 +20,7 @@ import { useNavigate } from "react-router-dom"; import productSlice from "../../store/features/product/productSlice"; import { FaFileExcel } from "react-icons/fa"; import exportToCSV from "../../utils/excel/exportToCSV"; +import exportToExcel from "../../utils/excel/exportToExcel"; const SellerDashboard = () => { const { OrderHistory, message, data, isError } = useAppSelector( @@ -164,7 +165,7 @@ const SellerDashboard = () => { ); const exportStats = () => { - exportToCSV(OrderHistory) + exportToExcel(OrderHistory) } return ( diff --git a/src/utils/excel/exportToExcel.ts b/src/utils/excel/exportToExcel.ts index b9a9fa64..4d6f267f 100644 --- a/src/utils/excel/exportToExcel.ts +++ b/src/utils/excel/exportToExcel.ts @@ -2,41 +2,46 @@ import * as XLSX from 'xlsx'; const exportToExcel = (orderHistory) => { - const orderData = orderHistory.order.map(order => { - // Parse the products string to JSON - const products = JSON.parse(order.products); + const orders = orderHistory.order; + const rows = [ + [ + 'Order ID', + 'Order Date', + 'Expected Delivery Date', + 'Order Status', + 'Payment Method', + 'Products', + 'Shipping Status' + ] + ]; + + orders.forEach(order => { + let productsInfo = ''; - return products.map(product => ({ - orderId: order.id, - cartId: order.cartId, - shopId: order.shopId, - orderDate: order.orderDate, - paymentMethodId: order.paymentMethodId, - status: order.status, - shippingProcess: order.shippingProcess, - expectedDeliveryDate: order.expectedDeliveryDate, - createdAt: order.createdAt, - updatedAt: order.updatedAt, - productId: product.id, - productName: product.name, - productPrice: product.price, - productDiscount: product.discount, - productQuantity: product.quantity, - productTotalPrice: product.totalPrice, - productDescription: product.description, - productImage: product.image, - })); - }).flat(); // Flatten the array of arrays + if (Array.isArray(order.products)) { + productsInfo = order.products.map(p => `${p.productId}(${p.status})`).join(', '); + } + + rows.push([ + order.id, + new Date(order.orderDate).toLocaleString(), + new Date(order.expectedDeliveryDate).toLocaleString(), + order.status, + order.paymentMethodId, + productsInfo, + order.shippingProcess + ]); + }); - // Convert the JSON data to a worksheet - const worksheet = XLSX.utils.json_to_sheet(orderData); + // Create a worksheet from the rows + const worksheet = XLSX.utils.aoa_to_sheet(rows); // Create a new workbook and append the worksheet const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, 'OrderHistory'); // Export the workbook to an Excel file - XLSX.writeFile(workbook, 'OrderHistory.xlsx'); + XLSX.writeFile(workbook, 'seller_orders.xlsx'); }; export default exportToExcel; \ No newline at end of file