├── src ├── pages │ ├── Main │ │ ├── Error-Page.jsx │ │ ├── Binance │ │ │ ├── Modals │ │ │ │ ├── DeleteBinanceModal.jsx │ │ │ │ ├── AddTokenModal.jsx │ │ │ │ ├── AddBinanceModal.jsx │ │ │ │ └── UpdateBinanceModal.jsx │ │ │ └── Binance.jsx │ │ ├── CombinedGiftList │ │ │ ├── Modals │ │ │ │ ├── DeleteConformationModel.jsx │ │ │ │ ├── AddProductModal.jsx │ │ │ │ └── UpdateProductModal.jsx │ │ │ └── CombinedGiftList.jsx │ │ ├── Customer │ │ │ ├── Customer.jsx │ │ │ └── CustomerTable │ │ │ │ └── CustomerTable.jsx │ │ ├── CMS │ │ │ ├── Cms.jsx │ │ │ └── CMSBLocks │ │ │ │ ├── FaqManage.jsx │ │ │ │ ├── PartnerLogoManage.jsx │ │ │ │ └── CardSliderManage.jsx │ │ ├── Unauthorized.jsx │ │ ├── Order │ │ │ ├── Modals │ │ │ │ ├── OrderDetails.css │ │ │ │ └── OrderDetails.jsx │ │ │ └── Order.jsx │ │ ├── GridView.jsx │ │ ├── BinanceOrders │ │ │ └── BinanceOrders.jsx │ │ ├── BitaqatyGiftCards.jsx │ │ ├── SidePanel.jsx │ │ ├── Collections.jsx │ │ └── Dashboard.jsx │ └── Auth │ │ ├── ForgetPassword.jsx │ │ ├── ResetPassword.jsx │ │ └── Login.jsx ├── css │ ├── side-bar.css │ ├── loader.css │ ├── dashboard.css │ ├── login.css │ └── table.css ├── main.jsx ├── util │ ├── generateId.js │ ├── Loader.jsx │ └── helper.js ├── components │ └── ProjectedRoutes.jsx ├── assets │ ├── orders-icon.svg │ ├── sales-icon.svg │ ├── cloud-logo.svg │ ├── products-icon.svg │ ├── doc-icon.svg │ ├── group-icon.svg │ ├── list-icon.svg │ ├── grid-icon.svg │ ├── delete-icon.svg │ └── react.svg ├── services │ ├── Auth.js │ ├── Order.js │ ├── Cms.js │ ├── Users.js │ ├── categories.js │ ├── Collections.js │ └── Product.js ├── index.css ├── App.css └── App.jsx ├── public ├── assets │ ├── logo1.png │ ├── logo2.png │ ├── category.png │ ├── login-bg.png │ ├── login-bg-2.png │ ├── Unauthorized.jpg │ └── default-gift-card.jpg └── vite.svg ├── vite.config.js ├── README.md ├── .eslintrc.cjs ├── index.html ├── package.json └── .gitignore /src/pages/Main/Error-Page.jsx: -------------------------------------------------------------------------------- 1 | export const ErrorPage = () => { 2 | 3 | return '' 4 | 5 | } -------------------------------------------------------------------------------- /public/assets/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/star900817/agc_binance_admin/HEAD/public/assets/logo1.png -------------------------------------------------------------------------------- /public/assets/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/star900817/agc_binance_admin/HEAD/public/assets/logo2.png -------------------------------------------------------------------------------- /public/assets/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/star900817/agc_binance_admin/HEAD/public/assets/category.png -------------------------------------------------------------------------------- /public/assets/login-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/star900817/agc_binance_admin/HEAD/public/assets/login-bg.png -------------------------------------------------------------------------------- /public/assets/login-bg-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/star900817/agc_binance_admin/HEAD/public/assets/login-bg-2.png -------------------------------------------------------------------------------- /public/assets/Unauthorized.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/star900817/agc_binance_admin/HEAD/public/assets/Unauthorized.jpg -------------------------------------------------------------------------------- /public/assets/default-gift-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/star900817/agc_binance_admin/HEAD/public/assets/default-gift-card.jpg -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/css/side-bar.css: -------------------------------------------------------------------------------- 1 | .ant-menu-item-selected { 2 | background-color: #00B6DE !important; 3 | color: white !important; 4 | } 5 | .img-container{ 6 | margin: 25px 0; 7 | } 8 | .img-container img { 9 | width: 28%; 10 | margin: 0 5px; 11 | object-fit: contain; 12 | } -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /src/pages/Main/Binance/Modals/DeleteBinanceModal.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const DeleteBinanceModal = ({ productId }) => { 4 | return ( 5 |
6 |

Are you Sure you want to Delete Product?

7 |
8 | ); 9 | }; 10 | 11 | export default DeleteBinanceModal; 12 | -------------------------------------------------------------------------------- /src/pages/Main/CombinedGiftList/Modals/DeleteConformationModel.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const DeleteConformationModel = ({ productId }) => { 4 | return ( 5 |
6 |

Are you Sure you want to Delete Product?

7 |
8 | ); 9 | }; 10 | 11 | export default DeleteConformationModel; 12 | -------------------------------------------------------------------------------- /src/util/generateId.js: -------------------------------------------------------------------------------- 1 | export const generateRandomString = (length) => { 2 | const chars = 3 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 4 | let result = ""; 5 | 6 | for (let i = 0; i < length; i++) { 7 | const randomIndex = Math.floor(Math.random() * chars.length); 8 | result += chars.charAt(randomIndex); 9 | } 10 | 11 | return result; 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/ProjectedRoutes.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Navigate } from 'react-router-dom'; 3 | 4 | const ProtectedRoute = ({ children }) => { 5 | const localstorgeToken = localStorage.getItem("admin_token"); 6 | return ( 7 |
8 | 9 | {localstorgeToken ? children : } 10 |
11 | ) 12 | }; 13 | 14 | export default ProtectedRoute; -------------------------------------------------------------------------------- /src/pages/Main/Customer/Customer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CustomerTable from "./CustomerTable/CustomerTable"; 3 | 4 | const Customer = () => { 5 | return ( 6 |
7 |
8 |

Customer List

9 |
10 | 11 |
12 | 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default Customer; 19 | -------------------------------------------------------------------------------- /src/css/loader.css: -------------------------------------------------------------------------------- 1 | .loading { 2 | text-align: center; 3 | } 4 | 5 | .svgbox { 6 | --green: #00B6DE; 7 | stroke: var(--green); 8 | stroke-width: 3; 9 | fill: none; 10 | stroke-dasharray: 50, 14; 11 | stroke-dashoffset: 192; 12 | animation: dash_682 2s linear infinite; 13 | } 14 | 15 | @keyframes dash_682 { 16 | 72.5% { 17 | opacity: 1; 18 | } 19 | 20 | to { 21 | stroke-dashoffset: 1; 22 | } 23 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /src/assets/orders-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/Main/CMS/Cms.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PartnerLogoManage from "./CMSBLocks/PartnerLogoManage"; 3 | import CardSliderManage from "./CMSBLocks/CardSliderManage"; 4 | import FaqManage from "./CMSBLocks/FaqManage"; 5 | 6 | const Cms = () => { 7 | return ( 8 |
9 |
10 |

Content Management System

11 |
12 | 13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | ); 22 | }; 23 | 24 | export default Cms; 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Admin Panel 9 | 10 | 11 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/services/Auth.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 4 | export const login = async (emailOrPhone, password) => { 5 | try { 6 | let result = ( 7 | await axios.post(`${url}/signInByAdmin`, { emailOrPhone, password }) 8 | ).data; 9 | 10 | if (result.success) { 11 | localStorage.setItem("admin_token", result.accessToken); 12 | localStorage.setItem( 13 | "accessList", 14 | JSON.stringify(result.data.userAccess) 15 | ); 16 | } 17 | 18 | return { success: result.success, message: result.message }; 19 | } catch (error) { 20 | console.log("error", error); 21 | return { success: false, message: error.response.data.message }; 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/assets/sales-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/util/Loader.jsx: -------------------------------------------------------------------------------- 1 | import '../css/loader.css'; 2 | 3 | const Loader = () => { 4 | return ( 5 |
6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | ); 24 | }; 25 | 26 | export default Loader; -------------------------------------------------------------------------------- /src/assets/cloud-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/products-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/pages/Main/Unauthorized.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Unauthorized = () => { 4 | return ( 5 | <> 6 |
9 | 15 |
16 | 17 |

25 | Unauthorized !
26 | Contact Super Admin for visiting permission to this page. 27 |
28 | 29 | Re-Authenticate just in case. 30 | 31 |

32 | 33 | ); 34 | }; 35 | 36 | export default Unauthorized; 37 | -------------------------------------------------------------------------------- /src/assets/doc-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arab-gift-cards-frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@mui/material": "^5.15.12", 14 | "antd": "^5.10.2", 15 | "axios": "^1.5.1", 16 | "chart.js": "^4.4.2", 17 | "react": "^18.2.0", 18 | "react-chartjs-2": "^5.2.0", 19 | "react-dom": "^18.2.0", 20 | "react-router-dom": "^6.16.0", 21 | "react-toastify": "^9.1.3" 22 | }, 23 | "devDependencies": { 24 | "@types/react": "^18.2.15", 25 | "@types/react-dom": "^18.2.7", 26 | "@vitejs/plugin-react": "^4.0.3", 27 | "eslint": "^8.45.0", 28 | "eslint-plugin-react": "^7.32.2", 29 | "eslint-plugin-react-hooks": "^4.6.0", 30 | "eslint-plugin-react-refresh": "^0.4.3", 31 | "vite": "^4.4.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/assets/group-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/list-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/css/dashboard.css: -------------------------------------------------------------------------------- 1 | .info-divs{ 2 | display: flex; 3 | justify-content: space-between; 4 | gap: 25px; 5 | } 6 | .info-divs> div{ 7 | flex-grow: 1; 8 | } 9 | .info-divs h3{ 10 | /* margin-top: 10px; */ 11 | margin-bottom: 10px; 12 | margin-top: 0; 13 | } 14 | .info-divs p{ 15 | color: #00253F; 16 | font-size: 28px; 17 | font-weight: 700; 18 | margin-top: 25px; 19 | margin-bottom: 10px; 20 | } 21 | .box{ 22 | background-color: white; 23 | border-radius: 12px; 24 | padding: 20px; 25 | position: relative; 26 | } 27 | .icon-div{ 28 | position: absolute; 29 | top: 15px; 30 | right: 15px; 31 | padding: 13px; 32 | border-radius: 20px; 33 | padding-bottom: 10px; 34 | 35 | } 36 | .product-div{ 37 | background-color: #fef3d6; 38 | } 39 | .customer-div{ 40 | background-color: #e5e4ff; 41 | } 42 | .sales-div{ 43 | background-color: #d9f7e8; 44 | } 45 | .orders-div{ 46 | background-color: #fdded1; 47 | } 48 | .chart-div{ 49 | background-color: white; 50 | padding: 20px; 51 | margin-top: 20px; 52 | border-radius: 20px; 53 | } 54 | /* .inner-chart-div canvas{ 55 | width: 900px !important; 56 | height: 350px !important; 57 | } */ 58 | 59 | .inner-chart-div canvas{ 60 | height: 20% !important; 61 | width: -webkit-fill-available !important; 62 | } 63 | 64 | @media only screen and (min-width: 1500px) and (max-width: 1900px){ 65 | .inner-chart-div canvas { 66 | height: 600px !important; 67 | 68 | } 69 | } 70 | 71 | @media only screen and (min-width: 1900px){ 72 | .inner-chart-div canvas { 73 | height: 750px !important; 74 | 75 | } 76 | } -------------------------------------------------------------------------------- /src/assets/grid-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/services/Order.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 4 | 5 | export const getOrders = async () => { 6 | try { 7 | let result = ( 8 | await axios.get(`${url}/getAllOrders`, { 9 | headers: { 10 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 11 | }, 12 | }) 13 | ).data; 14 | console.log(result.data, "adsfadfhello"); 15 | return { 16 | message: result.message, 17 | data: result.data, 18 | }; 19 | } catch (error) { 20 | console.log("error", error); 21 | return { success: false, message: error.response.data.message }; 22 | } 23 | }; 24 | 25 | export const sendCodesForOrders = async (data) => { 26 | try { 27 | let result = ( 28 | await axios.post(`${url}/sendProductCodeEmail`, data, { 29 | headers: { 30 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 31 | }, 32 | }) 33 | ).data; 34 | 35 | return { 36 | success: true, 37 | message: result.message, 38 | data: result.data, 39 | }; 40 | } catch (error) { 41 | console.log("error", error); 42 | return { success: false, message: error.response.data.message }; 43 | } 44 | }; 45 | 46 | export const getDashboardData = async () => { 47 | try { 48 | let result = ( 49 | await axios.get(`${url}/getDashboardData`, { 50 | headers: { 51 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 52 | }, 53 | }) 54 | ).data; 55 | 56 | return { 57 | message: result.message, 58 | data: result.data, 59 | orders: result.lastOrders, 60 | graph: result.salesData, 61 | }; 62 | } catch (error) { 63 | console.log("error", error); 64 | return { success: false, message: error.response.data.message }; 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /src/assets/delete-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; */ 3 | line-height: 1.5; 4 | font-weight: 400; 5 | font-family: "Tajawal", sans-serif; 6 | font-weight: 400; 7 | font-style: normal; 8 | color-scheme: light dark; 9 | color: rgba(255, 255, 255, 0.87); 10 | background-color: #fff; 11 | 12 | font-synthesis: none; 13 | text-rendering: optimizeLegibility; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | -webkit-text-size-adjust: 100%; 17 | } 18 | 19 | a { 20 | font-weight: 700; 21 | color: #646cff; 22 | text-decoration: inherit; 23 | } 24 | 25 | .react-tel-input .form-control { 26 | color: black !important; 27 | } 28 | 29 | a:hover { 30 | color: #535bf2; 31 | } 32 | 33 | h1, 34 | h2, 35 | h3 { 36 | color: #000; 37 | } 38 | 39 | body { 40 | margin: 0; 41 | } 42 | 43 | h1 { 44 | font-size: 3.2em; 45 | line-height: 1.1; 46 | } 47 | 48 | button { 49 | border-radius: 8px; 50 | border: 1px solid transparent; 51 | padding: 0.6em 1.2em; 52 | font-size: 1em; 53 | font-weight: 500; 54 | font-family: inherit; 55 | background-color: #1a1a1a; 56 | cursor: pointer; 57 | transition: border-color 0.25s; 58 | } 59 | button:hover { 60 | border-color: #646cff; 61 | } 62 | button:focus, 63 | button:focus-visible { 64 | outline: 4px auto -webkit-focus-ring-color; 65 | } 66 | 67 | @media (prefers-color-scheme: light) { 68 | :root { 69 | color: #213547; 70 | background-color: #ffffff; 71 | } 72 | a:hover { 73 | color: #747bff; 74 | } 75 | button { 76 | background-color: #f9f9f9; 77 | } 78 | } 79 | ::-webkit-scrollbar { 80 | background-color: #fff; 81 | width: 12px; 82 | } 83 | 84 | ::-webkit-scrollbar-track { 85 | background-color: #D9D9D9; 86 | border-radius: 8px; 87 | } 88 | 89 | /* scrollbar itself */ 90 | ::-webkit-scrollbar-thumb { 91 | background-color: #babac0; 92 | border-radius: 8px; 93 | } 94 | 95 | /* set button(top and bottom of the scrollbar) */ 96 | ::-webkit-scrollbar-button { 97 | display: none; 98 | } 99 | -------------------------------------------------------------------------------- /src/pages/Main/Order/Modals/OrderDetails.css: -------------------------------------------------------------------------------- 1 | .order_details_component { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | width: 100%; 6 | margin: auto; 7 | margin-top: 20px; 8 | margin-bottom: 20px; 9 | } 10 | 11 | .order_total_container { 12 | padding-right: 50px; 13 | } 14 | 15 | .customer_name_style { 16 | font-size: 25px; 17 | font-weight: bold; 18 | margin-top: 0px; 19 | margin-bottom: 7px; 20 | } 21 | 22 | .customer_sub_details { 23 | margin-top: 3px; 24 | margin-bottom: 3px; 25 | } 26 | 27 | .grand_total_heading { 28 | margin-top: 3px; 29 | margin-bottom: 3px; 30 | font-size: 18px; 31 | } 32 | 33 | .grand_total_value { 34 | margin-top: -8px; 35 | margin-bottom: 0px; 36 | font-size: 28px; 37 | font-weight: bold; 38 | } 39 | 40 | .product_details_component { 41 | margin-top: 20px; 42 | border-top: 1px solid rgb(199, 199, 199); 43 | padding-top: 20px; 44 | } 45 | 46 | .product_details_heading { 47 | margin-top: 0px; 48 | margin-bottom: 5px; 49 | font-weight: bold; 50 | font-size: 16px; 51 | } 52 | 53 | .product_list_holder { 54 | margin-top: 20px; 55 | } 56 | 57 | .product_image { 58 | height: 200px; 59 | width: 22%; 60 | object-fit: contain; 61 | } 62 | 63 | .product_detail_holder { 64 | display: flex; 65 | justify-content: space-between; 66 | align-items: center; 67 | } 68 | 69 | .product_info_container { 70 | width: 33%; 71 | margin-left: 20px; 72 | } 73 | 74 | .qty_count_container { 75 | width: 13%; 76 | } 77 | .qty_style { 78 | margin-top: 0px; 79 | margin-bottom: 5px; 80 | font-weight: bold; 81 | font-size: 15px; 82 | } 83 | 84 | .product_wise_total { 85 | width: 23%; 86 | } 87 | 88 | @media screen and (max-width: 768px) { 89 | .product_detail_holder { 90 | display: block; 91 | margin-bottom: 30px; 92 | } 93 | 94 | .product_info_container { 95 | width: 100%; 96 | } 97 | 98 | .qty_count_container { 99 | width: 100%; 100 | } 101 | 102 | .product_wise_total { 103 | width: 100%; 104 | } 105 | 106 | .product_info_container { 107 | margin-left: 0; 108 | } 109 | 110 | .product_image { 111 | width: 50%; 112 | height: auto; 113 | } 114 | } 115 | 116 | @media screen and (max-width: 450px) { 117 | .order_details_component { 118 | display: block; 119 | } 120 | 121 | .order_total_container { 122 | margin-top: 30px; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/css/login.css: -------------------------------------------------------------------------------- 1 | .img-container{ 2 | display: flex; 3 | justify-content: center; 4 | } 5 | .img-container img{ 6 | width: 25%; 7 | margin: 0 5px; 8 | object-fit: contain; 9 | } 10 | 11 | #normal_login > div{ 12 | margin-bottom: 45px; 13 | } 14 | 15 | #normal_login > div > div{ 16 | display: flex; 17 | flex-direction: column; 18 | justify-content: flex-start ; 19 | margin-bottom: unset !important; 20 | } 21 | #normal_login .ant-form-item-label{ 22 | text-align: left; 23 | } 24 | #normal_login .ant-form-item-label label{ 25 | color: #00253F; 26 | font-weight: 700; 27 | } 28 | .ant-form-item-required::before{ 29 | display: none !important; 30 | } 31 | .forget-password{ 32 | display: flex; 33 | justify-content: space-between; 34 | } 35 | .form_container{ 36 | /* background: url('/assets/login-bg.png') no-repeat #278aa9; */ 37 | background-image: url('/assets/login-bg-2.png'); 38 | background-position: center center; 39 | background-size: 100% 100%; 40 | background-repeat: no-repeat; 41 | } 42 | .form_container .card{ 43 | background-color: white; 44 | } 45 | .ant-space-item{ 46 | width: -webkit-fill-available; 47 | } 48 | .loginBtm button{ 49 | width: 100%; 50 | } 51 | .ant-form-item-control-input-content{ 52 | display: flex; 53 | justify-content: center; 54 | } 55 | .ant-form-item-control-input-content > div{ 56 | width: 70%; 57 | } 58 | .password-div span{ 59 | color: #202224; 60 | font-weight: 700; 61 | } 62 | .forget-password{ 63 | margin-bottom: 30px !important; 64 | padding-top: 10px; 65 | } 66 | 67 | #normal_login input{ 68 | background-color: #F5F9FF; 69 | } 70 | 71 | #normal_login input::placeholder{ 72 | color: #A6A6A6; 73 | /* font-size: 20px; */ 74 | font-weight: 500; 75 | } 76 | 77 | :where(.css-dev-only-do-not-override-xu9wm8).ant-checkbox-checked .ant-checkbox-inner{ 78 | background-color: unset; 79 | border-color: #656565; 80 | } 81 | :where(.css-dev-only-do-not-override-xu9wm8).ant-checkbox .ant-checkbox-inner:after{ 82 | border: solid #656565; 83 | border-width: 0 2px 2px 0; 84 | } 85 | .ant-checkbox-checked:not(.ant-checkbox-disabled):hover .ant-checkbox-inner{ 86 | background-color: unset !important; 87 | border-color: #656565; 88 | } 89 | .ant-checkbox-wrapper:not(.ant-checkbox-wrapper-disabled):hover .ant-checkbox-inner, 90 | :where(.css-dev-only-do-not-override-xu9wm8).ant-checkbox:not(.ant-checkbox-disabled):hover .ant-checkbox-inner{ 91 | border-color: #656565; 92 | } -------------------------------------------------------------------------------- /src/pages/Auth/ForgetPassword.jsx: -------------------------------------------------------------------------------- 1 | import { Form, Input, Button, Space } from "antd"; 2 | import { UserOutlined } from "@ant-design/icons"; 3 | import { useNavigate } from "react-router-dom"; 4 | import axios from "axios"; 5 | import { toast } from "react-toastify"; 6 | 7 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 8 | 9 | export const ForgetPassword = () => { 10 | let navigate = useNavigate(); 11 | 12 | const onFinish = async (values) => { 13 | try { 14 | const email = values.email; 15 | const res = await axios.post( 16 | `${url}/getForgetPassVerificationLink`, 17 | { email }, 18 | { 19 | headers: { 20 | "Content-Type": "application/json", 21 | }, 22 | } 23 | ); 24 | 25 | toast.success(res.data.message); 26 | } catch (error) { 27 | console.log(error); 28 | toast.error("Something went wrong"); 29 | } 30 | }; 31 | 32 | return ( 33 | <> 34 |
35 |
36 |

Enter Registered Email

37 |
38 | 51 | } 53 | placeholder="Email" 54 | /> 55 | 56 | 57 |
navigate("/")}> 58 | Back to Login 59 |
60 | 61 | 62 | 63 | 71 | 72 | 73 | 74 |
75 |
76 |
77 | 78 | ); 79 | }; 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | body li, h4, label, a, th, button, td { 2 | font-family: "Tajawal", sans-serif !important; 3 | } 4 | 5 | .login-form { 6 | max-width: 300px; 7 | margin: 0 auto; 8 | } 9 | .form_container { 10 | display: flex; 11 | flex-direction: column; 12 | width: 100%; 13 | height: 100vh; 14 | justify-content: center; 15 | } 16 | .card { 17 | margin: 0 auto; 18 | border-radius: 21px; 19 | padding: 25px 70px; 20 | box-shadow: 0px 10px 36px -3px rgba(0, 0, 0, 0.1); 21 | } 22 | .ant-layout-has-sider { 23 | height: 100vh; 24 | } 25 | .ant-layout-header { 26 | display: flex; 27 | justify-content: space-between; 28 | } 29 | 30 | aside + div { 31 | height: 100%; 32 | overflow-y: auto; 33 | } 34 | 35 | .categoryImg { 36 | width: 150px; 37 | height: 150px; 38 | object-fit: cover; 39 | } 40 | 41 | .addsubCategories { 42 | cursor: pointer; 43 | } 44 | .field { 45 | position: relative; 46 | margin-bottom: 10px; 47 | } 48 | 49 | .removeSubCategories { 50 | position: absolute; 51 | bottom: 16px; 52 | left: 35%; 53 | cursor: pointer; 54 | } 55 | button.subCatBtn { 56 | background: #e9e9e9; 57 | padding: 4px 0px; 58 | border-radius: 5px; 59 | 60 | text-align: center; 61 | border: none; 62 | font-size: 15px; 63 | margin-right: 10px; 64 | } 65 | 66 | button.subCatBtn:hover { 67 | background-color: #e0e0e0; 68 | } 69 | 70 | .uploadedImg { 71 | display: flex; 72 | align-items: center; 73 | } 74 | 75 | .uploadedImg img { 76 | margin-left: 20px; 77 | } 78 | 79 | .categoryBtn { 80 | margin-bottom: 15px; 81 | } 82 | 83 | .login-form-button { 84 | padding: 5px 30px; 85 | } 86 | 87 | /* .ant-space-item { 88 | width: 100%; 89 | min-width: 220px; 90 | text-align: center; 91 | } */ 92 | 93 | .loginBtm { 94 | width: 100%; 95 | /* min-width: 220px; */ 96 | text-align: center; 97 | } 98 | 99 | .error { 100 | color: red; 101 | } 102 | 103 | .faq-container { 104 | display: flex; 105 | flex-direction: column; 106 | gap: 10px; 107 | } 108 | 109 | .faq-field-container { 110 | display: flex; 111 | align-items: center; 112 | gap: 10px; 113 | } 114 | 115 | .faq-field-container textarea { 116 | padding: 10px; 117 | outline: none; 118 | resize: none; 119 | } 120 | 121 | .faq-btn-container { 122 | display: flex; 123 | gap: 20px; 124 | margin-bottom: 5rem; 125 | } 126 | 127 | .forget-password { 128 | text-align: right; 129 | margin-bottom: 12px; 130 | } 131 | .login-form-forgot{ 132 | text-align: right; 133 | } 134 | .blue-bg-theme{ 135 | background-color: #00B6DE; 136 | color: white; 137 | font-weight: 700; 138 | } 139 | .text-blue{ 140 | color: #00B6DE; 141 | } 142 | .text-dark-blue{ 143 | color: #00253F; 144 | } -------------------------------------------------------------------------------- /src/services/Cms.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { separateCMSImages } from "../util/helper"; 3 | 4 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 5 | 6 | export const getCms = async (cmsType = "partnerLogo") => { 7 | try { 8 | let result = ( 9 | await axios.get(`${url}/cms/getCMSImages`, { 10 | headers: { 11 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 12 | }, 13 | }) 14 | ).data; 15 | 16 | let separatedItems = separateCMSImages(result.data, cmsType); 17 | 18 | return { 19 | success: result.success, 20 | message: result.message, 21 | data: separatedItems, 22 | }; 23 | } catch (error) { 24 | console.log("error", error); 25 | return { success: false, message: error.response.data.message }; 26 | } 27 | }; 28 | 29 | export const deleteProduct = async (id) => { 30 | try { 31 | let result = ( 32 | await axios.delete(`${url}/cms/deleteCMSImage?imageId=${id}`, { 33 | headers: { 34 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 35 | }, 36 | }) 37 | ).data; 38 | 39 | return { success: result.success, message: result.message }; 40 | } catch (error) { 41 | console.log("error", error); 42 | return { success: false, message: error.response.data.message }; 43 | } 44 | }; 45 | 46 | export const addProduct = async (data, imageType = "partnerLogo") => { 47 | try { 48 | const addProductFormData = new FormData(); 49 | addProductFormData.append("imageType", imageType); 50 | 51 | data?.map((image) => { 52 | addProductFormData.append("partnerlogo", image); 53 | }); 54 | 55 | let result = await axios.post( 56 | `${url}/cms/addPartnerLogoImage`, 57 | addProductFormData, 58 | { 59 | headers: { 60 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 61 | }, 62 | } 63 | ); 64 | 65 | return { success: true, message: result.data.message }; 66 | } catch (error) { 67 | console.log("error", error); 68 | return { success: false, message: error.response.data.message }; 69 | } 70 | }; 71 | 72 | export const addCardSliderImages = async (data, imageType = "cardslider") => { 73 | try { 74 | const addProductFormData = new FormData(); 75 | addProductFormData.append("imageType", imageType); 76 | 77 | data?.map((image) => { 78 | addProductFormData.append("cardslider", image); 79 | }); 80 | 81 | let result = await axios.post( 82 | `${url}/cms/addCardSliderImage`, 83 | addProductFormData, 84 | { 85 | headers: { 86 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 87 | }, 88 | } 89 | ); 90 | 91 | return { success: true, message: result.data.message }; 92 | } catch (error) { 93 | console.log("error", error); 94 | return { success: false, message: error.response.data.message }; 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /src/services/Users.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | // const url = 'http://3.231.101.194/api' 4 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 5 | 6 | export const getUsers = async () => { 7 | try { 8 | let result = ( 9 | await axios.get(`${url}/getUsers`, { 10 | headers: { 11 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 12 | }, 13 | }) 14 | ).data; 15 | 16 | return { 17 | success: result.success, 18 | message: result.message, 19 | data: result.data, 20 | }; 21 | } catch (error) { 22 | console.log("error", error); 23 | return { success: false, message: error.response.data.message }; 24 | } 25 | }; 26 | 27 | export const getCustomerUser = async () => { 28 | try { 29 | let result = ( 30 | await axios.get(`${url}/getCustomerUser`, { 31 | headers: { 32 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 33 | }, 34 | }) 35 | ).data; 36 | 37 | return { 38 | success: result.success, 39 | message: result.message, 40 | data: result.data, 41 | }; 42 | } catch (error) { 43 | console.log("error", error); 44 | return { success: false, message: error.response.data.message }; 45 | } 46 | }; 47 | 48 | export const createUsers = async (data) => { 49 | try { 50 | let result = ( 51 | await axios.post(`${url}/user`, data, { 52 | headers: { 53 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 54 | }, 55 | }) 56 | ).data; 57 | 58 | return { 59 | success: result.success, 60 | message: result.message, 61 | data: result.data, 62 | }; 63 | } catch (error) { 64 | console.log("error", error); 65 | return { success: false, message: error.response.data.message }; 66 | } 67 | }; 68 | 69 | export const updateUsers = async (data) => { 70 | try { 71 | let result = ( 72 | await axios.put(`${url}/user`, data, { 73 | headers: { 74 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 75 | }, 76 | }) 77 | ).data; 78 | 79 | return { 80 | success: result.success, 81 | message: result.message, 82 | data: result.data, 83 | }; 84 | } catch (error) { 85 | console.log("error", error); 86 | return { success: false, message: error.response.data.message }; 87 | } 88 | }; 89 | 90 | export const deleteUsers = async (id) => { 91 | try { 92 | let result = ( 93 | await axios.delete(`${url}/user?id=${id}`, { 94 | headers: { 95 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 96 | }, 97 | }) 98 | ).data; 99 | 100 | return { success: result.success, message: result.message }; 101 | } catch (error) { 102 | console.log("error", error); 103 | return { success: false, message: error.response.data.message }; 104 | } 105 | }; 106 | -------------------------------------------------------------------------------- /src/pages/Auth/ResetPassword.jsx: -------------------------------------------------------------------------------- 1 | import { Form, Input, Button, Space } from "antd"; 2 | import { LockOutlined } from "@ant-design/icons"; 3 | import axios from "axios"; 4 | import { toast } from "react-toastify"; 5 | import { useLocation, useNavigate } from "react-router-dom"; 6 | 7 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 8 | 9 | export const ResetPassword = () => { 10 | const navigate = useNavigate(); 11 | 12 | const location = useLocation(); 13 | const searchParams = new URLSearchParams(location.search); 14 | const otp = searchParams.get("otp"); 15 | const email = searchParams.get("email"); 16 | 17 | const onFinish = async (values) => { 18 | try { 19 | const password = values.password; 20 | const confirmPassword = values.confirmPassword; 21 | 22 | if (password !== confirmPassword) { 23 | return toast.error("Password confirmation failed"); 24 | } 25 | 26 | const res = await axios.post( 27 | `${url}/verifyOtpWithPasswordChange`, 28 | { otp: otp, email: email, newPassword: password }, 29 | { 30 | headers: { 31 | "Content-Type": "application/json", 32 | }, 33 | } 34 | ); 35 | 36 | toast.success(res.data.message); 37 | navigate("/"); 38 | } catch (error) { 39 | console.log(error); 40 | toast.error("Something went wrong"); 41 | } 42 | }; 43 | return ( 44 | <> 45 |
46 |
47 |

Reset Password

48 |
49 | 58 | } 60 | type="password" 61 | placeholder="Password" 62 | /> 63 | 64 | 73 | } 75 | type="password" 76 | placeholder="Confirm Password" 77 | /> 78 | 79 | 80 | 81 |
82 | 89 |
90 |
91 |
92 |
93 |
94 |
95 | 96 | ); 97 | }; 98 | -------------------------------------------------------------------------------- /src/pages/Main/GridView.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "antd"; 2 | import React from "react"; 3 | import { EditOutlined, DeleteOutlined } from "@ant-design/icons"; 4 | 5 | const GridView = ({ image, price, category, title, onDelete, onUpdate }) => { 6 | const imageUrl = import.meta.env.VITE_REACT_APP_BACKEND_IMAGE_URL; 7 | 8 | return ( 9 |
10 |
11 | 12 |
13 | 17 | 35 |
36 |
37 |
38 |

{title}

39 |

{category}

40 |
41 | {price} SR 42 |
43 |
44 |
45 | ); 46 | }; 47 | 48 | export default GridView; 49 | -------------------------------------------------------------------------------- /src/services/categories.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 4 | // const url = "http://localhost:3001/api/"; 5 | 6 | export const createCategory = async (data) => { 7 | try { 8 | let result = ( 9 | await axios.post(`${url}/addCategory`, data, { 10 | headers: { 11 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 12 | }, 13 | }) 14 | ).data; 15 | console.log('result', result); 16 | 17 | return { 18 | success: result.code, 19 | message: result.message, 20 | data: result.data, 21 | }; 22 | } catch (error) { 23 | console.log('error', error); 24 | // return { success: false, message: error.data.message }; 25 | } 26 | }; 27 | 28 | export const getCategories = async () => { 29 | try { 30 | let result = ( 31 | await axios.get(`${url}/getCategoryS`, { 32 | headers: { 33 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 34 | }, 35 | }) 36 | ).data; 37 | return { 38 | success: result.code, 39 | message: result.message, 40 | data: result.data, 41 | }; 42 | } catch (error) { 43 | console.log('error', error); 44 | return { success: false, message: error.response.data.message }; 45 | } 46 | }; 47 | 48 | export const swapCategory = async (firstID, secondID) => { 49 | try { 50 | let x = { 51 | firstID, 52 | secondID, 53 | }; 54 | let result = ( 55 | await axios.post(`${url}/swapCategory`, x, { 56 | headers: { 57 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 58 | }, 59 | }) 60 | ).data; 61 | 62 | return { 63 | success: result.code, 64 | message: result.message, 65 | }; 66 | } catch (error) { 67 | console.log('error', error); 68 | return { success: false, message: error.response.data.message }; 69 | } 70 | }; 71 | 72 | export const deleteCategory = async (id) => { 73 | try { 74 | let x = { 75 | categoryId: id, 76 | }; 77 | let result = ( 78 | await axios.post(`${url}/deleteCategory`, x, { 79 | headers: { 80 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 81 | }, 82 | }) 83 | ).data; 84 | 85 | return { 86 | success: result.code, 87 | message: result.message, 88 | }; 89 | } catch (error) { 90 | console.log('error', error); 91 | return { success: false, message: error.response.data.message }; 92 | } 93 | }; 94 | 95 | export const updateCategoryDetail = async (data) => { 96 | try { 97 | let result = ( 98 | await axios.post(`${url}/editCategory`, data, { 99 | headers: { 100 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 101 | }, 102 | }) 103 | ).data; 104 | console.log('result', result); 105 | 106 | return { 107 | success: result.code, 108 | message: result.message, 109 | }; 110 | } catch (error) { 111 | console.log('error', error); 112 | // return { success: false, message: error.data.message }; 113 | } 114 | }; 115 | -------------------------------------------------------------------------------- /src/pages/Main/Binance/Modals/AddTokenModal.jsx: -------------------------------------------------------------------------------- 1 | import { Input, Radio } from 'antd' 2 | import React, { useEffect, useState } from 'react' 3 | 4 | const AddTokenModal = () => { 5 | const [data, setData] = useState({ 6 | type: 'single' 7 | }); 8 | 9 | const changeHandler = (e) => { 10 | const { name, value } = e.target 11 | setData({ ...data, [name]: value }) 12 | } 13 | useEffect(() => { 14 | setData({ 15 | type: data.type 16 | }) 17 | }, [data.type]) 18 | 19 | return ( 20 |
21 |
22 | 23 | 24 | Single 25 | Dual 26 | 27 |
28 | {data?.type === "dual" && 29 | <> 30 |
31 | 32 | 38 |
39 |
40 | 41 | 47 |
48 | 49 | } 50 | 51 | {data?.type === "single" && 52 |
53 | 54 | 60 |
61 | } 62 | 63 |
64 | 65 | 72 |
73 |
74 | 75 | 82 |
83 |
84 | ) 85 | } 86 | 87 | export default AddTokenModal -------------------------------------------------------------------------------- /src/services/Collections.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 4 | // const url = "http://localhost:3001/api/"; 5 | 6 | export const createCollection = async (data) => { 7 | try { 8 | let result = ( 9 | await axios.post(`${url}/addCollection`, data, { 10 | headers: { 11 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 12 | }, 13 | }) 14 | ).data; 15 | console.log('result', result); 16 | 17 | return { 18 | success: result.code, 19 | message: result.message, 20 | data: result.data, 21 | }; 22 | } catch (error) { 23 | console.log('error', error); 24 | // return { success: false, message: error.data.message }; 25 | } 26 | }; 27 | 28 | export const getCollections = async () => { 29 | try { 30 | let result = ( 31 | await axios.get(`${url}/getCollectionsS`, { 32 | headers: { 33 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 34 | }, 35 | }) 36 | ).data; 37 | return { 38 | success: result.code, 39 | message: result.message, 40 | data: result.data, 41 | }; 42 | } catch (error) { 43 | console.log('error', error); 44 | return { success: false, message: error.response.data.message }; 45 | } 46 | }; 47 | 48 | export const deleteCollection = async (id) => { 49 | try { 50 | let x = { 51 | collectionId: id, 52 | }; 53 | let result = ( 54 | await axios.post(`${url}/deleteCollection`, x, { 55 | headers: { 56 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 57 | }, 58 | }) 59 | ).data; 60 | 61 | return { 62 | success: result.code, 63 | message: result.message, 64 | }; 65 | } catch (error) { 66 | console.log('error', error); 67 | return { success: false, message: error.response.data.message }; 68 | } 69 | }; 70 | 71 | export const updateCollectionDetail = async (data) => { 72 | try { 73 | let result = ( 74 | await axios.post(`${url}/editCollection`, data, { 75 | headers: { 76 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 77 | }, 78 | }) 79 | ).data; 80 | console.log('result', result); 81 | 82 | return { 83 | success: result.code, 84 | message: result.message, 85 | }; 86 | } catch (error) { 87 | console.log('error', error); 88 | // return { success: false, message: error.data.message }; 89 | } 90 | }; 91 | 92 | export const swapCollection = async (firstID, secondID) => { 93 | try { 94 | let x = { 95 | firstID, 96 | secondID, 97 | }; 98 | let result = ( 99 | await axios.post(`${url}/swapCollection`, x, { 100 | headers: { 101 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 102 | }, 103 | }) 104 | ).data; 105 | 106 | return { 107 | success: result.code, 108 | message: result.message, 109 | }; 110 | } catch (error) { 111 | console.log('error', error); 112 | return { success: false, message: error.response.data.message }; 113 | } 114 | }; 115 | -------------------------------------------------------------------------------- /src/css/table.css: -------------------------------------------------------------------------------- 1 | .ant-btn-primary{ 2 | background-color: #00B6DE; 3 | color: white; 4 | } 5 | .ant-layout{ 6 | background-color: #f5f6fa; 7 | } 8 | h4{ 9 | color: #00253F !important; 10 | font-size: 24px !important; 11 | margin: 15px 0 !important; 12 | } 13 | th{ 14 | color: #00253F !important; 15 | font-weight: 700 !important; 16 | } 17 | table{ 18 | font-size: 16px; 19 | font-weight: 500; 20 | } 21 | .ant-table-wrapper table, .ant-table-wrapper table th{ 22 | text-align: center !important; 23 | } 24 | main{ 25 | background: #f5f6fa !important; 26 | } 27 | tbody{ 28 | background-color: white; 29 | } 30 | .ant-table-pagination.ant-pagination{ 31 | justify-content: center; 32 | } 33 | .ant-pagination-item-active{ 34 | background-color: #00B6DE !important; 35 | color: white !important; 36 | border: 0 !important; 37 | } 38 | .ant-pagination-item-active a{ 39 | color: white !important; 40 | } 41 | .toggle-btn{ 42 | display: flex; 43 | margin-left: 10px; 44 | background-color: white; 45 | height: fit-content; 46 | margin-top: 15px; 47 | padding: 5px; 48 | border: 0.5px solid #d9d9d9; 49 | border-radius: 8px; 50 | } 51 | .toggle-btn button{ 52 | padding: unset; 53 | width: fit-content; 54 | margin: unset !important; 55 | padding: 6px 56 | } 57 | .ant-btn-default{ 58 | border-color: transparent; 59 | } 60 | .toggle-btn button svg path{ 61 | fill: #AEAEAE !important 62 | } 63 | .toggle-btn .active{ 64 | background-color: #C0ECF6; 65 | } 66 | .toggle-btn .active path{ 67 | fill: #018AA9 !important 68 | } 69 | .toggle-btn button:focus, .toggle-btn button:active, .toggle-btn button:hover{ 70 | outline: 0; 71 | border-color: transparent !important; 72 | } 73 | .main-outer-div{ 74 | display: flex; 75 | justify-content: space-around; 76 | flex-wrap: wrap; 77 | gap: 15px; 78 | margin-top: 30px; 79 | 80 | /* display: grid; 81 | grid-template-columns: repeat(4, 1fr); 82 | gap: 10px; */ 83 | 84 | } 85 | .img-div, .info-div{ 86 | position: relative; 87 | } 88 | .img-div img{ 89 | height: 270px; 90 | object-fit: contain; 91 | border-radius: 8px; 92 | width: -webkit-fill-available; 93 | } 94 | .main-inner-div { 95 | background-color: white; 96 | padding: 10px; 97 | border-radius: 12px; 98 | width: 340px 99 | } 100 | .btn-div{ 101 | position: absolute; 102 | top: 10px; 103 | right: 10px; 104 | display: flex; 105 | } 106 | .btn-div button{ 107 | color: #018AA9; 108 | font-size: 12px; 109 | background-color: #C0ECF6; 110 | border-radius: 4px; 111 | padding: 5px 10px; 112 | } 113 | .btn-div button:focus{ 114 | outline: 0; 115 | } 116 | .edit-btn{ 117 | margin-right: 10px; 118 | } 119 | .delete-btn{ 120 | background-color: white !important; 121 | color: #FF5555 !important; 122 | border-color: #FF5555 !important; 123 | } 124 | .price-div{ 125 | position: absolute; 126 | bottom: 0px; 127 | right: 5px; 128 | font-size: 16px; 129 | font-weight: bold; 130 | } 131 | .info-div h3{ 132 | font-size: 16px; 133 | font-weight: 700; 134 | } 135 | .info-div p{ 136 | font-size: 14px; 137 | color: #018AA9; 138 | font-weight: 700; 139 | } 140 | .list-item{ 141 | text-decoration: none; 142 | list-style-type: none 143 | } -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter, RouterProvider } from 'react-router-dom'; 2 | import './App.css'; 3 | import { Login } from './pages/Auth/Login'; 4 | import { ErrorPage } from './pages/Main/Error-Page'; 5 | import { ForgetPassword } from './pages/Auth/ForgetPassword'; 6 | import { ResetPassword } from './pages/Auth/ResetPassword'; 7 | import { SidePanel } from './pages/Main/SidePanel'; 8 | import { Users } from './pages/Main/Users'; 9 | import { ToastContainer } from 'react-toastify'; 10 | import 'react-toastify/dist/ReactToastify.css'; 11 | import ProtectedRoute from './components/ProjectedRoutes'; 12 | import Categories from './pages/Main/Categories'; 13 | import Collections from './pages/Main/Collections'; 14 | import CombinedGiftList from './pages/Main/CombinedGiftList/CombinedGiftList'; 15 | import SelectedGiftCards from './pages/Main/SelectedGiftCards'; 16 | import BitaqatyGiftCards from './pages/Main/BitaqatyGiftCards'; 17 | import Binance from './pages/Main/Binance/Binance'; 18 | import Cms from './pages/Main/CMS/Cms'; 19 | import Unauthorized from './pages/Main/Unauthorized'; 20 | import Order from './pages/Main/Order/Order'; 21 | import Customer from './pages/Main/Customer/Customer'; 22 | import BinanceOrders from './pages/Main/BinanceOrders/BinanceOrders'; 23 | import Dashboard from './pages/Main/Dashboard'; 24 | 25 | function App() { 26 | 27 | const router = createBrowserRouter([ 28 | { 29 | path: '/', 30 | element: , 31 | errorElement: , 32 | }, 33 | { 34 | path: '/forgetPassword', 35 | element: , 36 | }, 37 | { 38 | path: '/resetPassword', 39 | element: , 40 | }, 41 | { 42 | path: '/main', 43 | element: ( 44 | 45 | {' '} 46 | {' '} 47 | 48 | ), 49 | children: [ 50 | { 51 | path: 'dashboard', 52 | element: , 53 | }, 54 | { 55 | path: 'users', 56 | element: , 57 | }, 58 | { 59 | path: 'collections', 60 | element: , 61 | }, 62 | { 63 | path: 'categories', 64 | element: , 65 | }, 66 | { 67 | path: 'giftcards', 68 | element: , 69 | }, 70 | { 71 | path: 'selectedgiftcards', 72 | element: , 73 | }, 74 | { 75 | path: 'bitaqatygiftcard', 76 | element: , 77 | }, 78 | { 79 | path: 'binance', 80 | element: , 81 | }, 82 | { 83 | path: 'binanceOrders', 84 | element: , 85 | }, 86 | { 87 | path: 'cmsManagement', 88 | element: , 89 | }, 90 | { 91 | path: 'orders', 92 | element: , 93 | }, 94 | { 95 | path: 'customers', 96 | element: , 97 | }, 98 | { 99 | path: 'unauthorized', 100 | element: , 101 | }, 102 | ], 103 | }, 104 | ]); 105 | 106 | return ( 107 | <> 108 | 109 | 110 | 111 | 112 | ); 113 | } 114 | 115 | export default App; 116 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/Auth/Login.jsx: -------------------------------------------------------------------------------- 1 | import { Form, Input, Button, Space, Checkbox } from 'antd'; 2 | // import { UserOutlined, LockOutlined } from "@ant-design/icons"; 3 | import { Link, useNavigate } from 'react-router-dom'; 4 | import { login } from '../../services/Auth'; 5 | import { toast } from 'react-toastify'; 6 | import '../../css/login.css'; 7 | import { useEffect } from 'react'; 8 | 9 | export const Login = () => { 10 | let navigate = useNavigate(); 11 | 12 | useEffect(() => { 13 | const token = localStorage.getItem("admin_token"); 14 | if (token) { 15 | navigate("/main/dashboard") 16 | } else { 17 | navigate("/") 18 | } 19 | },[]) 20 | 21 | const onFinish = async (values) => { 22 | let { success, message } = await login(values.email, values.password); 23 | console.log('data', success, message); 24 | if (success) { 25 | toast.success(message); 26 | navigate('/main/dashboard'); 27 | } else { 28 | toast.error(message); 29 | } 30 | }; 31 | 32 | return ( 33 | <> 34 |
35 |
36 |
37 | 38 | 39 |
40 |

44 | Login to Account 45 |

46 |
47 | 61 | } 63 | placeholder="Email" 64 | /> 65 | 66 | 76 | } 78 | type="password" 79 | placeholder="Password" 80 | /> 81 | 82 |
83 | 87 | Forget Password 88 | 89 |
93 | 94 | Remember Password 95 |
96 |
97 | 98 | 99 |
100 | 107 |
108 | 109 | {/* navigate("forgetPassword")} 112 | > 113 | Forgot password 114 | */} 115 |
116 |
117 |
118 |
119 |
120 | 121 | ); 122 | }; 123 | -------------------------------------------------------------------------------- /src/pages/Main/CMS/CMSBLocks/FaqManage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { MinusCircleOutlined } from "@ant-design/icons"; 3 | import { Button } from "antd"; 4 | import { toast } from "react-toastify"; 5 | import axios from "axios"; 6 | 7 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 8 | 9 | const FaqManage = () => { 10 | const [faqData, setFaqData] = useState([]); 11 | 12 | const fetchFaqData = async () => { 13 | try { 14 | let result = await axios.get(`${url}/faqs/getFaqs`, { 15 | headers: { 16 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 17 | }, 18 | }); 19 | 20 | if (result?.data?.data[0]?.faqs.length > 0) { 21 | setFaqData(result.data.data[0].faqs); 22 | } else { 23 | setFaqData([]); 24 | } 25 | } catch (error) { 26 | console.log(error); 27 | setFaqData([]); 28 | } 29 | }; 30 | 31 | const updateFaqData = async () => { 32 | try { 33 | const updatedFaq = faqData.map((faq) => { 34 | const newFaq = { question: faq.question, answer: faq.answer }; 35 | return newFaq; 36 | }); 37 | 38 | await axios.post( 39 | `${url}/faqs/addOrUpdateFaqs`, 40 | { faqs: updatedFaq }, 41 | { 42 | headers: { 43 | Authorization: `Bearer ${localStorage.getItem("admin_token")}`, 44 | }, 45 | } 46 | ); 47 | 48 | toast.success("Faq updated"); 49 | } catch (error) { 50 | console.log(error); 51 | } 52 | }; 53 | 54 | useEffect(() => { 55 | fetchFaqData(); 56 | }, []); 57 | 58 | const addField = () => { 59 | setFaqData((currentFaqData) => { 60 | let len = currentFaqData.length; 61 | if ( 62 | len > 0 && 63 | (!currentFaqData[len - 1].question || !currentFaqData[len - 1].answer) 64 | ) { 65 | return currentFaqData; 66 | } 67 | 68 | const newFaq = { 69 | question: "", 70 | answer: "", 71 | }; 72 | 73 | const updatedFaqData = [...currentFaqData, newFaq]; 74 | return updatedFaqData; 75 | }); 76 | }; 77 | 78 | const removeField = (index) => { 79 | setFaqData((currentFaqData) => { 80 | const updatedFaqData = currentFaqData.filter((faq, i) => i != index); 81 | return updatedFaqData; 82 | }); 83 | }; 84 | 85 | const onChangeHandler = (e, index) => { 86 | const name = e.target.name; 87 | const value = e.target.value; 88 | setFaqData((currentFaqData) => { 89 | const updatedFaqData = currentFaqData.map((faq, i) => { 90 | if (i == index) { 91 | const updatedFaq = { ...faq, [name]: value }; 92 | return updatedFaq; 93 | } else { 94 | return faq; 95 | } 96 | }); 97 | return updatedFaqData; 98 | }); 99 | }; 100 | 101 | const onSubmitHandler = () => { 102 | const len = faqData.length; 103 | if (!faqData[len - 1].question || !faqData[len - 1].answer) { 104 | toast.error("Faq field cannot be empty"); 105 | } else { 106 | updateFaqData(); 107 | } 108 | }; 109 | 110 | return ( 111 |
112 |

FAQ

113 | {faqData.map(({ question, answer }, index) => { 114 | return ( 115 |
116 | 124 | 132 | removeField(index)} 135 | /> 136 |
137 | ); 138 | })} 139 | 140 |
141 | 144 | 145 | {faqData.length > 0 && ( 146 | 149 | )} 150 |
151 |
152 | ); 153 | }; 154 | export default FaqManage; 155 | -------------------------------------------------------------------------------- /src/util/helper.js: -------------------------------------------------------------------------------- 1 | export function mergeObjects(parentsArray) { 2 | let mergedParents = []; 3 | parentsArray.forEach((parent) => { 4 | let mergedChild = {}; 5 | for (let key in parent) { 6 | if ( 7 | parent.hasOwnProperty(key) && 8 | typeof parent[key] === 'object' && 9 | key !== 'projectDetailsHolder' && 10 | key !== 'colection' 11 | ) { 12 | Object.assign(mergedChild, parent[key]); 13 | } else { 14 | mergedChild[key] = parent[key]; 15 | } 16 | } 17 | mergedParents.push(mergedChild); 18 | }); 19 | return mergedParents; 20 | } 21 | 22 | export const handleStateChange = (e, stateName) => { 23 | const { name, value } = e.target; 24 | console.log('name', name); 25 | console.log('value', value); 26 | stateName((prevState) => ({ 27 | ...prevState, 28 | [name]: value, 29 | })); 30 | }; 31 | 32 | export const getColections = (data) => { 33 | let collections = []; 34 | // collections.push({ value: 0, label: 'None' }); 35 | data.map((collection) => { 36 | collections.push({ value: collection?._id, label: collection?.name }); 37 | }); 38 | return collections; 39 | }; 40 | 41 | export const getCategoriesAndSubcategories = (data) => { 42 | let category = []; 43 | // category.push({ value: 0, label: 'None' }); 44 | let subCategories = {}; 45 | data.map((mainCategory) => { 46 | category.push({ value: mainCategory?._id, label: mainCategory?.name }); 47 | let newSubcategories = []; 48 | mainCategory?.subCategories?.map((subCategories) => { 49 | newSubcategories.push({ 50 | value: subCategories?._id ? subCategories?._id : subCategories?.id, 51 | label: subCategories?.subCategoryName, 52 | }); 53 | }); 54 | subCategories[mainCategory?._id] = newSubcategories; 55 | }); 56 | 57 | return { category, subCategories }; 58 | }; 59 | 60 | export const separateCMSImages = (imageObjectList, imageType) => { 61 | let imageUrlList = []; 62 | 63 | imageObjectList?.map((image) => { 64 | if (image.imageType == imageType) { 65 | imageUrlList.push(image); 66 | } 67 | }); 68 | 69 | return imageUrlList; 70 | }; 71 | 72 | export function filterByProductCollection(data, collectionName) { 73 | if (collectionName === 'general') return data; 74 | return data.filter((item) => item.productCollection === collectionName); 75 | } 76 | 77 | export function sortByCreatedAtDescending(arrayOfObjects) { 78 | return arrayOfObjects.sort((a, b) => { 79 | const dateA = new Date(a.createdAt); 80 | const dateB = new Date(b.createdAt); 81 | return dateB - dateA; 82 | }); 83 | } 84 | 85 | const getKeys = (searchObj) => { 86 | const keys = Object.keys(searchObj).filter((key) => { 87 | return ( 88 | searchObj[key] !== '' && 89 | searchObj[key] !== undefined && 90 | key !== 'startDate' && 91 | key !== 'endDate' 92 | ); 93 | }); 94 | return keys; 95 | }; 96 | 97 | export function searchData(searchObj, allData) { 98 | // Extract the keys from searchObj that are not empty 99 | const keysToSearch = getKeys(searchObj); 100 | 101 | if (keysToSearch.length === 0) return allData; // If no search criteria, return all data 102 | 103 | return allData.filter((record) => { 104 | return keysToSearch.every((key) => { 105 | // Handling nested 'colection' and 'category' object searches 106 | if (key === 'colection' || key === 'category') { 107 | const recordValue = record[key]?._id?.toString().toLowerCase(); 108 | const searchValue = searchObj[key]?.toString().toLowerCase(); 109 | return recordValue?.includes(searchValue); 110 | } 111 | // Handling 'title' search 112 | else if (key === 'title') { 113 | const recordValue = record[key]?.toString().toLowerCase(); 114 | const searchValue = searchObj[key]?.toString().toLowerCase(); 115 | return recordValue?.includes(searchValue); 116 | } 117 | // Handling 'productID' search within 'giftCards' array 118 | else if (key === 'productID') { 119 | return record.giftCards?.some( 120 | (giftCard) => 121 | giftCard.referenceNo 122 | .toString() 123 | .toLowerCase() 124 | .includes(searchObj[key].toString().toLowerCase()) || 125 | giftCard.code 126 | .toString() 127 | .toLowerCase() 128 | .includes(searchObj[key].toString().toLowerCase()) 129 | ); 130 | } 131 | // General case for other keys 132 | else { 133 | const recordValue = record[key]?.toString().toLowerCase(); 134 | const searchValue = searchObj[key]?.toString().toLowerCase(); 135 | return recordValue?.includes(searchValue); 136 | } 137 | }); 138 | }); 139 | } 140 | -------------------------------------------------------------------------------- /src/pages/Main/Customer/CustomerTable/CustomerTable.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from "react"; 2 | import { getCustomerUser } from "../../../../services/Users"; 3 | import { Button, Space, Input, Table } from "antd"; 4 | import { searchData } from "../../../../util/helper"; 5 | import { SearchOutlined } from '@ant-design/icons'; 6 | import Loader from "../../../../util/Loader"; 7 | 8 | const CustomerTable = () => { 9 | const [customers, setCustomers] = useState([]); 10 | const [copyCustomers, setCopyCustomers] = useState([]); 11 | const [isLoading, setIsLoading] = useState(true) 12 | const [sortParameters, setSortParameters] = useState({ 13 | email: "", 14 | firstName: "", 15 | }); 16 | 17 | const [searchText, setSearchText] = useState(''); 18 | const [searchedColumn, setSearchedColumn] = useState(''); 19 | const searchInput = useRef(null); 20 | const handleSearch = (selectedKeys, confirm, dataIndex) => { 21 | confirm(); 22 | setSearchText(selectedKeys[0]); 23 | setSearchedColumn(dataIndex); 24 | }; 25 | const handleReset = (clearFilters) => { 26 | clearFilters(); 27 | setSearchText(''); 28 | }; 29 | const getColumnSearchProps = (dataIndex) => ({ 30 | filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters, close }) => ( 31 |
e.stopPropagation()} 36 | > 37 | setSelectedKeys(e.target.value ? [e.target.value] : [])} 42 | onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)} 43 | style={{ 44 | marginBottom: 8, 45 | display: 'block', 46 | }} 47 | /> 48 | 49 | 58 | 71 | 80 | 81 |
82 | ), 83 | filterIcon: (filtered) => ( 84 | 89 | ), 90 | onFilter: (value, record) => 91 | record[dataIndex]?.toString().toLowerCase().includes(value.toLowerCase()), 92 | onFilterDropdownOpenChange: (visible) => { 93 | if (visible) { 94 | setTimeout(() => searchInput.current?.select(), 100); 95 | } 96 | } 97 | }); 98 | 99 | const CustomerTableColumns = [ 100 | { 101 | title: "Customer Name", 102 | dataIndex: "Customer Name", 103 | key: "firstName", 104 | ...getColumnSearchProps('firstName'), 105 | render: (text, record) =>

{record.firstName || record.lastName}

, 106 | }, 107 | { 108 | title: "Email ID", 109 | dataIndex: "email", 110 | ...getColumnSearchProps('email'), 111 | key: "email", 112 | }, 113 | { 114 | title: "User type", 115 | dataIndex: "role", 116 | kay: "role", 117 | }, 118 | { 119 | title: "Registered on", 120 | dataIndex: "createdAt", 121 | key: "createdAt", 122 | render: (dateStr) =>

{new Date(dateStr).toUTCString()}

, 123 | }, 124 | ]; 125 | 126 | useEffect(() => { 127 | const filtered = searchData(sortParameters, copyCustomers); 128 | setCustomers(filtered); 129 | }, [sortParameters]); 130 | 131 | useEffect(() => { 132 | async function fetch() { 133 | const { data: customerData } = await getCustomerUser(); 134 | setCustomers(customerData); 135 | setCopyCustomers(customerData); 136 | setIsLoading(false) 137 | } 138 | 139 | fetch(); 140 | 141 | }, []); 142 | 143 | return ( 144 | isLoading? : 145 |
146 | 151 | 152 | ); 153 | }; 154 | 155 | export default CustomerTable; 156 | -------------------------------------------------------------------------------- /src/pages/Main/Order/Order.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { getOrders } from '../../../services/Order'; 3 | import { Button, Input, Modal, Space, Table, Tag } from 'antd'; 4 | import OrderDetails from './Modals/OrderDetails'; 5 | import { searchData, sortByCreatedAtDescending } from '../../../util/helper'; 6 | import Loader from '../../../util/Loader'; 7 | 8 | const modalStyles = { 9 | width: '60% !important', // Default width for larger screens 10 | '@media screen and (max-width: 768px)': { 11 | width: '95%', // For screens smaller than or equal to 768px 12 | }, 13 | }; 14 | 15 | const Order = () => { 16 | const [orders, setOrders] = useState([]); 17 | const [copyOrders, setCopyOrders] = useState([]); 18 | const [isLoading, setIsLoading] = useState(true) 19 | const [orderDetails, setOrderDetails] = useState({ 20 | isOpen: false, 21 | data: null, 22 | }); 23 | const [sortParameters, setSortParameters] = useState({ 24 | orderId: '', 25 | }); 26 | 27 | const handleOpenDetailView = (data) => { 28 | setOrderDetails({ 29 | isOpen: true, 30 | data: data, 31 | }); 32 | }; 33 | 34 | const handleCloseDetailView = () => { 35 | setOrderDetails({ 36 | isOpen: false, 37 | data: null, 38 | }); 39 | }; 40 | 41 | useEffect(() => { 42 | async function fetch() { 43 | const { data: orderData } = await getOrders(); 44 | setOrders(sortByCreatedAtDescending(orderData)); 45 | setCopyOrders(sortByCreatedAtDescending(orderData)); 46 | setIsLoading(false) 47 | } 48 | 49 | fetch(); 50 | 51 | }, []); 52 | 53 | useEffect(() => { 54 | const filtered = searchData(sortParameters, copyOrders); 55 | setOrders(filtered); 56 | }, [sortParameters]); 57 | 58 | const OrderTableColumns = [ 59 | { 60 | title: 'Date', 61 | dataIndex: 'createdAt', 62 | key: 'createdAt', 63 | render: (dateStr) =>

{new Date(dateStr).toUTCString()}

, 64 | }, 65 | { 66 | title: 'Order ID', 67 | dataIndex: 'orderId', 68 | key: 'orderId', 69 | }, 70 | { 71 | title: 'Customer Name', 72 | dataIndex: 'Customer Name', 73 | key: 'userId', 74 | render: (text, record) => ( 75 |

{record.userId.fullName || record.userId.firstName}

76 | ), 77 | }, 78 | { 79 | title: 'TAG', 80 | dataIndex: 'tag', 81 | key: 'tag', 82 | render: (text, record) => { 83 | let type = ''; 84 | for (const product of record.products) { 85 | type = product.productType; 86 | break; 87 | } 88 | return

{type}

; 89 | }, 90 | }, 91 | { 92 | title: 'Payment Status', 93 | dataIndex: 'Payment Status', 94 | key: 'isPaymentSuccess', 95 | render: (text, record) => ( 96 | 97 | {record?.isPaymentSuccess ? 'Payment Success' : 'Abandoned checkouts'} 98 | 99 | ), 100 | }, 101 | { 102 | title: 'Grand Total', 103 | dataIndex: 'grandTotal', 104 | key: 'grandTotal', 105 | }, 106 | { 107 | title: 'Redeem Code Status', 108 | dataIndex: 'Redeem Code Status', 109 | key: 'isRedeemCodeShared', 110 | render: (text, record) => ( 111 |

{record?.isRedeemCodeShared ? 'Shared' : '-'}

112 | ), 113 | }, 114 | { 115 | title: 'Action', 116 | key: 'action', 117 | render: (_, record) => ( 118 | 119 | 122 | 123 | ), 124 | }, 125 | ]; 126 | 127 | return ( 128 | isLoading? : 129 |
130 |
131 |
139 |

Orders

140 |
141 |
142 | 146 | setSortParameters((prev) => ({ 147 | ...prev, 148 | orderId: e.target.value, 149 | })) 150 | } 151 | /> 152 |
153 |
154 |
159 | 160 | 161 | 169 | 170 | 171 | 172 | ); 173 | }; 174 | 175 | export default Order; 176 | -------------------------------------------------------------------------------- /src/pages/Main/BinanceOrders/BinanceOrders.jsx: -------------------------------------------------------------------------------- 1 | import { Input, Table } from 'antd' 2 | import React, { useEffect } from 'react' 3 | 4 | let data = [] 5 | 6 | 7 | const Logs = () => { 8 | const OrderTableColumns = [ 9 | { 10 | title: "Create At", 11 | dataIndex: "created_at", 12 | key: "created_at", 13 | sorter: { 14 | compare: (a, b) => a.type.localeCompare(b.type), 15 | multiple: 1, 16 | }, 17 | }, 18 | { 19 | title: "Order ID", 20 | dataIndex: "order_id", 21 | key: "order_id", 22 | sorter: { 23 | compare: (a, b) => a.order_id - b.order_id, 24 | multiple: 2, 25 | }, 26 | }, 27 | { 28 | title: "Ref.No", 29 | dataIndex: "ref_no", 30 | key: "ref_no", 31 | sorter: { 32 | compare: (a, b) => a.ref_no - b.ref_no, 33 | multiple: 3, 34 | }, 35 | render: (ref) => `****${ref?.substr(-4)}` 36 | 37 | }, 38 | { 39 | title: "Face Token", 40 | dataIndex: "face_token", 41 | key: "face_token", 42 | 43 | sorter: { 44 | compare: (a, b) => a.type.localeCompare(b.type), 45 | multiple: 4, 46 | }, 47 | }, 48 | { 49 | title: "Base Token", 50 | dataIndex: "base_token", 51 | key: "base_token", 52 | sorter: { 53 | compare: (a, b) => a.type.localeCompare(b.type), 54 | multiple: 5, 55 | }, 56 | }, 57 | { 58 | title: "Base Token Amount", 59 | dataIndex: "base_token_amt", 60 | key: "base_token_amt", 61 | sorter: { 62 | compare: (a, b) => a.base_token_amt - b.base_token_amt, 63 | multiple: 6, 64 | }, 65 | }, 66 | { 67 | title: "Status", 68 | dataIndex: "status", 69 | key: "status", 70 | sorter: { 71 | compare: (a, b) => a.type.localeCompare(b.type), 72 | multiple: 7, 73 | }, 74 | }, 75 | { 76 | title: "Type", 77 | dataIndex: "type", 78 | key: "type", 79 | sorter: { 80 | compare: (a, b) => a.type.localeCompare(b.type), 81 | multiple: 8, 82 | }, 83 | }, 84 | { 85 | title: "Code", 86 | dataIndex: "code", 87 | key: "code", 88 | render: (ref) => `****${ref?.substr(-4)}`, 89 | sorter: { 90 | compare: (a, b) => a.type.localeCompare(b.type), 91 | multiple: 9, 92 | }, 93 | }, 94 | ]; 95 | 96 | const dataGenerator = () => { 97 | let temp = [] 98 | for (let i = 0; i < 20; i++) { 99 | temp.push({ 100 | key: i, 101 | created_at: "2022-01-25", 102 | order_id: 96261 + i, 103 | ref_no: "0033002148629588", 104 | face_token: "USDT", 105 | base_token: "USDT", 106 | base_token_amt: 300 + i, 107 | status: "", 108 | type: "dual", 109 | code: "54545DZ52", 110 | description: "My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park. asda" 111 | },) 112 | } 113 | data = temp 114 | } 115 | 116 | useEffect(() => { 117 | dataGenerator() 118 | }, []) 119 | 120 | return ( 121 |
122 |
123 |
131 |

Orders

132 |
133 |
134 | 138 | // setSortParameters((prev) => ({ 139 | // ...prev, 140 | // orderId: e.target.value, 141 | // })) 142 | // } 143 | /> 144 |
145 |
146 |
( 151 |

{record.description}

152 | ), 153 | }} 154 | dataSource={data || []} 155 | /> 156 | 157 | 158 | ) 159 | } 160 | 161 | export default Logs -------------------------------------------------------------------------------- /src/pages/Main/CombinedGiftList/Modals/AddProductModal.jsx: -------------------------------------------------------------------------------- 1 | import { Input, Select, InputNumber } from 'antd'; 2 | import React, { useEffect, useState } from 'react'; 3 | import { getCollections } from '../../../../services/Collections'; 4 | import { getCategories } from '../../../../services/categories'; 5 | import { 6 | getColections, 7 | getCategoriesAndSubcategories, 8 | handleStateChange, 9 | } from '../../../../util/helper'; 10 | 11 | const AddProductModal = ({ setProductToAdd }) => { 12 | const [addProduct, setAddProduct] = useState({ 13 | productTag: 'bitaqty', 14 | minQty: 1, 15 | }); 16 | const [newImage, setNewImage] = useState(null); 17 | const [minQty, setMinQty] = useState(1); 18 | const [allcollections, setAllcollections] = useState(null); 19 | const [allcategories, setAllcategories] = useState(null); 20 | const [selectedSubCategories, setSelectedSubCategories] = useState(null); 21 | 22 | useEffect(() => { 23 | if (!addProduct) return; 24 | 25 | if (allcategories?.subCategories) { 26 | setSelectedSubCategories( 27 | allcategories?.subCategories[addProduct.category] 28 | ); 29 | } 30 | }, [addProduct?.category]); 31 | 32 | useEffect(() => { 33 | async function fetch() { 34 | const allCollections = await getCollections(); 35 | setAllcollections(getColections(allCollections.data)); 36 | 37 | const allCategories = await getCategories(); 38 | const { category, subCategories } = getCategoriesAndSubcategories( 39 | allCategories?.data 40 | ); 41 | setAllcategories({ category, subCategories }); 42 | } 43 | 44 | fetch(); 45 | }, []); 46 | 47 | useEffect(() => { 48 | if (newImage) { 49 | setAddProduct((prev) => ({ ...prev, image: newImage })); 50 | } 51 | }, [newImage]); 52 | 53 | useEffect(() => { 54 | if (minQty) { 55 | setAddProduct((prev) => ({ ...prev, minQty })); 56 | } 57 | }, [minQty]); 58 | 59 | useEffect(() => { 60 | setAddProduct((prev) => ({ 61 | ...prev, 62 | // baseToken: addProduct?.baseToken, 63 | baseToken: 'USDT', 64 | })); 65 | }, [addProduct?.baseToken]); 66 | 67 | useEffect(() => { 68 | setProductToAdd(addProduct); 69 | }, [addProduct]); 70 | 71 | useEffect(() => { 72 | setAddProduct((prev) => ({ 73 | ...prev, 74 | priceInSAR: addProduct?.priceInSAR, 75 | })); 76 | }, [addProduct?.priceInSAR]); 77 | 78 | useEffect(() => { 79 | setAddProduct((prev) => ({ 80 | ...prev, 81 | description: addProduct?.description, 82 | })); 83 | }, [addProduct?.description]); 84 | 85 | return ( 86 |
87 |
88 | 89 | handleStateChange(e, setAddProduct)} 94 | /> 95 |
96 | 97 | {allcollections && ( 98 |
99 | 100 | 123 | setAddProduct((prev) => ({ ...prev, category: value })) 124 | } 125 | /> 126 |
127 | )} 128 | 129 | {selectedSubCategories && ( 130 |
131 | 132 | handleStateChange(e, setAddProduct)} 150 | /> 151 |
152 | 153 |
154 | 155 | handleStateChange(e, setAddProduct)} 161 | /> 162 |
163 |
164 | 165 | handleStateChange(e, setAddProduct)} 171 | /> 172 |
173 |
174 | 175 | { 182 | setMinQty(value); 183 | }} 184 | /> 185 |
186 |
187 | {newImage ? ( 188 | 189 | ) : ( 190 | '' 191 | )} 192 |
193 |
194 | 195 | setNewImage(e.target.files[0])} /> 196 |
197 | 198 |
199 | 200 | handleStateChange(e, setAddProduct)} 206 | /> 207 |
208 |
209 | ); 210 | }; 211 | 212 | export default AddProductModal; 213 | -------------------------------------------------------------------------------- /src/pages/Main/BitaqatyGiftCards.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { 3 | fetchAllBitaQtyThirdParty, 4 | addProduct, 5 | getBitaqtyGifts, 6 | } from '../../services/Product'; 7 | import { getCategories } from '../../services/categories'; 8 | import { getCollections } from '../../services/Collections'; 9 | import { Button, Modal, Space, Table } from 'antd'; 10 | import { toast } from 'react-toastify'; 11 | import AddProductModal from './CombinedGiftList/Modals/AddProductModal'; 12 | import { 13 | getCategoriesAndSubcategories, 14 | mergeObjects, 15 | getColections, 16 | } from '../../util/helper'; 17 | 18 | const BitaqatyGiftCards = () => { 19 | const [thirPartyOption, setThirdPartyOption] = useState(null); 20 | const [dataToWorkon, setDataToWorkOn] = useState({ 21 | data: null, 22 | action: '', 23 | isOpen: false, 24 | }); 25 | const [productToAdd, setProductToAdd] = useState(null); 26 | const [availabel, setAvailabel] = useState([]); 27 | const [notAvailabel, setNotAvailabel] = useState([]); 28 | 29 | const [allcollections, setAllcollections] = useState(null); 30 | const [allcategories, setAllcategories] = useState(null); 31 | const [selectedSubCategories, setSelectedSubCategories] = useState(null); 32 | 33 | useEffect(() => { 34 | async function fetch() { 35 | const allCollections = await getCollections(); 36 | setAllcollections(getColections(allCollections.data)); 37 | 38 | const response = await getBitaqtyGifts(); 39 | const gifts = response.data; 40 | setAvailabel(mergeObjects(gifts)); 41 | } 42 | 43 | fetch(); 44 | }, [dataToWorkon.isOpen]); 45 | 46 | useEffect(() => { 47 | async function fetch() { 48 | const moreBitaQty = await fetchAllBitaQtyThirdParty(); 49 | // const availabelGiftCard = moreBitaQty.data.filter( 50 | // (data) => data.available == true 51 | // ); 52 | // setAvailabel(availabelGiftCard); 53 | // const notAvailabelGiftCard = moreBitaQty.data.filter( 54 | // (data) => data.available == false 55 | // ); 56 | // setNotAvailabel(notAvailabelGiftCard); 57 | setNotAvailabel(moreBitaQty.data); 58 | } 59 | 60 | fetch(); 61 | }, []); 62 | 63 | useEffect(() => { 64 | async function fetch() { 65 | const allCategories = await getCategories(); 66 | const { category, subCategories } = getCategoriesAndSubcategories( 67 | allCategories?.data 68 | ); 69 | setAllcategories({ category, subCategories }); 70 | } 71 | 72 | fetch(); 73 | }, []); 74 | const handleCloseModal = () => { 75 | setDataToWorkOn({ data: null, action: '', isOpen: false }); 76 | }; 77 | 78 | const addProductModal = (data) => { 79 | setDataToWorkOn({ data: data, action: 'add', isOpen: true }); 80 | }; 81 | 82 | const handleAddProduct = async () => { 83 | if (productToAdd) { 84 | const { success, message } = await addProduct(productToAdd); 85 | if (success) { 86 | toast.success(message); 87 | handleCloseModal(); 88 | } else { 89 | toast.error(message); 90 | handleCloseModal(); 91 | } 92 | } 93 | }; 94 | 95 | function checkIsExist(productID) { 96 | let check = false; 97 | availabel.map((item) => { 98 | if (item.productID == productID && check == false) { 99 | check = true; 100 | } 101 | }); 102 | 103 | return check; 104 | } 105 | 106 | const BitaqtyProductTableColumns = [ 107 | { 108 | title: 'Product ID', 109 | dataIndex: 'productID', 110 | key: 'productID', 111 | render: (text) => {text}, 112 | }, 113 | { 114 | title: 'ProductName', 115 | dataIndex: 'nameEn', 116 | key: 'nameEn', 117 | }, 118 | { 119 | title: 'Product Type', 120 | dataIndex: 'productTag', 121 | key: 'productTag', 122 | }, 123 | { 124 | title: 'Initial Price (USD)', 125 | dataIndex: 'costPriceAfterVat', 126 | key: 'costPriceAfterVat', 127 | render: (text) => parseFloat(text).toFixed(2), 128 | }, 129 | { 130 | title: 'Gift Status', 131 | dataIndex: 'Gift Status', 132 | key: 'costPriceAfterVat', 133 | render: (_, record) => ( 134 | <>{checkIsExist(record.productID) ? 'Already Added' : '-'} 135 | ), 136 | }, 137 | { 138 | title: 'Action', 139 | key: 'action', 140 | render: (_, record) => ( 141 | 142 | {checkIsExist(record.productID) ? ( 143 | Add 144 | ) : ( 145 | addProductModal(record)}>Add 146 | )} 147 | 148 | ), 149 | }, 150 | ]; 151 | 152 | // const alreadyExistingProducts = [ 153 | // { 154 | // title: "Product ID", 155 | // dataIndex: "productID", 156 | // key: "productID", 157 | // render: (text) => {text}, 158 | // }, 159 | // { 160 | // title: "ProductName", 161 | // dataIndex: "nameEn", 162 | // key: "nameEn", 163 | // }, 164 | // { 165 | // title: "Product Type", 166 | // dataIndex: "productTag", 167 | // key: "productTag", 168 | // }, 169 | // { 170 | // title: "Initial Price (USD)", 171 | // dataIndex: "costPriceAfterVat", 172 | // key: "costPriceAfterVat", 173 | // render: (text) => parseFloat(text).toFixed(2), 174 | // }, 175 | // { 176 | // title: "Action", 177 | // key: "action", 178 | // render: (_, record) => ( 179 | // 180 | // addProductModal(record)}>Add 181 | // 182 | // ), 183 | // }, 184 | // ]; 185 | return ( 186 | <> 187 | {/*
188 |

Available Giftcards

189 |
194 | */} 195 |
196 |

Bitaqaty Giftcards

197 |
202 | 203 | 204 | handleAddProduct()} 208 | title={'Add Product'} 209 | okText={'Add Product'} 210 | > 211 | 219 | 220 | 221 | ); 222 | }; 223 | 224 | export default BitaqatyGiftCards; 225 | -------------------------------------------------------------------------------- /src/pages/Main/CMS/CMSBLocks/PartnerLogoManage.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Input } from "antd"; 2 | import React, { useEffect, useState } from "react"; 3 | import { addProduct, deleteProduct, getCms } from "../../../../services/Cms"; 4 | import { toast } from "react-toastify"; 5 | 6 | const PartnerLogoManage = () => { 7 | const [partnerLogos, setPartnerLogo] = useState([]); 8 | const [existingPartnerLogo, setExistingPartnerLogo] = useState([]); 9 | const [toggle, setToggle] = useState(false); 10 | 11 | useEffect(() => { 12 | async function fetchCMS() { 13 | const letPartnerLogo = await getCms("partnerLogo"); 14 | setExistingPartnerLogo(letPartnerLogo.data); 15 | } 16 | 17 | fetchCMS(); 18 | }, [toggle]); 19 | 20 | const handleDeleteLogo = async (id) => { 21 | const { success, message } = await deleteProduct(id); 22 | if (success) { 23 | toast.success(message); 24 | 25 | setToggle(!toggle); 26 | } else { 27 | setToggle(!toggle); 28 | toast.error(message); 29 | } 30 | }; 31 | 32 | const handleAddLogos = async () => { 33 | const { success, message } = await addProduct(partnerLogos, "partnerLogo"); 34 | if (success) { 35 | toast.success(message); 36 | setToggle(!toggle); 37 | setPartnerLogo([]); 38 | } else { 39 | setToggle(!toggle); 40 | toast.error(message); 41 | } 42 | }; 43 | 44 | return ( 45 |
49 |
56 |
57 |
58 |

Manage partner logo

59 |
60 | setPartnerLogo([...e.target.files])} 64 | /> 65 |
66 |
67 | 70 |
78 | {partnerLogos?.length 79 | ? partnerLogos?.map((image) => ( 80 | 86 | )) 87 | : null} 88 |
89 |
90 |
98 |
109 | {existingPartnerLogo?.length 110 | ? existingPartnerLogo?.map((image) => ( 111 |
112 | 121 |

handleDeleteLogo(image?._id)} 134 | > 135 | 142 | 146 | 147 |

148 |
149 | )) 150 | : null} 151 |
152 |
153 |
154 |
155 | ); 156 | }; 157 | 158 | export default PartnerLogoManage; 159 | -------------------------------------------------------------------------------- /src/pages/Main/CMS/CMSBLocks/CardSliderManage.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Input } from "antd"; 2 | import React, { useEffect, useState } from "react"; 3 | import { 4 | addCardSliderImages, 5 | deleteProduct, 6 | getCms, 7 | } from "../../../../services/Cms"; 8 | import { toast } from "react-toastify"; 9 | 10 | const CardSliderManage = () => { 11 | const [cardSliderImage, setCardSliderImage] = useState([]); 12 | const [existingCardSliderImages, setExistingCardSliderImages] = useState([]); 13 | const [toggle, setToggle] = useState(false); 14 | 15 | useEffect(() => { 16 | async function fetchCMS() { 17 | const letCardSliderImages = await getCms("cardSlider"); 18 | setExistingCardSliderImages(letCardSliderImages.data); 19 | } 20 | 21 | fetchCMS(); 22 | }, [toggle]); 23 | 24 | const handleDeleteLogo = async (id) => { 25 | const { success, message } = await deleteProduct(id); 26 | if (success) { 27 | toast.success(message); 28 | 29 | setToggle(!toggle); 30 | } else { 31 | setToggle(!toggle); 32 | toast.error(message); 33 | } 34 | }; 35 | 36 | const handleAddLogos = async () => { 37 | const { success, message } = await addCardSliderImages( 38 | cardSliderImage, 39 | "cardslider" 40 | ); 41 | if (success) { 42 | toast.success(message); 43 | setToggle(!toggle); 44 | setCardSliderImage([]); 45 | } else { 46 | setToggle(!toggle); 47 | toast.error(message); 48 | } 49 | }; 50 | 51 | return ( 52 |
56 |
63 |
64 |
65 |

Manage Card slider images

66 |
67 | setCardSliderImage([...e.target.files])} 71 | /> 72 |
73 |
74 | 77 |
85 | {cardSliderImage?.length 86 | ? cardSliderImage?.map((image) => ( 87 | 93 | )) 94 | : null} 95 |
96 |
97 |
105 |
116 | {" "} 117 | {existingCardSliderImages?.length 118 | ? existingCardSliderImages?.map((image) => ( 119 |
120 | 127 |

handleDeleteLogo(image?._id)} 140 | > 141 | 148 | 152 | 153 |

154 |
155 | )) 156 | : null} 157 |
158 |
159 |
160 |
161 | ); 162 | }; 163 | 164 | export default CardSliderManage; 165 | -------------------------------------------------------------------------------- /src/pages/Main/SidePanel.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { 3 | MenuFoldOutlined, 4 | MenuUnfoldOutlined, 5 | UserOutlined, 6 | DashboardOutlined, 7 | AppstoreAddOutlined, 8 | LogoutOutlined, 9 | CreditCardOutlined, 10 | PicRightOutlined, 11 | ProfileOutlined, 12 | } from '@ant-design/icons'; 13 | import { Layout, Menu, Button, theme, Avatar, Dropdown, Space } from 'antd'; 14 | import { Outlet, useNavigate } from 'react-router-dom'; 15 | const { Header, Sider, Content } = Layout; 16 | import '../../css/side-bar.css' 17 | 18 | export const SidePanel = () => { 19 | const [collapsed, setCollapsed] = useState(false); 20 | const [key, setkey] = useState('1'); 21 | const { 22 | token: { colorBgContainer }, 23 | } = theme.useToken(); 24 | 25 | const navigate = useNavigate(); 26 | 27 | function navigateTo(value, key) { 28 | setkey(key); 29 | navigate(value); 30 | } 31 | 32 | const logOut = () => { 33 | localStorage.removeItem('admin_token'); 34 | navigate('/'); 35 | }; 36 | 37 | const manageProtectionOnRouting = (pageName) => { 38 | let userGrantedAccessList = JSON.parse(localStorage.getItem('accessList')); 39 | if (userGrantedAccessList?.includes(pageName)) return true; 40 | else false; 41 | }; 42 | 43 | const items = [ 44 | { 45 | key: '1', 46 | label: ( 47 | <> 48 | logOut()}> 49 | 109 | ) : !OrderData?.isPaymentSuccess ? ( 110 | 113 | ) : OrderData?.isPaymentSuccess ? ( 114 |

115 | {'Redeem code/s already shared.'} 116 |

117 | ) : null} */} 118 | 119 |
120 | {OrderData?.products?.map((product) => { 121 | let sku = ''; 122 | if (product?.productId?.productDetails?.productTag == 'bitaqty') { 123 | sku = product?.productId?.productDetails?.productID; 124 | } else if ( 125 | product?.productId?.giftCards && 126 | product?.productId?.giftCards.length > 0 127 | ) { 128 | sku = product?.productId?.giftCards[0].referenceNo; 129 | } 130 | return ( 131 |
132 |
133 | product 144 |
145 |

146 | {product?.productId?.productDetails?.nameEn || 147 | product?.productId?.title} 148 |

149 |

SKU: {sku}

150 |

151 | Price: {product?.productId?.priceInSAR} SAR 152 |

153 |
154 |
155 |

156 | {product?.quantity} Qty 157 |

158 |
159 |
160 |

161 | Total:{' '} 162 | {product?.productId?.priceInSAR * product?.quantity} 163 |

164 |
165 |
166 | {/* {OrderData?.isPaymentSuccess && 167 | !OrderData?.isRedeemCodeShared && ( 168 | 171 | handleTokenChange( 172 | product?.productId?._id, 173 | e.target.value 174 | ) 175 | } 176 | /> 177 | )} */} 178 |
179 | ); 180 | })} 181 |
182 | 183 | 184 | ); 185 | }; 186 | 187 | export default OrderDetails; 188 | -------------------------------------------------------------------------------- /src/services/Product.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | // const url = 'http://3.231.101.194/api' 4 | const url = import.meta.env.VITE_REACT_APP_BACKEND_URL; 5 | 6 | export const getBitaqtyGifts = async () => { 7 | try { 8 | let result = ( 9 | await axios.get(`${url}/getAllGiftCards`, { 10 | headers: { 11 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 12 | }, 13 | }) 14 | ).data; 15 | return { 16 | success: result.success, 17 | message: result.message, 18 | data: result.data, 19 | }; 20 | } catch (error) { 21 | console.log('error', error); 22 | return { success: false, message: error.response.data.message }; 23 | } 24 | }; 25 | 26 | export const fetchAllBitaQtyThirdParty = async () => { 27 | try { 28 | let result = ( 29 | await axios.get(`${url}/bitaqaty`, { 30 | headers: { 31 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 32 | }, 33 | }) 34 | ).data; 35 | console.log(result.data, 'adsfadsf'); 36 | let res = []; 37 | if (result.data) { 38 | result.data.map((bitaqty) => { 39 | res.push({ ...bitaqty, productTag: 'bitaqty' }); 40 | }); 41 | } 42 | 43 | return { success: result.success, message: result.message, data: res }; 44 | } catch (error) { 45 | console.log('error', error); 46 | return { success: false, message: error.response.data.message }; 47 | } 48 | }; 49 | 50 | export const deleteProduct = async (id) => { 51 | try { 52 | let result = ( 53 | await axios.delete(`${url}/deleteBitaqtyGifts?cardId=${id}`, { 54 | headers: { 55 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 56 | }, 57 | }) 58 | ).data; 59 | 60 | return { success: result.success, message: result.message }; 61 | } catch (error) { 62 | console.log('error', error); 63 | return { success: false, message: error.response.data.message }; 64 | } 65 | }; 66 | 67 | export const deleteBinance = async (id) => { 68 | try { 69 | let result = ( 70 | await axios.delete(`${url}/deleteBinanceGifts?cardId=${id}`, { 71 | headers: { 72 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 73 | }, 74 | }) 75 | ).data; 76 | 77 | return { success: true, message: result.message }; 78 | } catch (error) { 79 | return { success: false, message: error.response.data.message }; 80 | } 81 | }; 82 | 83 | export const updateproduct = async (data) => { 84 | try { 85 | console.log('again', data); 86 | const productUpdateFormData = new FormData(); 87 | productUpdateFormData.append('image', data.image); 88 | productUpdateFormData.append('cardId', data.cardId); 89 | productUpdateFormData.append('category', data.category); 90 | productUpdateFormData.append('subCategory', data.subCategory); 91 | productUpdateFormData.append('colection', data.colection); 92 | productUpdateFormData.append( 93 | 'productDetails', 94 | JSON.stringify(data.productDetails) 95 | ); 96 | data?.price && productUpdateFormData.append('price', data?.price || 0); 97 | console.log('here', productUpdateFormData); 98 | let result = await axios.put( 99 | `${url}/updateBitaqtyGifts`, 100 | productUpdateFormData, 101 | { 102 | headers: { 103 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 104 | }, 105 | } 106 | ); 107 | return { success: result.success, message: result.message }; 108 | } catch (error) { 109 | console.log('error', error); 110 | return { success: false, message: error.response.data.message }; 111 | } 112 | }; 113 | 114 | export const updateBinance = async (data) => { 115 | try { 116 | const updateProductFormData = new FormData(); 117 | updateProductFormData.append('title', data?.title); 118 | updateProductFormData.append('price', data?.price); 119 | updateProductFormData.append('priceInSAR', data?.priceInSAR); 120 | updateProductFormData.append('image', data?.image); 121 | updateProductFormData.append('colection', data?.colection); 122 | updateProductFormData.append('category', data?.category); 123 | updateProductFormData.append('subCategory', data?.subCategory); 124 | updateProductFormData.append('productTag', data?.productTag); 125 | updateProductFormData.append('cardId', data?._id); 126 | updateProductFormData.append('baseToken', data.baseToken); 127 | updateProductFormData.append('faceToken', data.faceToken); 128 | updateProductFormData.append('minQty', data.minQty); 129 | updateProductFormData.append('isDualToken', data.isDualToken); 130 | updateProductFormData.append('description', data.description); 131 | 132 | let result = await axios.put( 133 | `${url}/updateBinanceGifts`, 134 | updateProductFormData, 135 | { 136 | headers: { 137 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 138 | }, 139 | } 140 | ); 141 | return { success: result.success, message: result.message }; 142 | } catch (error) { 143 | console.log('error', error); 144 | return { success: false, message: error.response.data.message }; 145 | } 146 | }; 147 | 148 | export const addProduct = async (data) => { 149 | try { 150 | const addProductFormData = new FormData(); 151 | addProductFormData.append('title', data.title); 152 | addProductFormData.append('minQty', data.minQty); 153 | addProductFormData.append('price', data.price); 154 | addProductFormData.append('image', data.image); 155 | addProductFormData.append('category', data.category); 156 | addProductFormData.append('colection', data.colection); 157 | addProductFormData.append('subCategory', data.subCategory); 158 | addProductFormData.append('priceInSAR', data.priceInSAR); 159 | addProductFormData.append('description', data.description); 160 | let result = await axios.post( 161 | `${url}/addBitaqtyGifts`, 162 | addProductFormData, 163 | { 164 | headers: { 165 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 166 | }, 167 | } 168 | ); 169 | 170 | return { success: result.success, message: result.message }; 171 | } catch (error) { 172 | console.log('error', error); 173 | return { success: false, message: error.response.data.message }; 174 | } 175 | }; 176 | 177 | export const addBinance = async (data) => { 178 | try { 179 | const addProductFormData = new FormData(); 180 | addProductFormData.append('title', data.title); 181 | addProductFormData.append('price', data.price); 182 | addProductFormData.append('priceInSAR', data.priceInSAR); 183 | addProductFormData.append('image', data.image); 184 | addProductFormData.append('colection', data.colection); 185 | addProductFormData.append('category', data.category); 186 | addProductFormData.append('subCategory', data.subCategory); 187 | addProductFormData.append('productTag', data.productTag); 188 | addProductFormData.append('baseToken', data.baseToken); 189 | addProductFormData.append('faceToken', data.faceToken); 190 | addProductFormData.append('minQty', data.minQty); 191 | addProductFormData.append('isDualToken', data.isDualToken); 192 | addProductFormData.append('description', data.description); 193 | 194 | // need to changhe the API 195 | 196 | let result = await axios.post( 197 | `${url}/addBinanceGifts`, 198 | addProductFormData, 199 | { 200 | headers: { 201 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 202 | }, 203 | } 204 | ); 205 | return { success: result.data.success, message: result.data.message }; 206 | } catch (error) { 207 | return { success: false, message: error.response.data.message }; 208 | } 209 | }; 210 | 211 | export const fetchAllBinanceGifts = async () => { 212 | try { 213 | let result = ( 214 | await axios.get(`${url}/getBinanceGifts`, { 215 | headers: { 216 | Authorization: `Bearer ${localStorage.getItem('admin_token')}`, 217 | }, 218 | }) 219 | ).data; 220 | 221 | return { success: result.success, message: result.message, data: result }; 222 | } catch (error) { 223 | console.log('error', error); 224 | return { success: false, message: error.response.data.message }; 225 | } 226 | }; 227 | -------------------------------------------------------------------------------- /src/pages/Main/Binance/Modals/AddBinanceModal.jsx: -------------------------------------------------------------------------------- 1 | import { Input, Select, InputNumber } from 'antd'; 2 | import React, { useEffect, useState } from 'react'; 3 | import { getCollections } from '../../../../services/Collections'; 4 | import { getCategories } from '../../../../services/categories'; 5 | import { 6 | getColections, 7 | getCategoriesAndSubcategories, 8 | handleStateChange, 9 | } from '../../../../util/helper'; 10 | 11 | const AddBinanceModal = ({ setBinanceToAdd }) => { 12 | const [newBinance, setNewBinance] = useState({ 13 | productTag: 'binance', 14 | minQty: 1, 15 | }); 16 | 17 | const [allcollections, setAllcollections] = useState(null); 18 | const [allcategories, setAllcategories] = useState(null); 19 | const [selectedSubCategories, setSelectedSubCategories] = useState(null); 20 | const [isDualToken, setIsDualToken] = useState('false'); 21 | const [minQty, setMinQty] = useState(1); 22 | const [newImage, setNewImage] = useState(null); 23 | 24 | useEffect(() => { 25 | if (!newBinance) return; 26 | 27 | if (allcategories?.subCategories) { 28 | console.log(allcategories?.subCategories[newBinance?.category]); 29 | setSelectedSubCategories( 30 | allcategories?.subCategories[newBinance?.category] 31 | ); 32 | } 33 | }, [newBinance?.category]); 34 | 35 | useEffect(() => { 36 | async function fetch() { 37 | const allCollections = await getCollections(); 38 | setAllcollections(getColections(allCollections.data)); 39 | 40 | const allCategories = await getCategories(); 41 | const { category, subCategories } = getCategoriesAndSubcategories( 42 | allCategories?.data 43 | ); 44 | setAllcategories({ category, subCategories }); 45 | } 46 | 47 | fetch(); 48 | }, []); 49 | 50 | useEffect(() => { 51 | if (newImage) { 52 | setNewBinance((prev) => ({ ...prev, image: newImage })); 53 | } 54 | }, [newImage]); 55 | 56 | useEffect(() => { 57 | if (isDualToken) { 58 | setNewBinance((prev) => ({ ...prev, isDualToken })); 59 | } 60 | }, [isDualToken]); 61 | 62 | useEffect(() => { 63 | if (minQty) { 64 | setNewBinance((prev) => ({ ...prev, minQty })); 65 | } 66 | }, [minQty]); 67 | 68 | useEffect(() => { 69 | setNewBinance((prev) => ({ 70 | ...prev, 71 | faceToken: newBinance?.faceToken, 72 | })); 73 | }, [newBinance?.faceToken]); 74 | 75 | useEffect(() => { 76 | setNewBinance((prev) => ({ 77 | ...prev, 78 | baseToken: newBinance?.baseToken, 79 | })); 80 | }, [newBinance?.baseToken]); 81 | 82 | useEffect(() => { 83 | setBinanceToAdd(newBinance); 84 | }, [newBinance]); 85 | 86 | useEffect(() => { 87 | setNewBinance((prev) => ({ 88 | ...prev, 89 | priceInSAR: newBinance?.priceInSAR, 90 | })); 91 | }, [newBinance?.priceInSAR]); 92 | 93 | useEffect(() => { 94 | setNewBinance((prev) => ({ 95 | ...prev, 96 | description: newBinance?.description, 97 | })); 98 | }, [newBinance?.description]); 99 | 100 | return ( 101 |
102 |
103 | 104 | handleStateChange(e, setNewBinance)} 109 | /> 110 |
111 | {allcollections && ( 112 |
113 | 114 | 136 | setNewBinance((prev) => ({ 137 | ...prev, 138 | category: value, 139 | subCategory: '', 140 | })) 141 | } 142 | /> 143 |
144 | )} 145 | 146 | {selectedSubCategories && ( 147 |
148 | 149 | handleStateChange(e, setNewBinance)} 169 | /> 170 |
*/} 171 |
172 | 173 | handleStateChange(e, setNewBinance)} 201 | /> 202 |
203 | 204 | {isDualToken == 'true' && ( 205 |
206 | 207 | handleStateChange(e, setNewBinance)} 211 | /> 212 |
213 | )} 214 | 215 |
216 | 217 | handleStateChange(e, setNewBinance)} 222 | /> 223 |
224 |
225 | 226 | handleStateChange(e, setNewBinance)} 231 | /> 232 |
233 | 234 |
235 | 236 | { 243 | setMinQty(value); 244 | }} 245 | /> 246 |
247 | 248 |
249 | {newImage && ( 250 | 251 | )} 252 | 253 |
254 | 255 | setNewImage(e.target.files[0])} /> 256 |
257 |
258 | 259 |
260 | 261 | handleStateChange(e, setNewBinance)} 266 | /> 267 |
268 |
269 | ); 270 | }; 271 | 272 | export default AddBinanceModal; 273 | -------------------------------------------------------------------------------- /src/pages/Main/CombinedGiftList/CombinedGiftList.jsx: -------------------------------------------------------------------------------- 1 | import { Modal, Space, Table } from 'antd'; 2 | import { useEffect, useState } from 'react'; 3 | import { 4 | addProduct, 5 | deleteProduct, 6 | fetchAllBitaQtyThirdParty, 7 | getBitaqtyGifts, 8 | updateproduct, 9 | } from '../../../services/Product'; 10 | import { 11 | getColections, 12 | getCategoriesAndSubcategories, 13 | mergeObjects, 14 | } from '../../../util/helper'; 15 | import DeleteConformationModel from './Modals/DeleteConformationModel'; 16 | import { toast } from 'react-toastify'; 17 | import UpdateProductModal from './Modals/UpdateProductModal'; 18 | import AddProductModal from './Modals/AddProductModal'; 19 | import { getCategories } from '../../../services/categories'; 20 | import { getCollections } from '../../../services/Collections'; 21 | 22 | const CombinedGiftList = () => { 23 | const [gifts, setGifts] = useState(null); 24 | const [thirPartyOption, setThirdPartyOption] = useState(null); 25 | const [dataToWorkon, setDataToWorkOn] = useState({ 26 | data: null, 27 | action: '', 28 | isOpen: false, 29 | }); 30 | 31 | const [updatedProduct, setUpdatedProduct] = useState(null); 32 | const [productToAdd, setProductToAdd] = useState(null); 33 | 34 | const [allcollections, setAllcollections] = useState(null); 35 | const [allcategories, setAllcategories] = useState(null); 36 | const [selectedSubCategories, setSelectedSubCategories] = useState(null); 37 | 38 | useEffect(() => { 39 | async function fetch() { 40 | const allCollections = await getCollections(); 41 | setAllcollections(getColections(allCollections.data)); 42 | 43 | const allCategories = await getCategories(); 44 | const { category, subCategories } = getCategoriesAndSubcategories( 45 | allCategories?.data 46 | ); 47 | setAllcategories({ category, subCategories }); 48 | } 49 | 50 | fetch(); 51 | }, []); 52 | 53 | const handleCloseModal = () => { 54 | setDataToWorkOn({ data: null, action: '', isOpen: false }); 55 | }; 56 | 57 | const addProductModal = (data) => { 58 | setDataToWorkOn({ data: data, action: 'add', isOpen: true }); 59 | }; 60 | 61 | const handleAddProduct = async () => { 62 | if (productToAdd) { 63 | console.log('productToAdd', productToAdd); 64 | const { success, message } = await addProduct(productToAdd); 65 | if (success) { 66 | toast.success(message); 67 | handleCloseModal(); 68 | } else { 69 | toast.error(message); 70 | handleCloseModal(); 71 | } 72 | } 73 | }; 74 | 75 | const editProductModal = (data) => { 76 | console.log('data', data); 77 | setDataToWorkOn({ data: data, action: 'edit', isOpen: true }); 78 | }; 79 | 80 | const handleProductUpdate = async () => { 81 | if (updatedProduct) { 82 | const { success, message } = await updateproduct(updatedProduct); 83 | if (success) { 84 | toast.success(message); 85 | handleCloseModal(); 86 | } else { 87 | toast.error(message); 88 | handleCloseModal(); 89 | } 90 | } 91 | }; 92 | 93 | const deleteProductModal = (data) => { 94 | setDataToWorkOn({ data: data, action: 'delete', isOpen: true }); 95 | }; 96 | 97 | const handleDeleteProduct = async () => { 98 | const { success, message } = await deleteProduct(dataToWorkon.data); 99 | if (success) { 100 | toast.success(message); 101 | handleCloseModal(); 102 | } else { 103 | toast.error(message); 104 | handleCloseModal(); 105 | } 106 | }; 107 | 108 | const PlatformProuctTableColumns = [ 109 | { 110 | title: 'Product ID', 111 | dataIndex: 'productID', 112 | key: 'productID', 113 | render: (text) => {text}, 114 | }, 115 | { 116 | title: 'ProductName', 117 | dataIndex: 'nameEn', 118 | key: 'nameEn', 119 | }, 120 | { 121 | title: 'Product Type', 122 | dataIndex: 'productTag', 123 | key: 'productTag', 124 | }, 125 | { 126 | title: 'Initial Price (USD)', 127 | dataIndex: 'costPriceAfterVat', 128 | key: 'costPriceAfterVat', 129 | render: (text) => parseFloat(text).toFixed(2), 130 | }, 131 | { 132 | title: 'Platform Price (USD)', 133 | dataIndex: 'price', 134 | key: 'price', 135 | render: (text) => parseFloat(text).toFixed(2), 136 | }, 137 | { 138 | title: 'Price In SAR', 139 | dataIndex: 'priceInSAR', 140 | key: 'priceInSAR', 141 | render: (text) => parseFloat(text).toFixed(2), 142 | }, 143 | { 144 | title: 'Action', 145 | key: 'action', 146 | render: (_, record) => ( 147 | 148 | editProductModal(record)}>Update 149 | deleteProductModal(record._id, record.key)}> 150 | Delete 151 | 152 | 153 | ), 154 | }, 155 | ]; 156 | 157 | const BitaqtyProductTableColumns = [ 158 | { 159 | title: 'Product ID', 160 | dataIndex: 'productID', 161 | key: 'productID', 162 | render: (text) => {text}, 163 | }, 164 | { 165 | title: 'ProductName', 166 | dataIndex: 'nameEn', 167 | key: 'nameEn', 168 | }, 169 | { 170 | title: 'Product Type', 171 | dataIndex: 'productTag', 172 | key: 'productTag', 173 | }, 174 | { 175 | title: 'Initial Price (USD)', 176 | dataIndex: 'costPriceAfterVat', 177 | key: 'costPriceAfterVat', 178 | render: (text) => parseFloat(text).toFixed(2), 179 | }, 180 | { 181 | title: 'Action', 182 | key: 'action', 183 | render: (_, record) => ( 184 | 185 | addProductModal(record)}>Add 186 | 187 | ), 188 | }, 189 | ]; 190 | 191 | useEffect(() => { 192 | async function fetch() { 193 | const response = await getBitaqtyGifts(); 194 | const gifts = response.data; 195 | console.log('gifts', gifts); 196 | setGifts(mergeObjects(gifts)); 197 | } 198 | 199 | fetch(); 200 | }, [dataToWorkon.isOpen]); 201 | 202 | useEffect(() => { 203 | async function fetch() { 204 | const moreBitaQty = await fetchAllBitaQtyThirdParty(); 205 | setThirdPartyOption(moreBitaQty.data); 206 | } 207 | 208 | fetch(); 209 | }, []); 210 | 211 | return ( 212 |
213 |
214 |

Gifts Already Added

215 |
216 |
record.productID} 221 | /> 222 | 223 |
224 |

More Gifts to add

225 |
226 |
record.productID} 231 | /> 232 | 233 | handleDeleteProduct()} 237 | title={'Delete Confirmation'} 238 | okText={'Delete'} 239 | > 240 | {dataToWorkon.action === 'delete' ? ( 241 | 242 | ) : null} 243 | 244 | 245 | handleProductUpdate()} 249 | title={'Update Product'} 250 | okText={'Update'} 251 | > 252 | 260 | 261 | 262 | handleAddProduct()} 266 | title={'Add Product'} 267 | okText={'Add Product'} 268 | > 269 | 277 | 278 | 279 | ); 280 | }; 281 | 282 | export default CombinedGiftList; 283 | -------------------------------------------------------------------------------- /src/pages/Main/Binance/Modals/UpdateBinanceModal.jsx: -------------------------------------------------------------------------------- 1 | import { Input, Select, InputNumber } from 'antd'; 2 | import React, { useEffect, useState } from 'react'; 3 | import { 4 | getColections, 5 | getCategoriesAndSubcategories, 6 | handleStateChange, 7 | } from '../../../../util/helper'; 8 | import { getCollections } from '../../../../services/Collections'; 9 | import { getCategories } from '../../../../services/categories'; 10 | 11 | const UpdateBinanceModal = ({ data, setBinanceToUpdate }) => { 12 | const [newBinance, setNewBinance] = useState(null); 13 | 14 | const [allcollections, setAllcollections] = useState(null); 15 | const [allcategories, setAllcategories] = useState(null); 16 | const [selectedSubCategories, setSelectedSubCategories] = useState(null); 17 | const [isDualToken, setIsDualToken] = useState('false'); 18 | const [minQty, setMinQty] = useState(1); 19 | const [newImage, setNewImage] = useState(null); 20 | 21 | useEffect(() => { 22 | console.log(newBinance); 23 | if (!newBinance) return; 24 | 25 | if (allcategories?.subCategories) { 26 | setSelectedSubCategories( 27 | allcategories?.subCategories[newBinance?.category] 28 | ); 29 | } 30 | }, [newBinance?.category, allcategories]); 31 | 32 | useEffect(() => { 33 | async function fetch() { 34 | const allCollections = await getCollections(); 35 | setAllcollections(getColections(allCollections.data)); 36 | 37 | const allCategories = await getCategories(); 38 | const { category, subCategories } = getCategoriesAndSubcategories( 39 | allCategories?.data 40 | ); 41 | setAllcategories({ category, subCategories }); 42 | } 43 | 44 | fetch(); 45 | }, []); 46 | 47 | useEffect(() => { 48 | setNewBinance((prev) => ({ 49 | ...prev, 50 | priceInSAR: newBinance?.priceInSAR, 51 | })); 52 | }, [newBinance?.price]); 53 | 54 | useEffect(() => { 55 | if (isDualToken) { 56 | setNewBinance((prev) => ({ ...prev, isDualToken })); 57 | } 58 | }, [isDualToken]); 59 | 60 | useEffect(() => { 61 | if (minQty) { 62 | setNewBinance((prev) => ({ ...prev, minQty })); 63 | } 64 | }, [minQty]); 65 | 66 | useEffect(() => { 67 | setNewBinance((prev) => ({ 68 | ...prev, 69 | faceToken: newBinance?.faceToken, 70 | })); 71 | }, [newBinance?.faceToken]); 72 | 73 | useEffect(() => { 74 | setNewBinance((prev) => ({ 75 | ...prev, 76 | baseToken: newBinance?.baseToken, 77 | })); 78 | }, [newBinance?.baseToken]); 79 | 80 | useEffect(() => { 81 | setNewBinance((prev) => ({ 82 | ...prev, 83 | ...data, 84 | category: data.category?._id, 85 | colection: data.colection?._id, 86 | })); 87 | }, [data]); 88 | 89 | useEffect(() => { 90 | if (newImage) setNewBinance((prev) => ({ ...prev, image: newImage })); 91 | }, [newImage]); 92 | 93 | useEffect(() => { 94 | setBinanceToUpdate(newBinance); 95 | }, [newBinance]); 96 | 97 | useEffect(() => { 98 | setNewBinance((prev) => ({ 99 | ...prev, 100 | description: newBinance?.description, 101 | })); 102 | }, [newBinance?.description]); 103 | 104 | return ( 105 |
106 |
107 | 108 | handleStateChange(e, setNewBinance)} 113 | /> 114 |
115 | 116 | {allcollections && ( 117 |
118 | 119 | 144 | setNewBinance((prev) => ({ 145 | ...prev, 146 | category: value, 147 | subCategory: '', 148 | })) 149 | } 150 | /> 151 |
152 | )} 153 | 154 | {selectedSubCategories && ( 155 |
156 | 157 | handleStateChange(e, setNewBinance)} 178 | /> 179 |
*/} 180 | 181 |
182 | 183 | handleStateChange(e, setNewBinance)} 211 | value={newBinance?.baseToken} 212 | /> 213 |
214 | 215 | {newBinance?.isDualToken == 'true' && ( 216 |
217 | 218 | handleStateChange(e, setNewBinance)} 222 | value={newBinance?.faceToken} 223 | /> 224 |
225 | )} 226 | 227 |
228 | 229 | handleStateChange(e, setNewBinance)} 234 | /> 235 |
236 |
237 | 238 | handleStateChange(e, setNewBinance)} 243 | /> 244 |
245 |
246 | 247 | { 254 | setMinQty(value); 255 | }} 256 | /> 257 |
258 |
259 | 260 |
261 | {newImage ? ( 262 | 263 | ) : ( 264 | 275 | )} 276 | setNewImage(e.target.files[0])} /> 277 |
278 |
279 |
280 | 281 | handleStateChange(e, setNewBinance)} 286 | /> 287 |
288 |
289 | ); 290 | }; 291 | 292 | export default UpdateBinanceModal; 293 | -------------------------------------------------------------------------------- /src/pages/Main/Collections.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Button, Table, Modal, Input, Form } from 'antd'; 3 | import { 4 | DeleteOutlined, 5 | EditOutlined, 6 | CloseCircleOutlined, 7 | UpOutlined, 8 | DownOutlined, 9 | } from '@ant-design/icons'; 10 | import { 11 | createCollection, 12 | getCollections, 13 | deleteCollection, 14 | updateCollectionDetail, 15 | swapCollection, 16 | } from '../../services/Collections'; 17 | import { toast } from 'react-toastify'; 18 | import { generateRandomString } from '../../util/generateId'; 19 | import Loader from '../../util/Loader'; 20 | 21 | const formItemLayout = { 22 | labelCol: { 23 | xs: { 24 | span: 24, 25 | }, 26 | sm: { 27 | span: 4, 28 | }, 29 | }, 30 | wrapperCol: { 31 | xs: { 32 | span: 24, 33 | }, 34 | sm: { 35 | span: 20, 36 | }, 37 | }, 38 | }; 39 | const formItemLayoutWithOutLabel = { 40 | wrapperCol: { 41 | xs: { 42 | span: 24, 43 | offset: 0, 44 | }, 45 | sm: { 46 | span: 20, 47 | offset: 4, 48 | }, 49 | }, 50 | }; 51 | 52 | const Collections = () => { 53 | const [isModalOpen, setIsModalOpen] = useState(false); 54 | const [form] = Form.useForm(); 55 | const [getCollectionsData, setgetCollectionsData] = useState([]); 56 | const [edit, setEdit] = useState(false); 57 | const [isLoading, setIsLoading] = useState(true) 58 | const [editCollectionData, setEditCollectionData] = useState(null); 59 | let initialCollectionState = { 60 | uuid: generateRandomString(5), 61 | title: '', 62 | }; 63 | const [collection, setCollection] = useState(initialCollectionState); 64 | 65 | const [error, setError] = useState(false); 66 | 67 | useEffect(() => { 68 | getCollectionsDetails(); 69 | }, []); 70 | 71 | const showModal = () => { 72 | setIsModalOpen(true); 73 | setCollection(initialCollectionState); 74 | }; 75 | 76 | const handleCancel = () => { 77 | setIsModalOpen(false); 78 | setEdit(false); 79 | setError(false); 80 | setCollection(initialCollectionState); 81 | }; 82 | 83 | const onFinish = (values) => { 84 | console.log('Received values of form:', values); 85 | }; 86 | 87 | const handleChange = (e) => { 88 | if (e.target.name == 'title') { 89 | if (e.target.value == '') { 90 | setError(true); 91 | } else { 92 | setError(false); 93 | } 94 | } 95 | setCollection((prev) => ({ ...prev, [e.target.name]: e.target.value })); 96 | }; 97 | 98 | const handleSubmit = async () => { 99 | if (collection.title == '') { 100 | setError(true); 101 | } else { 102 | const fb = new FormData(); 103 | fb.append('name', collection.title); 104 | fb.append('uid', new Date().getTime()); 105 | let { success, message, data } = await createCollection(fb); 106 | if (success == 200) { 107 | toast.success(message); 108 | getCollectionsDetails(); 109 | setCollection(initialCollectionState); 110 | setIsModalOpen(false); 111 | } 112 | } 113 | }; 114 | 115 | const removeElementFromArray = (array, element) => { 116 | if (array.length <= 1) { 117 | return array; 118 | } 119 | const newArr = array.filter((item) => { 120 | return item.id !== element; 121 | }); 122 | 123 | return newArr; 124 | }; 125 | 126 | const getCollectionsDetails = async () => { 127 | let { success, data } = await getCollections(); 128 | setgetCollectionsData(data); 129 | setIsLoading(false) 130 | }; 131 | 132 | const upCategoryOrder = async (index) => { 133 | console.log(index); 134 | if (index > 0) { 135 | console.log(index); 136 | const firstID = getCollectionsData[index]._id; 137 | const secondID = getCollectionsData[index - 1]._id; 138 | const { success, message } = await swapCollection(firstID, secondID); 139 | if (success == 200) { 140 | toast.success(message); 141 | getCollectionsDetails(); 142 | } 143 | } 144 | }; 145 | 146 | const downCategoryOrder = async (index) => { 147 | if (index < getCollectionsData.length - 1) { 148 | console.log(index); 149 | const firstID = getCollectionsData[index]._id; 150 | const secondID = getCollectionsData[index + 1]._id; 151 | const { success, message } = await swapCollection(firstID, secondID); 152 | if (success == 200) { 153 | toast.success(message); 154 | getCollectionsDetails(); 155 | } 156 | } 157 | }; 158 | 159 | const deleteCollectionData = async (id) => { 160 | const { success, message } = await deleteCollection(id); 161 | if (success == 200) { 162 | toast.success(message); 163 | getCollectionsDetails(); 164 | } 165 | }; 166 | 167 | const editCtaegoryData = async (id) => { 168 | const findData = getCollectionsData.find((item) => item._id == id); 169 | console.log('findData', findData); 170 | setEdit(true); 171 | setEditCollectionData(findData); 172 | setCollection({ 173 | uuid: findData?._id, 174 | title: findData?.name, 175 | }); 176 | 177 | // setSubCollection() 178 | setEdit(true); 179 | setIsModalOpen(true); 180 | }; 181 | 182 | const updateCollections = async () => { 183 | if (collection.title == '') { 184 | setError(true); 185 | } else { 186 | const fb = new FormData(); 187 | fb.append('collectionId', collection.uuid); 188 | fb.append('name', collection.title); 189 | 190 | let { success, message } = await updateCollectionDetail(fb); 191 | if (success == 200) { 192 | toast.success(message); 193 | setIsModalOpen(false); 194 | setEdit(false); 195 | getCollectionsDetails(); 196 | setCollection(initialCollectionState); 197 | } 198 | } 199 | }; 200 | 201 | const columns = [ 202 | { 203 | title: 'Name', 204 | dataIndex: 'name', 205 | key: 'name', 206 | }, 207 | { 208 | title: 'Action', 209 | dataIndex: 'action', 210 | key: 'action', 211 | render: (value, record, index) => { 212 | return ( 213 | <> 214 | upCategoryOrder(index)} 217 | /> 218 | 219 | downCategoryOrder(index)} 222 | /> 223 | deleteCollectionData(record._id)} 226 | /> 227 | editCtaegoryData(record._id)} 230 | /> 231 | 232 | ); 233 | }, 234 | }, 235 | ]; 236 | 237 | return ( 238 | isLoading? : 239 | <> 240 | 248 | 249 | {edit === true ? ( 250 | 257 | Update 258 | , 259 | ]} 260 | > 261 | {console.log('1', edit)} 262 | 271 |
272 | 273 | 281 | {error && ( 282 | Collection field is required ! 283 | )} 284 |
285 |
286 |
287 | ) : ( 288 | 294 |
295 | 303 | {error && ( 304 | Collection field is required ! 305 | )} 306 |
307 |
308 | )} 309 | 310 |
311 | 312 | ); 313 | }; 314 | 315 | export default Collections; 316 | -------------------------------------------------------------------------------- /src/pages/Main/CombinedGiftList/Modals/UpdateProductModal.jsx: -------------------------------------------------------------------------------- 1 | import { Input, Select } from 'antd'; 2 | import { useEffect, useState } from 'react'; 3 | import { handleStateChange } from '../../../../util/helper'; 4 | 5 | const UpdateProductModal = ({ 6 | data, 7 | setUpdatedProduct, 8 | allcollections, 9 | allcategories, 10 | selectedSubCategories, 11 | setSelectedSubCategories, 12 | }) => { 13 | console.log('data', data); 14 | const [product, setproduct] = useState(null); 15 | const [newImage, setNewImage] = useState(null); 16 | 17 | useEffect(() => { 18 | setproduct({ ...data, colection: data.colection._id }); 19 | }, [data]); 20 | 21 | useEffect(() => { 22 | if (newImage) setproduct((prev) => ({ ...prev, image: newImage })); 23 | }, [newImage]); 24 | 25 | useEffect(() => { 26 | if (!product) return; 27 | 28 | if (allcategories?.subCategories) { 29 | setSelectedSubCategories(allcategories?.subCategories[product.category]); 30 | } 31 | }, [product?.category]); 32 | 33 | useEffect(() => { 34 | if (product) { 35 | const { image, price, _id, __v, ...productDetails } = product; 36 | 37 | console.log('adafdf', product); 38 | 39 | setUpdatedProduct({ 40 | image, 41 | price: price ? parseFloat(price) : 0, 42 | cardId: _id, 43 | productDetails, 44 | colection: product.colection, 45 | category: product.category, 46 | subCategory: product.subCategory, 47 | }); 48 | } 49 | }, [product]); 50 | 51 | return ( 52 |
53 |
54 | 55 | handleStateChange(e, setproduct)} 59 | /> 60 |
61 | 62 |
63 | 64 | handleStateChange(e, setproduct)} 68 | /> 69 |
70 | 71 |
72 | 73 | handleStateChange(e, setproduct)} 77 | /> 78 |
79 | 80 | 81 |
82 | {newImage ? ( 83 | 84 | ) : ( 85 | 96 | )} 97 | setNewImage(e.target.files[0])} /> 98 |
99 | 100 | {/*
101 | 102 | handleStateChange(e, setproduct)} 106 | /> 107 |
108 | 109 |
110 | 111 | handleStateChange(e, setproduct)} 115 | /> 116 |
*/} 117 | 118 |
119 | 120 | handleStateChange(e, setproduct)} 124 | /> 125 |
126 | 127 |
128 | 129 | handleStateChange(e, setproduct)} 133 | /> 134 |
135 | 136 | {allcollections && ( 137 |
138 | 139 | 164 | setproduct((prev) => ({ 165 | ...prev, 166 | category: value, 167 | subCategory: '', 168 | })) 169 | } 170 | /> 171 |
172 | )} 173 | 174 | {selectedSubCategories && ( 175 |
176 | 177 | handleStateChange(e, setproduct)} 197 | /> 198 |
199 | 200 |
201 | 202 | handleStateChange(e, setproduct)} 206 | /> 207 |
*/} 208 | 209 | {/*
210 | 211 | handleStateChange(e, setproduct)} 215 | /> 216 |
217 | 218 |
219 | 220 | handleStateChange(e, setproduct)} 224 | /> 225 |
226 | 227 |
228 | 229 | handleStateChange(e, setproduct)} 233 | /> 234 |
*/} 235 | 236 | {/*
237 | 238 | handleStateChange(e, setproduct)} 242 | /> 243 |
*/} 244 | 245 | {/*
246 | 247 | handleStateChange(e, setproduct)} 251 | /> 252 |
253 | 254 |
255 | 256 | handleStateChange(e, setproduct)} 260 | /> 261 |
262 | 263 |
264 | 265 | handleStateChange(e, setproduct)} 269 | /> 270 |
271 | 272 |
273 | 274 | handleStateChange(e, setproduct)} 278 | /> 279 |
280 | 281 |
282 | 283 | handleStateChange(e, setproduct)} 287 | /> 288 |
289 | 290 |
291 | 292 | handleStateChange(e, setproduct)} 296 | /> 297 |
298 | 299 |
300 | 301 | handleStateChange(e, setproduct)} 305 | /> 306 |
307 | 308 |
309 | 310 | handleStateChange(e, setproduct)} 314 | /> 315 |
*/} 316 |
317 | ); 318 | }; 319 | 320 | export default UpdateProductModal; 321 | -------------------------------------------------------------------------------- /src/pages/Main/Binance/Binance.jsx: -------------------------------------------------------------------------------- 1 | import { Input, Button, Modal, Space, Table } from 'antd'; 2 | import React, { useRef, useEffect, useState } from 'react'; 3 | import { SearchOutlined } from '@ant-design/icons'; 4 | import AddBinanceModal from './Modals/AddBinanceModal'; 5 | import { 6 | addBinance, 7 | deleteBinance, 8 | fetchAllBinanceGifts, 9 | updateBinance, 10 | } from '../../../services/Product'; 11 | import { toast } from 'react-toastify'; 12 | import DeleteBinanceModal from './Modals/DeleteBinanceModal'; 13 | import UpdateBinanceModal from './Modals/UpdateBinanceModal'; 14 | 15 | const Binance = () => { 16 | const [allBinance, setAllBinance] = useState([]); 17 | 18 | const [openAddProductModal, setOpenAddProductModal] = useState(false); 19 | 20 | const [dataToWorkon, setDataToWorkOn] = useState({ 21 | data: null, 22 | action: '', 23 | isOpen: false, 24 | }); 25 | 26 | const handleCloseAddProductModal = () => { 27 | setOpenAddProductModal(false); 28 | }; 29 | 30 | // For Delete and Edit (Modal) 31 | const handleCloseModal = () => { 32 | setDataToWorkOn({ data: null, action: '', isOpen: false }); 33 | }; 34 | 35 | const handleOpenAddProductModal = () => { 36 | setOpenAddProductModal(true); 37 | }; 38 | 39 | const deleteProductModal = (data) => { 40 | setDataToWorkOn({ data: data, action: 'delete', isOpen: true }); 41 | }; 42 | 43 | const handleDeleteProduct = async () => { 44 | const { success, message } = await deleteBinance(dataToWorkon.data); 45 | if (success) { 46 | toast.success(message); 47 | handleCloseModal(); 48 | } else { 49 | toast.success(message); 50 | handleCloseModal(); 51 | } 52 | }; 53 | 54 | const editProductModal = (data) => { 55 | setDataToWorkOn({ data: data, action: 'edit', isOpen: true }); 56 | }; 57 | 58 | const [binanceToAdd, setBinanceToAdd] = useState(null); 59 | const handleAddProduct = async () => { 60 | if (binanceToAdd) { 61 | const { success, message } = await addBinance(binanceToAdd); 62 | if (success) { 63 | toast.success(message); 64 | setOpenAddProductModal(); 65 | } else { 66 | toast.error(message); 67 | setOpenAddProductModal(); 68 | } 69 | } 70 | }; 71 | 72 | const [binanceToUpdate, setBinanceToUpdate] = useState(null); 73 | const handleProductUpdate = async () => { 74 | if (binanceToUpdate) { 75 | const { __v, ...data } = binanceToUpdate; 76 | const { success, message } = await updateBinance(data); 77 | if (success) { 78 | toast.success(message); 79 | handleCloseModal(); 80 | } else { 81 | toast.error(message); 82 | handleCloseModal(); 83 | } 84 | } 85 | }; 86 | 87 | useEffect(() => { 88 | async function fetch() { 89 | const binance = await fetchAllBinanceGifts(); 90 | setAllBinance(binance?.data?.data); 91 | } 92 | 93 | fetch(); 94 | }, [dataToWorkon?.isOpen, openAddProductModal]); 95 | 96 | const [searchText, setSearchText] = useState(''); 97 | const [searchedColumn, setSearchedColumn] = useState(''); 98 | const searchInput = useRef(null); 99 | const handleSearch = (selectedKeys, confirm, dataIndex) => { 100 | confirm(); 101 | setSearchText(selectedKeys[0]); 102 | setSearchedColumn(dataIndex); 103 | }; 104 | const handleReset = (clearFilters) => { 105 | clearFilters(); 106 | setSearchText(''); 107 | }; 108 | const getColumnSearchProps = (dataIndex) => ({ 109 | filterDropdown: ({ 110 | setSelectedKeys, 111 | selectedKeys, 112 | confirm, 113 | clearFilters, 114 | close, 115 | }) => ( 116 |
e.stopPropagation()} 121 | > 122 | 127 | setSelectedKeys(e.target.value ? [e.target.value] : []) 128 | } 129 | onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)} 130 | style={{ 131 | marginBottom: 8, 132 | display: 'block', 133 | }} 134 | /> 135 | 136 | 147 | 161 | 170 | 171 |
172 | ), 173 | filterIcon: (filtered) => ( 174 | 179 | ), 180 | onFilter: (value, record) => { 181 | if (Array.isArray(record[dataIndex])) { 182 | return record[dataIndex].some((item) => { 183 | return ( 184 | item['code'] 185 | ?.toString() 186 | .toLowerCase() 187 | .includes(value.toLowerCase()) || 188 | item['referenceNo'] 189 | ?.toString() 190 | .toLowerCase() 191 | .includes(value.toLowerCase()) 192 | ); 193 | }); 194 | } else { 195 | return record[dataIndex] 196 | .toString() 197 | .toLowerCase() 198 | .includes(value.toLowerCase()); 199 | } 200 | }, 201 | onFilterDropdownOpenChange: (visible) => { 202 | if (visible) { 203 | setTimeout(() => searchInput.current?.select(), 100); 204 | } 205 | }, 206 | }); 207 | 208 | const BinanceTableColumn = [ 209 | { 210 | title: 'Title', 211 | dataIndex: 'title', 212 | key: 'title', 213 | ...getColumnSearchProps('title'), 214 | render: (text) => {text}, 215 | }, 216 | { 217 | title: 'Product Type', 218 | dataIndex: 'productTag', 219 | key: 'productTag', 220 | }, 221 | { 222 | title: 'Type', 223 | dataIndex: 'priceInSAR', 224 | key: 'priceInSAR', 225 | }, 226 | { 227 | title: 'Base Token Amt', 228 | dataIndex: 'price', 229 | key: 'price', 230 | }, 231 | { 232 | title: 'Type', 233 | dataIndex: 'isDualToken', 234 | key: 'isDualToken', 235 | render: (value) => { 236 | if (value == 'true') { 237 | return 'Dual'; 238 | } else { 239 | return 'Single'; 240 | } 241 | }, 242 | }, 243 | { 244 | title: 'Base Token', 245 | dataIndex: 'baseToken', 246 | key: 'baseToken', 247 | }, 248 | { 249 | title: 'Face Token', 250 | dataIndex: 'faceToken', 251 | key: 'faceToken', 252 | render: (value) => { 253 | if (value == 'undefined') { 254 | return ''; 255 | } 256 | }, 257 | }, 258 | { 259 | title: 'Min Qty', 260 | dataIndex: 'minQty', 261 | key: 'minQty', 262 | }, 263 | { 264 | title: 'Gift Cards(ReferenceNo/RedeemCode)', 265 | dataIndex: 'giftCards', 266 | key: 'giftCards', 267 | ...getColumnSearchProps('giftCards'), 268 | render: (cards, record) => { 269 | if (cards && cards.length > 0) { 270 | return ( 271 |
    272 | {cards.map((card) => ( 273 |
  • 274 | {record.minQty == 0 275 | ? '' 276 | : `${card.referenceNo} / ${card.code}`} 277 |
  • 278 | ))} 279 |
280 | ); 281 | return cardsString; 282 | } else { 283 | return ''; 284 | } 285 | }, 286 | }, 287 | { 288 | title: 'Action', 289 | key: 'action', 290 | render: (_, record) => ( 291 | 292 | editProductModal(record)}>Update 293 | deleteProductModal(record._id, record.key)}> 294 | Delete 295 | 296 | 297 | ), 298 | }, 299 | ]; 300 | 301 | return ( 302 |
303 | 311 | 312 |
317 | 318 | handleAddProduct()} 322 | title={'Add binance'} 323 | okText={'Add'} 324 | > 325 | 326 | 327 | 328 | handleProductUpdate()} 332 | title={'Update Product'} 333 | okText={'Update'} 334 | > 335 | 339 | 340 | 341 | handleDeleteProduct()} 345 | title={'Delete Confirmation'} 346 | okText={'Delete'} 347 | > 348 | {dataToWorkon.action === 'delete' ? ( 349 | 350 | ) : null} 351 | 352 | 353 | ); 354 | }; 355 | 356 | export default Binance; 357 | -------------------------------------------------------------------------------- /src/pages/Main/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import "../../css/dashboard.css"; 3 | import { Button, Dropdown, Modal, Select, Space, Table, Tag } from "antd"; 4 | import { DownOutlined } from "@ant-design/icons"; 5 | import OrderDetails from "./Order/Modals/OrderDetails"; 6 | import { 7 | Chart as ChartJS, 8 | CategoryScale, 9 | LinearScale, 10 | PointElement, 11 | LineElement, 12 | Title, 13 | Tooltip, 14 | Legend, 15 | } from "chart.js"; 16 | import { Line } from "react-chartjs-2"; 17 | import { getDashboardData, getOrders } from "../../services/Order"; 18 | import { getCustomerUser } from "../../services/Users"; 19 | import { fetchAllBinanceGifts, getBitaqtyGifts } from "../../services/Product"; 20 | import Loader from "../../util/Loader"; 21 | 22 | const Dashboard = () => { 23 | const [orderDetails, setOrderDetails] = useState({ 24 | isOpen: false, 25 | data: null, 26 | }); 27 | const [productNumbers, setProductNumbers] = useState(); 28 | const [customerNumbers, setCustomerNumbers] = useState(); 29 | const [salesNumbers, setSalesNumbers] = useState(); 30 | const [orderNumbers, setorderNumbers] = useState(); 31 | 32 | const [lastOrdersData, setLastOrdersData] = useState(); 33 | const [chartData, setChartData] = useState(); 34 | const [selectedMonth, setSelectedMonth] = useState(); 35 | const [isLoading, setisLoading] = useState(true); 36 | const [items, setItems] = useState(); 37 | let labels 38 | 39 | const handleOpenDetailView = (data) => { 40 | setOrderDetails({ 41 | isOpen: true, 42 | data: data, 43 | }); 44 | }; 45 | 46 | const months = { 47 | 1: "January", 48 | 2: "February", 49 | 3: "March", 50 | 4: "April", 51 | 5: "May", 52 | 6: "June", 53 | 7: "July", 54 | 8: "August", 55 | 9: "September", 56 | 10: "October", 57 | 11: "November", 58 | 12: "December", 59 | }; 60 | 61 | // const tempData = [ 62 | // { 63 | // "3-2024": [], 64 | // }, 65 | // { 66 | // "4-2024": [], 67 | // }, 68 | // ]; 69 | 70 | useEffect(() => { 71 | async function fetch() { 72 | const { 73 | data: generalInfo, 74 | orders: lastOrders, 75 | graph: graphData, 76 | } = await getDashboardData(); 77 | 78 | setorderNumbers(generalInfo.orders); 79 | setCustomerNumbers(generalInfo.customers); 80 | setProductNumbers(generalInfo.products); 81 | setSalesNumbers(generalInfo.sales); 82 | setLastOrdersData(lastOrders); 83 | setChartData(graphData); 84 | setisLoading(false); 85 | } 86 | 87 | fetch(); 88 | }, []); 89 | 90 | useEffect(() => { 91 | const itemData = chartData?.map((key, index) => ({ 92 | label: 93 | months[Object.keys(key)[0].split("-")[0]] + 94 | " " + 95 | Object.keys(key)[0].split("-")[1], 96 | value: String(index + 1), 97 | })); 98 | setItems(itemData); 99 | },[chartData]) 100 | 101 | useEffect(() => { 102 | if (items) { 103 | const [month, year] = items[0].label.split(" "); 104 | const formattedMonth = Object.keys(months).find( 105 | (key) => months[key] === month 106 | ); 107 | setSelectedMonth(formattedMonth + "-" + year); 108 | } 109 | }, [items]); 110 | 111 | ChartJS.register( 112 | CategoryScale, 113 | LinearScale, 114 | PointElement, 115 | LineElement, 116 | Title, 117 | Tooltip, 118 | Legend 119 | ); 120 | 121 | const options = { 122 | responsive: true, 123 | plugins: { 124 | legend: { 125 | position: "top", 126 | }, 127 | // title: { 128 | // display: true, 129 | // text: "Chart.js Line Chart", 130 | // }, 131 | }, 132 | scales: { 133 | x: { 134 | display: true, 135 | title: { 136 | display: true, 137 | text: 'Days of month' 138 | }, 139 | ticks: { 140 | autoSkip: true, 141 | maxTicksLimit: 10, 142 | }, 143 | }, 144 | y: { 145 | display: true, 146 | title: { 147 | display: true, 148 | text: 'Sales' 149 | }, 150 | } 151 | } 152 | }; 153 | 154 | const handleChange = (value, label) => { 155 | const [month, year] = label.label.split(" "); 156 | const formattedMonth = Object.keys(months).find( 157 | (key) => months[key] === month 158 | ); 159 | setSelectedMonth(formattedMonth + "-" + year); 160 | }; 161 | 162 | let chart; 163 | 164 | if (chartData && selectedMonth) { 165 | 166 | const selectedData = chartData.find((item) => Object.keys(item) == selectedMonth) 167 | 168 | labels = selectedData[selectedMonth].map((item) => Object.keys(item)[0]) 169 | 170 | // const data = Object.values(chartData[selectedMonth]).map((value) => value); 171 | 172 | const data = selectedData[selectedMonth].map((item) => Object.values(item)[0]); 173 | 174 | let dataPoints = []; 175 | let totalPrice; 176 | 177 | data.forEach((item) => { 178 | totalPrice = 0; 179 | item.forEach((i) => { 180 | totalPrice += Number(i.grandTotal); 181 | }); 182 | dataPoints.push(totalPrice); 183 | }); 184 | 185 | chart = { 186 | labels, 187 | datasets: [ 188 | { 189 | label: "Sales Data", 190 | data: dataPoints, 191 | borderColor: "#00B6DE", 192 | backgroundColor: "#00B6DE", 193 | }, 194 | ], 195 | }; 196 | } 197 | 198 | 199 | const modalStyles = { 200 | width: "60% !important", 201 | "@media screen and (max-width: 768px)": { 202 | width: "95%", 203 | }, 204 | }; 205 | 206 | const handleCloseDetailView = () => { 207 | setOrderDetails({ 208 | isOpen: false, 209 | data: null, 210 | }); 211 | }; 212 | 213 | const OrderTableColumns = [ 214 | { 215 | title: "Date", 216 | dataIndex: "createdAt", 217 | key: "createdAt", 218 | render: (dateStr) =>

{new Date(dateStr).toUTCString()}

, 219 | }, 220 | { 221 | title: "Order ID", 222 | dataIndex: "orderId", 223 | key: "orderId", 224 | }, 225 | { 226 | title: "Customer Name", 227 | dataIndex: "Customer Name", 228 | key: "userId", 229 | render: (text, record) => ( 230 |

{record.userId.fullName || record.userId.firstName}

231 | ), 232 | }, 233 | { 234 | title: "TAG", 235 | dataIndex: "tag", 236 | key: "tag", 237 | render: (text, record) => { 238 | let type = ""; 239 | for (const product of record.products) { 240 | type = product.productType; 241 | break; 242 | } 243 | return

{type}

; 244 | }, 245 | }, 246 | { 247 | title: "Payment Status", 248 | dataIndex: "Payment Status", 249 | key: "isPaymentSuccess", 250 | render: (text, record) => ( 251 | 255 | {record?.isPaymentSuccess ? "Payment Success" : "Abandoned checkouts"} 256 | 257 | ), 258 | }, 259 | { 260 | title: "Grand Total", 261 | dataIndex: "grandTotal", 262 | key: "grandTotal", 263 | }, 264 | { 265 | title: "Redeem Code Status", 266 | dataIndex: "Redeem Code Status", 267 | key: "isRedeemCodeShared", 268 | render: (text, record) => ( 269 |

{record?.isRedeemCodeShared ? "Shared" : "-"}

270 | ), 271 | }, 272 | { 273 | title: "Action", 274 | key: "action", 275 | render: (_, record) => ( 276 | 277 | 280 | 281 | ), 282 | }, 283 | ]; 284 | 285 | return isLoading ? ( 286 | 287 | ) : ( 288 |
289 |
290 |

Home

291 |
292 |
293 |
294 |

Products

295 |

{productNumbers}

296 |
297 | 304 | 310 | 317 | 318 |
319 |
320 |
321 |

Customers

322 |

{customerNumbers}

323 |
324 | 331 | 338 | 344 | 345 |
346 |
347 |
348 |

Sales

349 |

{salesNumbers}

350 |
351 | 358 | 362 | 367 | 368 |
369 |
370 |
371 |

Orders

372 |

{orderNumbers}

373 |
374 | 381 | 386 | 392 | 393 |
394 |
395 |
396 | 397 |
398 |
399 | {" "} 400 |

Sales Details

401 | {items && ( 402 |
424 | 425 | 426 | 434 | 435 | 436 | 437 | ); 438 | }; 439 | 440 | export default Dashboard; 441 | --------------------------------------------------------------------------------