diff --git a/.gitignore b/.gitignore index 0f9cbba..64afa58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,108 +1,105 @@ +# Dependencies +node_modules/ + +# Production build +dist/ +build/ + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* lerna-debug.log* -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - # Runtime data pids *.pid *.seed *.pid.lock -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +# Diagnostic reports +report.*.json -# Coverage directory used by tools like istanbul -coverage +# Coverage +coverage/ +.nyc_output/ *.lcov -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release -build - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache +# Cache +.cache/ +.parcel-cache/ .eslintcache - -# Microbundle cache +.npm .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ +# TypeScript +*.tsbuildinfo + +# IDE / Editor +.vscode/ +.idea/ +*.swp +*.swo + +# OS files +.DS_Store +Thumbs.db + +# Package manager +.yarn-integrity + # Optional REPL history .node_repl_history -# Output of 'npm pack' -*.tgz +# Temporary files +*.tmp +*.temp +*.bak -# Yarn Integrity file -.yarn-integrity +# Next.js +.next/ -# dotenv environment variables file -# .env -.env.test +# Nuxt.js +.nuxt/ -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files +# Gatsby .cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public +public/ -# vuepress build output +# VuePress .vuepress/dist -# Serverless directories +# Serverless .serverless/ -# FuseBox cache -.fusebox/ - -# DynamoDB Local files +# DynamoDB .dynamodb/ -# TernJS port file +# TernJS .tern-port -# wincompare file -*.bak +# Bower +bower_components/ + +# JSPM +jspm_packages/ + +# Optional test outputs +test-results/ +playwright-report/ + +# Vite +vite.svg \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index baeedbd..5838755 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "lodash": "^4.17.21", "mui-daterange-picker": "^1.0.5", "notistack": "^3.0.1", + "papaparse": "^5.5.3", "process": "^0.11.10", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -71,6 +72,7 @@ "stylis-plugin-rtl": "^2.1.1", "util": "^0.12.5", "web-vitals": "^3.3.1", + "xlsx": "^0.18.5", "yup": "^1.1.1" }, "devDependencies": { @@ -5648,6 +5650,15 @@ "node": ">=8.9" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -6825,6 +6836,19 @@ "node": ">=4" } }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -6989,6 +7013,15 @@ "node": ">= 4.0" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -7242,6 +7275,18 @@ "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.10.tgz", "integrity": "sha512-x3elaK+ZY23W1YtFsNQknRdURzkV7g3Z93AoA7SHZJUEXbVjRsNh4h9Uf09+OjWF/4u8tXeAt37gezGRdwR/2g==" }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -9907,6 +9952,15 @@ "node": ">= 0.6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -14337,6 +14391,12 @@ "node": ">=8" } }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -18683,6 +18743,18 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -20176,6 +20248,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -20560,6 +20650,27 @@ } } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -24455,6 +24566,11 @@ "regex-parser": "^2.2.11" } }, + "adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==" + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -25342,6 +25458,15 @@ "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" }, + "cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "requires": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -25468,6 +25593,11 @@ "q": "^1.1.2" } }, + "codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==" + }, "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -25667,6 +25797,11 @@ "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.10.tgz", "integrity": "sha512-x3elaK+ZY23W1YtFsNQknRdURzkV7g3Z93AoA7SHZJUEXbVjRsNh4h9Uf09+OjWF/4u8tXeAt37gezGRdwR/2g==" }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" + }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -27606,6 +27741,11 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, + "frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -30787,6 +30927,11 @@ "retry": "^0.13.1" } }, + "papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==" + }, "param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -33726,6 +33871,14 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "requires": { + "frac": "~1.1.2" + } + }, "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -34810,6 +34963,16 @@ "is-typed-array": "^1.1.10" } }, + "wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==" + }, + "word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -35140,6 +35303,20 @@ "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", "requires": {} }, + "xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "requires": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index b63ef17..d35bb8d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "lodash": "^4.17.21", "mui-daterange-picker": "^1.0.5", "notistack": "^3.0.1", + "papaparse": "^5.5.3", "process": "^0.11.10", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -66,6 +67,7 @@ "stylis-plugin-rtl": "^2.1.1", "util": "^0.12.5", "web-vitals": "^3.3.1", + "xlsx": "^0.18.5", "yup": "^1.1.1" }, "scripts": { diff --git a/src/components/nearle_components/getValueColor.js b/src/components/nearle_components/getValueColor.js new file mode 100644 index 0000000..ceab661 --- /dev/null +++ b/src/components/nearle_components/getValueColor.js @@ -0,0 +1,3 @@ +export const getValueColor = (value) => { + return Number(value) !== 0 ? 'red' : 'inherit'; +}; diff --git a/src/layout/MainLayout/Header/HeaderContent/index.js b/src/layout/MainLayout/Header/HeaderContent/index.js index 194606a..3404b0f 100644 --- a/src/layout/MainLayout/Header/HeaderContent/index.js +++ b/src/layout/MainLayout/Header/HeaderContent/index.js @@ -169,7 +169,7 @@ const HeaderContent = () => { - Orders + Create Orders } /> @@ -188,7 +188,7 @@ const HeaderContent = () => { - Group Orders + Create Group Orders } /> diff --git a/src/menu-items/nearle.js b/src/menu-items/nearle.js index d25086c..ed55be1 100644 --- a/src/menu-items/nearle.js +++ b/src/menu-items/nearle.js @@ -96,14 +96,14 @@ const nearle = { type: 'item', url: '/nearle/reports/ridersummary', icon: DirectionsBikeOutlinedIcon + }, + { + id: 'RiderLogs', + title: , + type: 'item', + url: '/nearle/reports/riderlogs', + icon: DirectionsBikeOutlinedIcon } - // { - // id: 'RiderLogs', - // title: , - // type: 'item', - // url: '/nearle/reports/riderlogs', - // icon: DirectionsBikeOutlinedIcon - // } ] }, { diff --git a/src/pages/nearle/api/api.js b/src/pages/nearle/api/api.js index b708d78..65e0bc6 100644 --- a/src/pages/nearle/api/api.js +++ b/src/pages/nearle/api/api.js @@ -147,9 +147,9 @@ export const fetchOrdersSummary = async ({ queryKey }) => { // ==============================|| getreportlocationsummary (orders summary)||============================== // export const getreportlocationsummary = async ({ queryKey }) => { console.log('queryKey for getreportlocationsummary', queryKey); - const [startdate, enddate, locationId] = queryKey; + const [startdate, enddate, locationId, debouncedSearch] = queryKey; const response = await axios.get( - `${process.env.REACT_APP_URL}/deliveries/getreportlocationsummary/?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}` + `${process.env.REACT_APP_URL}/deliveries/getreportlocationsummary/?tenantid=${tenid}&locationid=${locationId}&fromdate=${startdate}&todate=${enddate}&keyword=${debouncedSearch}` ); console.log('getreportlocationsummary', response.data.details); @@ -292,9 +292,16 @@ export const fetchCount = async ({ queryKey }) => { // ==============================|| fetchRidersLogs (RiderLogs)||============================== // export const fetchRidersLogs = async ({ queryKey }) => { - const [tenantid, startdate] = queryKey; + const [appId, startdate, riderSearch = ''] = queryKey; + // const riderLogsResponse = await axios.get( + // `${process.env.REACT_APP_URL}/partners/getriderlogs/?applocationid=${appId}&fromdate=${startdate || ''}&todate=${startdate}&keyword=${ + // riderSearch || '' + // }` + // ); const riderLogsResponse = await axios.get( - `${process.env.REACT_APP_URL}/partners/getriderlogs/?tenantid=${tenantid}&fromdate=${startdate || ''}` + `https://jupiter.nearle.app/live/api/v2/partners/getriderlogs/?applocationid=${appId}&fromdate=${startdate || ''}&todate=${ + startdate || '' + }&keyword=${riderSearch || ''}` ); console.log('fetchRidersLogs', riderLogsResponse.data.details); return riderLogsResponse.data.details; diff --git a/src/pages/nearle/invoice/invoice.js b/src/pages/nearle/invoice/invoice.js index 721749b..e9908fc 100644 --- a/src/pages/nearle/invoice/invoice.js +++ b/src/pages/nearle/invoice/invoice.js @@ -505,7 +505,7 @@ const Invoice = () => { setTimeout(() => { setIsLoader(false); // setpredialog(true); - navigate('/invoice/preview', { + navigate('/nearle/invoice/preview', { state: item }); }, 500); diff --git a/src/pages/nearle/orders/createorder1.js b/src/pages/nearle/orders/createorder1.js index 8fcdc6f..c9d7e99 100644 --- a/src/pages/nearle/orders/createorder1.js +++ b/src/pages/nearle/orders/createorder1.js @@ -118,6 +118,8 @@ const Createorder1 = () => { const [isLocation, setIsLocation] = useState(false); const textFieldRef1 = useRef(null); const textFieldRef2 = useRef(null); + const [collectionamt, setCollectionamt] = useState(0); + const [quantity, setQuantity] = useState(1); const handleOkClick1 = () => { // Set focus back to the text field after clicking the "OK" chip @@ -836,7 +838,9 @@ const Createorder1 = () => { subcategoryid: +subCatId, taxamount: 0.0, tenantid: tenant.tenantid, - tenantuserid: parseInt(localStorage.getItem('userid')) + tenantuserid: parseInt(localStorage.getItem('userid')), + collectionamt: +collectionamt || 0, + quantity: +quantity || 1 }, pickup: { @@ -936,8 +940,8 @@ const Createorder1 = () => { autoHideDuration: 1000 }); if (admintoken) { - // notifyadmin(admintoken); - sendnotifications(); + notifyadmin(admintoken); + // sendnotifications(); } navigate('/nearle/orders'); @@ -2233,6 +2237,37 @@ const Createorder1 = () => { + + + + { + setCollectionamt(e.target.value); + }} + inputProps={{ min: 0 }} + /> + + + + + { + setQuantity(e.target.value); + }} + inputProps={{ min: 1 }} + /> + + + + {/* ================================================= || Notes || ================================================= */} diff --git a/src/pages/nearle/orders/multiOrderBackup.js b/src/pages/nearle/orders/multiOrderBackup.js new file mode 100644 index 0000000..0f0ecc7 --- /dev/null +++ b/src/pages/nearle/orders/multiOrderBackup.js @@ -0,0 +1,898 @@ +import React from 'react'; +import Loader from 'components/Loader'; +import { useEffect, useState, Fragment } from 'react'; +import { useTheme } from '@mui/material/styles'; +import MainCard from 'components/MainCard'; +import axios from 'axios'; +import ClearIcon from '@mui/icons-material/Clear'; +import { SearchOutlined, CloseOutlined } from '@ant-design/icons'; +import { Empty } from 'antd'; +import MyLocationIcon from '@mui/icons-material/MyLocation'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import dayjs from 'dayjs'; +var utc = require('dayjs/plugin/utc'); +dayjs.extend(utc); +import { enqueueSnackbar } from 'notistack'; +import { useNavigate } from 'react-router'; +import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api'; + +import { + FormControl, + InputAdornment, + Grid, + Typography, + Stack, + Button, + TextField, + Autocomplete, + Divider, + Dialog, + DialogTitle, + DialogContent, + Checkbox, + DialogActions, + CircularProgress, + IconButton, + OutlinedInput, + FormGroup, + FormControlLabel, + Table, + TableContainer, + TableCell, + TableBody, + TableRow, + Paper, + TableHead, + Box +} from '@mui/material'; +import CircularLoader from 'components/nearle_components/CircularLoader'; +// import RidersPinPointOSM from './RidersPinPointOSM'; +import RidersPinPoint from './ridersPinPoint'; + +const MultipleOrders = () => { + const navigate = useNavigate(); + const theme = useTheme(); + const [loading, setLoading] = useState(false); + const [btnLoading, setBtnLoading] = useState(false); + const [appId, setAppId] = useState(0); + + const [tenantLocations, setTenantlocations] = useState([]); + const userid = localStorage.getItem('userid'); + const tenId = localStorage.getItem('tenantid'); + const [tid, setTid] = useState(0); + const [isLocation, setIsLocation] = useState(false); + const [basePrice, setBasePrice] = useState(0); + const [pricePerKm, setPricePerKm] = useState(0); + const [minKm, setMinKm] = useState(0); + const [pickCust, setPickCust] = useState(null); + const [dropCust, setDropCust] = useState([]); + const [isCustomerOpen, setIsCustomerOpen] = useState(false); + const [searchCustList, setSearchCustList] = useState(''); + const [customerlist, setCustomerlist] = useState([]); + const [startdate, setStartdate] = useState(dayjs().format('MM-DD-YYYY')); + const [timeslotarr, setTimeslotarr] = useState([]); + const [starttime, setStatrttime] = useState(); + const [endtime, setEndtime] = useState(); + const [alertmessage, setAlertmessage] = useState(''); + const [otherinstructions, setOtherinstructions] = useState(''); + const [admintoken, setAdmintoken] = useState(); + const [totaldist, settotaldist] = useState(0); + const [totalAmt, settotalAmt] = useState(0); + const [isLoading, setIsLoading] = useState(false); + const [showMap, setShowMap] = useState(false); + + useEffect(() => { + dropCust && console.log('dropCust', dropCust); + }, [dropCust]); + + // =============================================== || opentoast || =============================================== + const opentoast = (message, variant, time) => { + enqueueSnackbar(message, { + variant: variant, + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: time ? time : 1500 + }); + console.log(alertmessage); + }; + // ==============================|| fetchAppLocations ||============================== // + const fetchAppLocations = async () => { + try { + const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`); + console.log('fetchAppLocations', locationRes.data.details); + } catch (err) { + console.log('locationRes', err); + } + }; + useEffect(() => { + fetchAppLocations(); + }, []); + + // ============================================= || fetchTenantPricing || ============================================= + + const fetchTenantPricing = async (id) => { + try { + const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${tenId}`); + console.log('pricingResponse', pricingResponse.data.details); + setBasePrice(pricingResponse.data.details.baseprice); + setPricePerKm(pricingResponse.data.details.priceperkm); + setMinKm(pricingResponse.data.details.minkm); + } catch (error) { + console.log('fetchTenantPricing error', error); + } + }; + useEffect(() => { + fetchTenantPricing(); + }, []); + // ============================================= || gettenantlocations (branches) || ============================================= + const gettenantlocations = async (id) => { + try { + const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`); + console.log('gettenantlocations', res.data.details); + if (res.data.details.length == 1) { + setIsLocation(true); + setTenantlocations(res.data.details); + setPickCust(res.data.details[0]); + } else { + setTenantlocations(res.data.details); + } + } catch (err) { + console.log('gettenantlocations', err); + } + }; + useEffect(() => { + gettenantlocations(tenId); + }, []); + // ========================================================= || clientdetails || ========================================================= + const clientdetails = async () => { + try { + let url = + searchCustList == '' + ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenId}&pageno=1&pagesize=10` + : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenId}&keyword=${searchCustList}`; + await axios + .get(url) + .then((res) => { + if (res.data.status) { + console.log('clientdetails', res.data.details); + + setCustomerlist(res.data.details); + let arr = []; + res.data.details.map((val) => { + arr.push({ + label: `${val.firstname} | ${val.contactno}`, + ...val + }); + }); + } + }) + .catch((err) => { + console.log(err); + opentoast('server error', 'warning'); + }); + } catch (err) { + console.log(err); + } + }; + useEffect(() => { + if (tenId) { + clientdetails(); + } + }, [searchCustList.length > 3, searchCustList == '', tenId]); + + // ========================================================= || calculateTotal(dist , charge) || ========================================================= + const calculateTotal = () => { + let a1 = 0; + let a2 = 0; + dropCust?.map((customer) => { + a1 += customer.distance; + a2 += customer.totalcharge; + }); + settotaldist(a1); + settotalAmt(a2); + }; + useEffect(() => { + dropCust && calculateTotal(); + }, [dropCust]); + + // ========================================================= || handleCheckboxChange || ========================================================= + const handleCheckboxChange = async (event, customer) => { + setIsLoading(true); + console.log('event', event.target.checked); + console.log('customer', customer); + if (event.target.checked) { + // If the checkbox is checked, calculate the distance and add the customer + try { + const obj = await calculateDistance(customer); + console.log('return of calculateDistance', obj); + + const { roundedDistance, totalcharge } = obj; + // Create a new customer object with the distance property + const updatedCustomer = { + ...customer, + distance: roundedDistance, + totalcharge: totalcharge + }; + + // Add the updated customer object to dropCust + setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); + + // Log the rounded distance + console.log(`Rounded Distance: ${roundedDistance} km`); + } catch (error) { + console.error('Failed to calculate distance:', error); + } + setIsLoading(false); + } else { + // If the checkbox is unchecked, remove the customer from dropCust + setDropCust((prevDropCust) => { + return prevDropCust.filter((cust) => cust.customerid !== customer.customerid); + }); + setIsLoading(false); + } + }; + + // ========================================================= || calculateDistance || ========================================================= + const calculateDistance = async (customer) => { + console.log('Distance calculation starts'); + const service = new google.maps.DistanceMatrixService(); + // Helper function to get the distance matrix + const getDistanceMatrix = async (origins, destinations) => { + console.log('origins', origins); + console.log('destinations', destinations); + return new Promise((resolve, reject) => { + console.log('calculation starts'); + service.getDistanceMatrix( + { + origins: [new google.maps.LatLng(origins.latitude, origins.longitude)], + destinations: [new google.maps.LatLng(destinations.latitude, destinations.longitude)], + travelMode: 'DRIVING', + unitSystem: google.maps.UnitSystem.METRIC // Distances in metric units (km) + }, + (response, status) => { + console.log('cal response', response); + console.log('cal status', status); + if (status === 'OK') { + console.log('calcualtion resolved'); + resolve(response); + } else { + console.log('calcualtion rejected'); + + reject(new Error(`Error calculating distance: ${status}`)); + } + } + ); + }); + }; + + try { + // Call getDistanceMatrix and wait for the response + const response = await getDistanceMatrix(pickCust, customer); + + // Extract distance from the first result + const distanceInMeters = response.rows[0].elements[0].distance.value; + + // Convert distance from meters to kilometers + const distanceInKilometers = distanceInMeters / 1000; + + // Round the distance to the nearest integer + const roundedDistance = Math.round(distanceInKilometers); + let totalcharge; + if (roundedDistance < minKm) { + console.log('minKm', minKm); + console.log('pricePerKm', pricePerKm); + console.log('basePrice', basePrice); + totalcharge = basePrice; + } else { + console.log('minKm', minKm); + console.log('pricePerKm', pricePerKm); + console.log('basePrice', basePrice); + totalcharge = (roundedDistance - minKm) * pricePerKm + basePrice; + console.log('totalcharge', totalcharge); + } + + // Return the rounded distance + return { roundedDistance, totalcharge }; + } catch (error) { + console.error('Error calculating distance:', error); + throw error; // Rethrow the error to be handled by the caller + } + }; + + // ==================================================== || fetchTiming || ==================================================== + const fetchTiming = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`) + .then((res) => { + console.log('fetchTiming', res); + const { opentime, closetime, latitude, longitude, radius } = res.data.details[0]; + if (res.data.status) { + setStatrttime(`${dayjs().format('MM-DD-YYYY')} ${opentime}`); + setEndtime(`${dayjs().format('MM-DD-YYYY')} ${closetime}`); + console.log('starttime', `${dayjs().format('MM-DD-YYYY')} ${opentime}`); + console.log('endtime', `${dayjs().format('MM-DD-YYYY')} ${closetime} `); + let arr = []; + for ( + let i = `${dayjs().format('MM-DD-YYYY')} ${opentime}`, j = 0; + dayjs(`${dayjs().format('MM-DD-YYYY')} ${closetime} `).diff(i, 'm') >= 0; + j++, i = dayjs(i).add(30, 'm') + ) { + arr.push(i); + } + console.log('setTimeslotarr', arr); + setTimeslotarr(arr); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + if (appId) { + fetchTiming(); + } + }, [starttime, endtime, appId]); + + const fetchAppAdminTokens = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/utils/getapplocationconfig/?applocationid=${appId}`) + .then((res) => { + const userfcmtokemArray = res.data.details.applocationadmins.map((admin) => admin.userfcmtokem); // fcm => firebase cloud messaging + console.log('fetchAppAdminTokens', res); + console.log('userfcmtokemArray', userfcmtokemArray); + if (res.data.status) { + setAdmintoken(userfcmtokemArray); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + + useEffect(() => { + if (starttime && endtime) { + fetchAppAdminTokens(); + } + }, [starttime, endtime]); + + useEffect(() => { + console.log('pickCust', pickCust); + }, [pickCust]); + + // ==================================================== || fetchtenantinfo || ==================================================== + const fetchtenantinfo = async () => { + setLoading(true); + console.log('tid', tid); + + await axios + .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`) + .then((res) => { + console.log('fetchtenantinfo', res); + if (res.data.status) { + fetchAppAdminTokens(); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + if (tid) { + fetchtenantinfo(); + } + }, [tid]); + // ================================================== || sendnotifications || ================================================== + const sendnotifications = async () => { + setLoading(true); + await axios + .post(`${process.env.REACT_APP_URL}/utils/sendnotifications`, { + priority: 'high', + registration_ids: admintoken, + data: { + accessid: process.env.REACT_APP_RIDER_ACCESS_ID + }, + notification: { + title: 'Nearle Merchant', + body: 'An Order has been placed successfully,kindly process the same', + sound: 'ring' + } + }) + .then((res) => { + console.log(res); + if (res.data.message == 'Success') { + enqueueSnackbar('Notification sent Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + enqueueSnackbar(err.message, { + variant: 'error', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + setLoading(false); + }); + }; + // =============================================== || creategrouporders || =============================================== + const creategrouporders = async () => { + const arr = dropCust?.map((customer) => ({ + applocationid: pickCust.applocationid, + cancellled: '', + // categoryid: +tenant.categoryid, + configid: 9, + customerid: customer.customerid, + deliveryaddress: customer.address || '', + deliverycharge: +customer.totalcharge || 0, + deliverycity: customer.city || '', + deliverycontactno: customer.contactno || '', + deliverycustomer: customer.firstname || '', + deliveryid: +customer.customerid, + deliverylandmark: customer.landmark || '', + deliverylat: customer.latitude, + deliverylocation: customer.suburb || '', + deliverylocationid: customer.deliverylocationid || 0, + deliverylong: customer.longitude, + // deliverytime: `${dayjs(startdate).format('YYYY-MM-DD HH:mm:ss')} `, + deliverytime: dayjs().format('YYYY-MM-DD HH:mm:ss'), + deliverytype: 'B', + delivered: '', + itemcount: 1, + kms: customer.distance.toString() || 0, + locationid: +pickCust.locationid, + moduleid: +pickCust.moduleid, + orderamount: +customer.totalcharge || 0, + ordercharges: 0.0, + orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + orderheaderid: 0, + orderid: '', // + ordernotes: otherinstructions, + orderstatus: 'created', + ordervalue: +customer.totalcharge || 0, + partnerid: pickCust.partnerid, + partneruserid: +userid, + paymentstatus: 1, + paymenttype: 42, + pending: '', + pickupaddress: pickCust.address || '', + pickupcity: pickCust.locationcity || '', + pickupcontactno: pickCust.contactno || '', + pickupcustomer: pickCust.locationname || '', + pickuplandmark: pickCust.landmark || '', + pickuplat: pickCust.latitude, + pickuplocation: pickCust.suburb || '', + pickuplocationid: pickCust.locationid || 0, + pickuplong: pickCust.longitude, + processing: '', + ready: '', + remarks: '', + taxamount: 0.0, + tenantid: pickCust.tenantid, + tenantuserid: 0 + })); + console.log('arr', arr); + + if (!tenId) { + opentoast('Choose Client ', 'warning'); + } else { + setLoading(true); + + await axios + .post(`${process.env.REACT_APP_URL}/orders/createorders`, arr) + .then((res) => { + if (res.data.status) { + enqueueSnackbar('Order Created Successfully', { + variant: 'success', + anchorOrigin: { vertical: 'top', horizontal: 'right' }, + autoHideDuration: 1000 + }); + if (admintoken) { + // notifyadmin(admintoken); + sendnotifications(); + } + navigate('/nearle/orders'); + } else { + opentoast(res.data.message, 'warning'); + } + setLoading(false); + console.log(res); + }) + .catch((err) => { + console.log(err); + // opentoast(err.data.message, 'warning'); + setLoading(false); + }); + } + console.log(arr); + }; + + return ( + <> + {loading && } + {/* */} + + + + + Multiple Orders + + + + + + {/* Business Location */} + + {tenantLocations?.length === 1 ? ( + + + + ) + }} + /> + ) : ( + `${option.locationname} (${option.suburb})`} + onChange={(event, value, reason) => { + if (value) { + setTid(value.tenantid); + setIsLocation(true); + setPickCust(value); + } + if (reason === 'clear') setIsLocation(false); + }} + renderInput={(params) => } + /> + )} + + + {/* Date Picker */} + + + { + let diff = dayjs().diff(dayjs(dayjs(e).format('YYYY-MM-DD')), 'd'); + + if (diff <= 0) { + setStartdate(e); + + let arr = []; + timeslotarr.forEach((val) => { + if (dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { + arr.push(val); + } + }); + + if (arr[0]) { + setOrderarr([ + { + sno: 1, + address: '', + customerid: '', + deliverytime: dayjs(arr[0]), + deliverylocationid: '', + clientname: '', + contactno: '', + latitude: '', + longitude: '' + } + ]); + } else { + setOrderarr([]); + } + } else { + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + /> + + + + + + + {/* ===================================================== || Pickup || ===================================================== */} + {pickCust && ( + + + + + Pickup Location + Address + + + + + {pickCust?.locationname} + {pickCust?.address} + + +
+
+ )} + + {/* ===================================================== || Drop || ===================================================== */} + + { + if (!isLocation) { + opentoast('Select Business Location', 'warning'); + } else { + setIsCustomerOpen(true); + setSearchCustList(''); + } + }} + > + Select Customers + + } + > + + + + + S.No + Customer + Address + Kms + Charge + Action + + + + {!dropCust && ( + + + + + + )} + {dropCust?.map((customer, index) => ( + + {index + 1} + {customer.firstname} + {customer.address} + {customer.distance} + {`₹${customer.totalcharge}.00`} + + { + handleCheckboxChange(event, customer)} + /> + } + + + ))} + {dropCust?.length != 0 && ( + + + Total + + + + + {`${totaldist} `} + + + {`₹${totalAmt}.00`} + + + + )} + +
+
+
+ + {/* ================================================= || Riders Map || ================================================= */} + + {/* {showMap && dropCust.length >= 1 && } */} + + {/* ================================================= || Notes || ================================================= */} + {dropCust && ( + + + + setOtherinstructions(e.target.value)} + /> + + + + + + + )} + + {/* ============================================= || saved address Dialog || ============================================= */} + { + setIsCustomerOpen(false); + }} + fullWidth + sx={{ minWidth: 'lg' }} + > + {isLoading && } + + + {`Select Drop Customers (${dropCust?.length || 0})`} + + + setSearchCustList(e.target.value)} + sx={{ + '& .MuiOutlinedInput-input': { + p: '10.5px 0px 12px' + }, + bgcolor: 'white' + }} + startAdornment={ + + + + } + endAdornment={ + { + setSearchCustList(''); + }} + > + + + } + autoComplete="off" + /> + + + + + + + {customerlist.length == 0 ? ( + + + + ) : ( + + {customerlist && + customerlist.map((customer, index) => ( + + cust.customerid === customer.customerid)} // Set the checked state of the checkbox based on whether the customer is in `dropCust` + onChange={(event) => handleCheckboxChange(event, customer)} + /> + } + label={ +
+ + {`${customer.firstname} (${customer.contactno})`} + + + + {customer.address} + +
+ } + /> +
+ ))} +
+ )} +
+ + + + +
+ + ); +}; + +export default MultipleOrders; diff --git a/src/pages/nearle/orders/multipleOrders.js b/src/pages/nearle/orders/multipleOrders.js index ca629ea..6a8c6b8 100644 --- a/src/pages/nearle/orders/multipleOrders.js +++ b/src/pages/nearle/orders/multipleOrders.js @@ -1,11 +1,11 @@ import React from 'react'; import Loader from 'components/Loader'; -import { useEffect, useState, Fragment } from 'react'; +import { useEffect, useState, Fragment, useRef } from 'react'; import { useTheme } from '@mui/material/styles'; import MainCard from 'components/MainCard'; import axios from 'axios'; import ClearIcon from '@mui/icons-material/Clear'; -import { SearchOutlined, CloseOutlined } from '@ant-design/icons'; +import { SearchOutlined, CloseOutlined, ExclamationCircleOutlined, FileAddOutlined } from '@ant-design/icons'; import { Empty } from 'antd'; import MyLocationIcon from '@mui/icons-material/MyLocation'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; @@ -16,7 +16,8 @@ var utc = require('dayjs/plugin/utc'); dayjs.extend(utc); import { enqueueSnackbar } from 'notistack'; import { useNavigate } from 'react-router'; -import { GoogleMap, LoadScript, Marker } from '@react-google-maps/api'; +import Papa from 'papaparse'; +import * as XLSX from 'xlsx'; import { FormControl, @@ -24,9 +25,11 @@ import { Grid, Typography, Stack, + Box, Button, TextField, Autocomplete, + Chip, Divider, Dialog, DialogTitle, @@ -45,24 +48,31 @@ import { TableRow, Paper, TableHead, - Box + FormLabel, + RadioGroup, + Radio, + Backdrop, + List, + ListItem, + ListItemText } from '@mui/material'; import CircularLoader from 'components/nearle_components/CircularLoader'; -// import RidersPinPointOSM from './RidersPinPointOSM'; -import RidersPinPoint from './ridersPinPoint'; const MultipleOrders = () => { const navigate = useNavigate(); const theme = useTheme(); + const locationRef = useRef(null); + const tenantRef = useRef(null); + const userid = localStorage.getItem('userid'); + const [locations, setLocations] = useState([]); + const [tenantlist, setTenantlist] = useState([]); const [loading, setLoading] = useState(false); const [btnLoading, setBtnLoading] = useState(false); const [appId, setAppId] = useState(0); - const [tenantLocations, setTenantlocations] = useState([]); - const userid = localStorage.getItem('userid'); - const tenId = localStorage.getItem('tenantid'); - const [tid, setTid] = useState(0); - const [isLocation, setIsLocation] = useState(false); + // const [tenantid, setTenantid] = useState(0); + const tenantid = localStorage.getItem('tenantid') || 0; + const [locationid, setLocationid] = useState(0); const [basePrice, setBasePrice] = useState(0); const [pricePerKm, setPricePerKm] = useState(0); const [minKm, setMinKm] = useState(0); @@ -75,17 +85,31 @@ const MultipleOrders = () => { const [timeslotarr, setTimeslotarr] = useState([]); const [starttime, setStatrttime] = useState(); const [endtime, setEndtime] = useState(); + const [selectedtime, setSelectedtime] = useState(''); const [alertmessage, setAlertmessage] = useState(''); const [otherinstructions, setOtherinstructions] = useState(''); const [admintoken, setAdmintoken] = useState(); const [totaldist, settotaldist] = useState(0); const [totalAmt, settotalAmt] = useState(0); - const [isLoading, setIsLoading] = useState(false); - const [showMap, setShowMap] = useState(false); + const [totalQty, settotalQty] = useState(0); + const [totalCash, settotalCash] = useState(0); + const [users, setUsers] = useState([]); + const [uploadType, setUploadType] = useState(null); + const [tenantValue, setTenantValue] = useState(null); + const [locationValue, setLocationValue] = useState(null); + const [pickupSlotsList, setPickupSlotsList] = useState(null); + const [pickupSlot, setPickupSlot] = useState(null); useEffect(() => { - dropCust && console.log('dropCust', dropCust); - }, [dropCust]); + if (timeslotarr[0]) { + let arr = []; + timeslotarr.map((val) => { + if (dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { + arr.push(val); + } + }); + } + }, [timeslotarr]); // =============================================== || opentoast || =============================================== const opentoast = (message, variant, time) => { @@ -96,24 +120,71 @@ const MultipleOrders = () => { }); console.log(alertmessage); }; + + // 🔹 Smart toast wrapper — prevents duplicate toasts for same message within 3 seconds + let toastCache = {}; + const OpenToast = (message, type = 'info', timeout = 10000) => { + const key = `${type}-${message}`; + if (toastCache[key]) return; // skip duplicates + opentoast(message, type, timeout); // your existing toast/snackbar + toastCache[key] = true; + setTimeout(() => delete toastCache[key], 3000); // reset after delay + }; + // ==============================|| fetchAppLocations ||============================== // + const fetchAppLocations = async () => { + setLoading(true); + try { const locationRes = await axios.get(`${process.env.REACT_APP_URL}/partners/getlocations/?userid=${userid}`); console.log('fetchAppLocations', locationRes.data.details); + setLocations(locationRes.data.details); } catch (err) { console.log('locationRes', err); + OpenToast(err.message, 'error', 5000); + } finally { + setLoading(false); } }; useEffect(() => { fetchAppLocations(); }, []); + // ===================================================== || fetchtenantinfolist || ===================================================== + + const fetchtenantinfolist = async () => { + setLoading(true); + await axios + .get(`${process.env.REACT_APP_URL}/tenants/gettenants/?applocationid=${appId}&status=active`) + + .then((res) => { + console.log(res); + if (res.data.status) { + let arr = []; + res.data.details.map((val) => { + arr.push({ + ...val, + label: `${val.tenantname}` + }); + }); + setTenantlist(arr); + } + setLoading(false); + }) + .catch((err) => { + console.log(err); + setLoading(false); + }); + }; + useEffect(() => { + appId && fetchtenantinfolist(); + }, [appId]); // ============================================= || fetchTenantPricing || ============================================= const fetchTenantPricing = async (id) => { try { - const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${tenId}`); + const pricingResponse = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantpricing/?tenantid=${id}`); console.log('pricingResponse', pricingResponse.data.details); setBasePrice(pricingResponse.data.details.baseprice); setPricePerKm(pricingResponse.data.details.priceperkm); @@ -122,18 +193,17 @@ const MultipleOrders = () => { console.log('fetchTenantPricing error', error); } }; - useEffect(() => { - fetchTenantPricing(); - }, []); // ============================================= || gettenantlocations (branches) || ============================================= const gettenantlocations = async (id) => { try { const res = await axios.get(`${process.env.REACT_APP_URL}/tenants/gettenantlocations/?tenantid=${id}`); console.log('gettenantlocations', res.data.details); if (res.data.details.length == 1) { - setIsLocation(true); setTenantlocations(res.data.details); setPickCust(res.data.details[0]); + setLocationid(res.data.details[0].locationid); + setLocationValue(res.data.details[0].locationid); + setPickupSlotsList(res.data.details[0].slots); } else { setTenantlocations(res.data.details); } @@ -142,15 +212,15 @@ const MultipleOrders = () => { } }; useEffect(() => { - gettenantlocations(tenId); - }, []); + gettenantlocations(tenantid); + }, [tenantid]); // ========================================================= || clientdetails || ========================================================= const clientdetails = async () => { try { let url = searchCustList == '' - ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenId}&pageno=1&pagesize=10` - : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenId}&keyword=${searchCustList}`; + ? `${process.env.REACT_APP_URL}/customers/gettenantcustomers/?tenantid=${tenantid}&pageno=1&pagesize=30` + : `${process.env.REACT_APP_URL}/customers/search/?tenantid=${tenantid}&keyword=${searchCustList}`; await axios .get(url) .then((res) => { @@ -176,37 +246,39 @@ const MultipleOrders = () => { } }; useEffect(() => { - if (tenId) { + if (tenantid) { clientdetails(); } - }, [searchCustList.length > 3, searchCustList == '', tenId]); + }, [searchCustList.length > 3, searchCustList == '', tenantid]); // ========================================================= || calculateTotal(dist , charge) || ========================================================= const calculateTotal = () => { let a1 = 0; let a2 = 0; - dropCust?.map((customer) => { + let a3 = 0; + let a4 = 0; + dropCust.map((customer) => { a1 += customer.distance; a2 += customer.totalcharge; + a3 += customer.quantity; + a4 += customer.collectionamt; }); settotaldist(a1); settotalAmt(a2); + settotalQty(a3); + settotalCash(a4); }; useEffect(() => { - dropCust && calculateTotal(); + calculateTotal(); }, [dropCust]); // ========================================================= || handleCheckboxChange || ========================================================= const handleCheckboxChange = async (event, customer) => { - setIsLoading(true); - console.log('event', event.target.checked); - console.log('customer', customer); + setLoading(true); if (event.target.checked) { // If the checkbox is checked, calculate the distance and add the customer try { const obj = await calculateDistance(customer); - console.log('return of calculateDistance', obj); - const { roundedDistance, totalcharge } = obj; // Create a new customer object with the distance property const updatedCustomer = { @@ -219,84 +291,172 @@ const MultipleOrders = () => { setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); // Log the rounded distance - console.log(`Rounded Distance: ${roundedDistance} km`); + // console.log(`Rounded Distance: ${roundedDistance} km`); } catch (error) { console.error('Failed to calculate distance:', error); + } finally { + setLoading(false); } - setIsLoading(false); } else { // If the checkbox is unchecked, remove the customer from dropCust setDropCust((prevDropCust) => { return prevDropCust.filter((cust) => cust.customerid !== customer.customerid); }); - setIsLoading(false); + setLoading(false); + } + }; + // ========================================================= || handleCheckboxChange1 || ========================================================= + // const handleCheckboxChange1 = async (customer) => { + // console.log('customer', customer); + // setLoading(true); + // try { + // const obj = await calculateDistance(customer); + // const { roundedDistance, totalcharge } = obj; + // // Create a new customer object with the distance property + // const updatedCustomer = { + // ...customer, + // distance: roundedDistance, + // totalcharge: totalcharge + // }; + + // // Add the updated customer object to dropCust + // setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); + + // // Log the rounded distance + // console.log(`Rounded Distance: ${roundedDistance} km`); + // setLoading(false); + // } catch (error) { + // console.error('Failed to calculate distance:', error); + // } + // }; + const handleCheckboxChange1 = async (customer) => { + console.log('customer', customer); + + setLoading(true); + + try { + setDropCust((prevDropCust) => { + const isAlreadySelected = prevDropCust.some((c) => c.firstname === customer.firstname); + + // 🔴 REMOVE if already exists + if (isAlreadySelected) { + return prevDropCust.filter((c) => c.firstname !== customer.firstname); + } + + // 🟢 ADD if not exists (calculate distance) + return prevDropCust; + }); + + // Only calculate distance if customer is not already added + const alreadyExists = dropCust.some((c) => c.firstname === customer.firstname); + + if (!alreadyExists) { + const obj = await calculateDistance(customer); + const { roundedDistance, totalcharge } = obj; + + const updatedCustomer = { + ...customer, + distance: roundedDistance, + totalcharge + }; + + setDropCust((prevDropCust) => [...prevDropCust, updatedCustomer]); + + console.log(`Rounded Distance: ${roundedDistance} km`); + } + } catch (error) { + console.error('Failed to calculate distance:', error); + } finally { + setLoading(false); } }; // ========================================================= || calculateDistance || ========================================================= - const calculateDistance = async (customer) => { - console.log('Distance calculation starts'); - const service = new google.maps.DistanceMatrixService(); - // Helper function to get the distance matrix - const getDistanceMatrix = async (origins, destinations) => { - console.log('origins', origins); - console.log('destinations', destinations); - return new Promise((resolve, reject) => { - console.log('calculation starts'); - service.getDistanceMatrix( - { - origins: [new google.maps.LatLng(origins.latitude, origins.longitude)], - destinations: [new google.maps.LatLng(destinations.latitude, destinations.longitude)], - travelMode: 'DRIVING', - unitSystem: google.maps.UnitSystem.METRIC // Distances in metric units (km) - }, - (response, status) => { - console.log('cal response', response); - console.log('cal status', status); - if (status === 'OK') { - console.log('calcualtion resolved'); - resolve(response); - } else { - console.log('calcualtion rejected'); - reject(new Error(`Error calculating distance: ${status}`)); - } + // 🔹 Main distance calculation function + const calculateDistance = async (customer) => { + const service = new google.maps.DistanceMatrixService(); + + // Helper: safely get distance matrix + const getDistanceMatrix = (origins, destinations) => { + return new Promise((resolve, reject) => { + // 2; + try { + if (!origins || !destinations) { + return reject(new Error('Origin or destination data missing.')); } - ); + service.getDistanceMatrix( + { + origins: [new google.maps.LatLng(origins.latitude, origins.longitude)], + destinations: [new google.maps.LatLng(destinations.latitude, destinations.longitude)], + travelMode: 'DRIVING', + unitSystem: google.maps.UnitSystem.METRIC + }, + (response, status) => { + if (status === 'OK') { + resolve(response); + } else { + reject(new Error(`Google API error: ${status}`)); + } + } + ); + } catch (err) { + reject(new Error(`Unexpected error inside DistanceMatrixService: ${err.message}`)); + } }); }; try { - // Call getDistanceMatrix and wait for the response - const response = await getDistanceMatrix(pickCust, customer); - - // Extract distance from the first result - const distanceInMeters = response.rows[0].elements[0].distance.value; - - // Convert distance from meters to kilometers - const distanceInKilometers = distanceInMeters / 1000; - - // Round the distance to the nearest integer - const roundedDistance = Math.round(distanceInKilometers); - let totalcharge; - if (roundedDistance < minKm) { - console.log('minKm', minKm); - console.log('pricePerKm', pricePerKm); - console.log('basePrice', basePrice); - totalcharge = basePrice; - } else { - console.log('minKm', minKm); - console.log('pricePerKm', pricePerKm); - console.log('basePrice', basePrice); - totalcharge = (roundedDistance - minKm) * pricePerKm + basePrice; - console.log('totalcharge', totalcharge); + // --- Input validation --- + if (!customer || typeof customer !== 'object') { + throw new Error('Invalid customer data: expected an object.'); } - // Return the rounded distance + if (!pickCust || typeof pickCust !== 'object') { + throw new Error('Origin (pickCust) data missing or invalid.'); + } + + // --- Call Google Maps API --- + const response = await getDistanceMatrix(pickCust, customer); + + // --- Validate response structure --- + if (!response.rows?.[0]?.elements?.[0] || !response.rows[0].elements[0].distance?.value) { + throw new Error('Malformed Distance Matrix response: missing distance value.'); + } + // --- Compute distance --- + const distanceInMeters = response.rows[0].elements[0].distance.value; + const distanceInKilometers = distanceInMeters / 1000; + const roundedDistance = Math.round(distanceInKilometers); + + // --- Calculate total charge --- + let totalcharge; + if (roundedDistance < minKm) { + totalcharge = basePrice; + } else { + totalcharge = (roundedDistance - minKm) * pricePerKm + basePrice; + } return { roundedDistance, totalcharge }; } catch (error) { - console.error('Error calculating distance:', error); - throw error; // Rethrow the error to be handled by the caller + // --- Categorized smart error handling --- + console.log('on calculateDistance', error.message); + if (error.message.includes('Google API')) { + console.log('🚨 Google Maps API Error:', error.message); + OpenToast('Invalid file format, upload valid file', 'error', 5000); + } else if (error.message.includes('Invalid coordinates')) { + console.log('📍 Invalid coordinate format:', error.message, 3000); + OpenToast('Invalid coordinate format. Check location data.', 'warning'), 3000; + } else if (error.message.includes('Malformed Distance Matrix')) { + console.log('⚠️ Unexpected Google response structure:', error.message); + OpenToast('Google Distance Matrix returned invalid data.', 'error', 3000); + } else if (error.message.includes('Origin') || error.message.includes('customer')) { + console.log('❌ Missing or invalid input data:', error.message); + OpenToast('Missing or invalid input data for distance calculation.', 'warning', 3000); + } else { + console.log('💥 Unexpected error calculating distance:', error); + OpenToast('Unexpected error during distance calculation.', 'error', 3000); + } + + throw error; // keeps your current flow intact } }; @@ -307,7 +467,7 @@ const MultipleOrders = () => { .get(`${process.env.REACT_APP_URL}/utils/getapplocations/?applocationid=${appId}`) .then((res) => { console.log('fetchTiming', res); - const { opentime, closetime, latitude, longitude, radius } = res.data.details[0]; + const { opentime, closetime } = res.data.details[0]; if (res.data.status) { setStatrttime(`${dayjs().format('MM-DD-YYYY')} ${opentime}`); setEndtime(`${dayjs().format('MM-DD-YYYY')} ${closetime}`); @@ -335,7 +495,7 @@ const MultipleOrders = () => { if (appId) { fetchTiming(); } - }, [starttime, endtime, appId]); + }, [appId]); const fetchAppAdminTokens = async () => { setLoading(true); @@ -357,39 +517,41 @@ const MultipleOrders = () => { }; useEffect(() => { - if (starttime && endtime) { + if (appId) { fetchAppAdminTokens(); } - }, [starttime, endtime]); + }, [appId]); useEffect(() => { console.log('pickCust', pickCust); }, [pickCust]); - - // ==================================================== || fetchtenantinfo || ==================================================== - const fetchtenantinfo = async () => { - setLoading(true); - console.log('tid', tid); - - await axios - .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tid}`) - .then((res) => { - console.log('fetchtenantinfo', res); - if (res.data.status) { - fetchAppAdminTokens(); - } - setLoading(false); - }) - .catch((err) => { - console.log(err); - setLoading(false); - }); - }; useEffect(() => { - if (tid) { - fetchtenantinfo(); - } - }, [tid]); + console.log('dropCust', dropCust); + }, [dropCust]); + // // ==================================================== || fetchtenantinfo || ==================================================== + // const fetchtenantinfo = async () => { + // setLoading(true); + // console.log('tenantid', tenantid); + + // await axios + // .get(`${process.env.REACT_APP_URL}/tenants/gettenantinfo/?tenantid=${tenantid}`) + // .then((res) => { + // console.log('fetchtenantinfo', res); + // if (res.data.status) { + // setTenantid(res.data.details.tenantid); + // } + // setLoading(false); + // }) + // .catch((err) => { + // console.log(err); + // setLoading(false); + // }); + // }; + // useEffect(() => { + // if (tenantid) { + // fetchtenantinfo(); + // } + // }, [tenantid]); // ================================================== || sendnotifications || ================================================== const sendnotifications = async () => { setLoading(true); @@ -427,48 +589,256 @@ const MultipleOrders = () => { setLoading(false); }); }; - // =============================================== || creategrouporders || =============================================== - const creategrouporders = async () => { - const arr = dropCust?.map((customer) => ({ + + const cleanReceiverName = (name) => { + if (typeof name !== 'string') return name; + return name.replace(/^[\d.\s]+/, '').trim(); + }; + + const handleFileUpload = (event) => { + console.log('Normal upload started...'); + try { + const file = event.target.files?.[0]; + if (!file) { + opentoast('No file selected.', 'warning'); + return; + } + + const fileName = file.name.toLowerCase(); + const isCSV = fileName.endsWith('.csv'); + const isExcel = fileName.endsWith('.xls') || fileName.endsWith('.xlsx'); + + // Invalid file + if (!isCSV && !isExcel) { + opentoast('Invalid file type. Please upload a CSV or Excel file.', 'warning'); + return; + } + + // ---------------------- CSV ---------------------- + if (isCSV) { + Papa.parse(file, { + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: (results) => { + const data = results.data || []; + if (data.length === 0) { + opentoast('CSV file is empty or invalid.', 'warning'); + setUsers([]); + return; + } + const cleanedData = data.map((row) => ({ + ...row, + firstname: cleanReceiverName(row.firstname) + })); + console.log('✅ Parsed CSV Data:', cleanedData); + setUsers(cleanedData); + opentoast('CSV file uploaded successfully, ✅', 'success', 3000); + opentoast(' Press Continue to add delivery customers', 'warning', 5000); + }, + error: (error) => { + console.error('❌ CSV Parse Error:', error.message); + opentoast(`CSV parsing failed: ${error.message}`, 'warning'); + } + }); + } + + // ---------------------- EXCEL (.xls / .xlsx) ---------------------- + if (isExcel) { + const reader = new FileReader(); + + reader.onload = (e) => { + try { + const data = e.target.result; + + // Use correct mode for binary Excel formats + const workbook = XLSX.read(data, { + type: 'binary', + cellDates: true, + cellNF: false, + cellText: false + }); + const firstSheet = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheet]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: '' }); + + if (!jsonData || jsonData.length === 0) { + opentoast('Excel file is empty or invalid.', 'warning'); + setUsers([]); + return; + } + + const cleanedData = jsonData.map((row) => ({ + ...row, + firstname: cleanReceiverName(row.firstname) + })); + + console.log('✅ Parsed Excel Data:', cleanedData); + setUsers(cleanedData); + opentoast('Excel file uploaded successfully ✅, press continue', 'success', 3000); + } catch (err) { + console.error('❌ Excel Parse Error:', err); + opentoast(`Error reading Excel: ${err.message}`, 'warning'); + } + }; + + // ✅ Key fix: use readAsBinaryString for both .xls & .xlsx + reader.readAsBinaryString(file); + } + } catch (err) { + console.error('Unexpected error during file upload:', err.message); + opentoast(`Unexpected error: ${err.message}`, 'warning'); + } + }; + + // your header mapping + const headerMap = { + 'pickupdate(yyyy-mmm-dd)': 'date', + 'sendername*': 'locationname', + 'senderphone*': 'locationcontact', + 'senderaddress*': 'locationaddress', + 'receivername*': 'firstname', + 'receiverphone*': 'contactno', + 'receiveralternatephone*': 'altcontactno', + receiverfulladdress: 'address', + receiverlatitude: 'latitude', + receiverlongitude: 'longitude', + 'itemdescription*': 'description', + Quantity: 'quantity', + ' Collect Cash': 'collectionamt' + }; + + // helper to normalize headers + const normalizeHeader = (header) => header?.toString().trim().toLowerCase().replace(/\s+/g, ''); + + const handleFileDirectUpload = (event) => { + try { + const file = event.target.files?.[0]; + if (!file) { + opentoast('No file selected.', 'warning'); + return; + } + + const fileName = file.name.toLowerCase(); + const isCSV = fileName.endsWith('.csv'); + const isExcel = fileName.endsWith('.xls') || fileName.endsWith('.xlsx'); + + if (!isCSV && !isExcel) { + opentoast('Invalid file type. Please upload a CSV or Excel file.', 'warning'); + return; + } + + const processData = (data, headers) => { + console.log('data', data); + const normalizedMap = {}; + for (const key in headerMap) { + normalizedMap[normalizeHeader(key)] = headerMap[key]; + } + console.log('normalizedMap', normalizedMap); + + const mappedData = data.map((row) => { + const newRow = {}; + for (const key in row) { + const cleanKey = normalizeHeader(key); + const newKey = normalizedMap[cleanKey] || cleanKey; + let value = row[key]; + if (newKey === 'firstname') value = cleanReceiverName(value); + + newRow[newKey] = value; + } + + return newRow; + }); + + const missingCols = Object.keys(headerMap).filter((clientCol) => !headers.includes(normalizeHeader(clientCol))); + + if (missingCols.length > 0) { + isExcel && opentoast(`Missing columns: ${missingCols.join(', ')}`, 'warning'); + } + + console.log('✅ Final Processed Data:', mappedData); + setUsers(mappedData); + opentoast('File uploaded and successfully ', 'success', 3000); + opentoast('Press Continue', 'warning', 3000); + }; + + // ============ CSV handler ============ + if (isCSV) { + Papa.parse(file, { + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: (results) => { + if (!results.data?.length) { + opentoast('CSV file is empty or has no valid rows.', 'warning'); + setUsers([]); + return; + } + const headers = results.meta.fields.map(normalizeHeader); + processData(results.data, headers); + }, + error: (error) => { + console.error('❌ CSV Parsing Error:', error); + opentoast(`CSV parsing failed: ${error.message}`, 'warning'); + } + }); + } + + // ============ Excel handler ============ + if (isExcel) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = e.target.result; + // Try reading as binary first + let workbook; + try { + workbook = XLSX.read(data, { type: 'binary' }); + } catch { + // fallback for modern XLSX files + const arrayBuffer = new Uint8Array(data); + workbook = XLSX.read(arrayBuffer, { type: 'array' }); + } + + const firstSheet = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheet]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: '' }); + + if (!jsonData?.length) { + opentoast('Excel file is empty or invalid.', 'warning'); + setUsers([]); + return; + } + + const headers = Object.keys(jsonData[0]).map(normalizeHeader); + processData(jsonData, headers); + } catch (err) { + console.error('❌ Error processing Excel:', err); + opentoast(`Error reading Excel: ${err.message}`, 'warning'); + } + }; + + // Important: use readAsBinaryString for Excel + reader.readAsBinaryString(file); + } + } catch (err) { + console.error('Unexpected error during file upload:', err); + opentoast(`Unexpected error: ${err.message}`, 'warning'); + } + }; + + // =============================================== || createorders || =============================================== + const createorders = async () => { + // ===================== Build Payload ===================== + const arr = dropCust.map((customer) => ({ applocationid: pickCust.applocationid, - cancellled: '', - // categoryid: +tenant.categoryid, configid: 9, - customerid: customer.customerid, - deliveryaddress: customer.address || '', - deliverycharge: +customer.totalcharge || 0, - deliverycity: customer.city || '', - deliverycontactno: customer.contactno || '', - deliverycustomer: customer.firstname || '', - deliveryid: +customer.customerid, - deliverylandmark: customer.landmark || '', - deliverylat: customer.latitude, - deliverylocation: customer.suburb || '', - deliverylocationid: customer.deliverylocationid || 0, - deliverylong: customer.longitude, - // deliverytime: `${dayjs(startdate).format('YYYY-MM-DD HH:mm:ss')} `, - deliverytime: dayjs().format('YYYY-MM-DD HH:mm:ss'), - deliverytype: 'B', - delivered: '', - itemcount: 1, - kms: customer.distance.toString() || 0, - locationid: +pickCust.locationid, - moduleid: +pickCust.moduleid, - orderamount: +customer.totalcharge || 0, - ordercharges: 0.0, - orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), - orderheaderid: 0, - orderid: '', // - ordernotes: otherinstructions, - orderstatus: 'created', - ordervalue: +customer.totalcharge || 0, partnerid: pickCust.partnerid, partneruserid: +userid, paymentstatus: 1, paymenttype: 42, - pending: '', pickupaddress: pickCust.address || '', - pickupcity: pickCust.locationcity || '', + pickupcity: pickCust.city || '', pickupcontactno: pickCust.contactno || '', pickupcustomer: pickCust.locationname || '', pickuplandmark: pickCust.landmark || '', @@ -476,82 +846,175 @@ const MultipleOrders = () => { pickuplocation: pickCust.suburb || '', pickuplocationid: pickCust.locationid || 0, pickuplong: pickCust.longitude, - processing: '', - ready: '', - remarks: '', - taxamount: 0.0, tenantid: pickCust.tenantid, - tenantuserid: 0 + + customerid: +customer?.customerid, + deliveryaddress: customer.address || '', + deliverycharge: +customer.totalcharge || 0, + deliverycity: customer.city || '', + deliverycontactno: customer.contactno?.toString() || '', + deliverycustomer: customer.firstname || '', + deliveryid: +customer.customerid, + deliverylandmark: customer.landmark || '', + deliverylat: customer.latitude?.toString() || '', + deliverylocation: customer.suburb || '', + deliverylocationid: customer.deliverylocationid || 0, + deliverylong: customer.longitude?.toString() || '', + deliverytime: `${dayjs(startdate).format('YYYY-MM-DD')} ${dayjs(selectedtime.$d).format('HH:mm:ss')}`, + deliverytype: 'B', + + itemcount: 1, + quantity: customer.quantity, + collectionamt: customer.collectionamt, + kms: customer.distance?.toString() || '0', + locationid: +pickCust.locationid, + moduleid: +pickCust.moduleid, + + orderamount: +customer.totalcharge || 0, + ordercharges: 0.0, + orderdate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + ordernotes: otherinstructions, + orderstatus: 'created', + ordervalue: +customer.totalcharge || 0, + pickupSlot })); + console.log('arr', arr); - if (!tenId) { - opentoast('Choose Client ', 'warning'); - } else { - setLoading(true); - - await axios - .post(`${process.env.REACT_APP_URL}/orders/createorders`, arr) - .then((res) => { - if (res.data.status) { - enqueueSnackbar('Order Created Successfully', { - variant: 'success', - anchorOrigin: { vertical: 'top', horizontal: 'right' }, - autoHideDuration: 1000 - }); - if (admintoken) { - // notifyadmin(admintoken); - sendnotifications(); - } - navigate('/nearle/orders'); - } else { - opentoast(res.data.message, 'warning'); - } - setLoading(false); - console.log(res); - }) - .catch((err) => { - console.log(err); - // opentoast(err.data.message, 'warning'); - setLoading(false); - }); + // ===================== Validation ===================== + if (!tenantid) { + opentoast('Choose Client', 'warning'); + return; } - console.log(arr); + setLoading(true); + try { + const res = await axios.post(`${process.env.REACT_APP_URL}/orders/createorders`, arr); + if (res.data.status) { + opentoast('Order Created Successfully', 'success', 2000); + if (admintoken) { + sendnotifications(); + } + navigate('/nearle/orders'); + setLoading(false); + } else { + console.log(res.data); + console.error('Create order failed (API response):', res.data); + opentoast(res?.data?.message || 'Order creation failed. Please try again.', 'warning', 3000); + } + } catch (err) { + opentoast(err.message, 'error', 2000); + console.log('create orders', err.message); + console.error('Create order error:', { + message: err.message, + response: err.response, + request: err.request, + stack: err.stack + }); + + // Exact but short error for user + let toastMessage = 'Something went wrong. Please try again.'; + + if (err.response) { + // Server responded with error + toastMessage = err.response.data?.message || `Server error (${err.response.status})`; + } else if (err.request) { + // No response received + toastMessage = 'Network error. Check your internet connection.'; + } + opentoast(toastMessage, 'error'); + setLoading(false); + } finally { + setLoading(false); + setBtnLoading(false); + } + }; + + const [fileName, setFileName] = useState(''); + const removeFileExtension = (fileName) => { + return fileName.replace(/\.[^/.]+$/, ''); + }; + + const onFileChange = (event) => { + const file = event.target.files[0]; + if (!file) return; + const cleanedName = removeFileExtension(file.name); + setFileName((prev) => (prev ? `${prev}, ${cleanedName}` : cleanedName)); + if (tenantid === 916) { + handleFileDirectUpload(event); + } else { + handleFileDirectUpload(event); + // handleFileUpload(event); + } + }; + + const handleQuantityChange = (customerid, value) => { + setDropCust((prev) => prev.map((cust) => (cust.customerid === customerid ? { ...cust, quantity: Number(value) || 0 } : cust))); + }; + const handleCollectionAmtChange = (customerid, value) => { + setDropCust((prev) => prev.map((cust) => (cust.customerid === customerid ? { ...cust, collectionamt: Number(value) || 0 } : cust))); }; return ( <> - {loading && } - {/* */} - - - - - Group Orders - - - - + {loading && ( + <> + + {/* */} + + )} + { + theme.zIndex.drawer + 1 + }} + open={btnLoading} // when loader = true, backdrop covers the page + > + + + } + + + {/* ===================================================== ||Business Location || ===================================================== */} + - {/* Business Location */} - + Create Multiple Order + + {tenantLocations?.length === 1 ? ( @@ -564,223 +1027,523 @@ const MultipleOrders = () => { fullWidth options={tenantLocations || []} getOptionLabel={(option) => `${option.locationname} (${option.suburb})`} - onChange={(event, value, reason) => { - if (value) { - setTid(value.tenantid); - setIsLocation(true); - setPickCust(value); + value={locationValue} + onOpen={(event) => { + if (!appId && !tenantid) { + event.preventDefault(); + + OpenToast('Please select Location and Tenant first!', 'warning', 3000); + + setTimeout(() => { + locationRef.current?.focus(); + }, 0); + } else if (!tenantid) { + event.preventDefault(); + + OpenToast('Please select Tenant first!', 'warning', 3000); + + setTimeout(() => { + tenantRef.current?.focus(); + }, 0); } - if (reason === 'clear') setIsLocation(false); }} - renderInput={(params) => } + onChange={(event, value, reason) => { + if (reason === 'clear') { + setLocationid(0); + setLocationValue(null); + setPickCust(null); + } else { + setLocationid(value?.locationid || 0); + setLocationValue(value); + setPickCust(value); + setPickupSlotsList(value?.slots); + } + }} + renderInput={(params) => } /> )} - - {/* Date Picker */} - - - { - let diff = dayjs().diff(dayjs(dayjs(e).format('YYYY-MM-DD')), 'd'); - - if (diff <= 0) { - setStartdate(e); - - let arr = []; - timeslotarr.forEach((val) => { - if (dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { - arr.push(val); - } - }); - - if (arr[0]) { - setOrderarr([ - { - sno: 1, - address: '', - customerid: '', - deliverytime: dayjs(arr[0]), - deliverylocationid: '', - clientname: '', - contactno: '', - latitude: '', - longitude: '' - } - ]); - } else { - setOrderarr([]); - } - } else { - opentoast('choose Upcoming Date', 'warning'); - setStartdate(NaN); - } - }} - /> - - - - {/* ===================================================== || Pickup || ===================================================== */} - {pickCust && ( - - - - - Pickup Location - Address - - - - - {pickCust?.locationname} - {pickCust?.address} - - -
-
- )} + {/* ===================================================== || Pickup || ===================================================== */} + + + {locationid !== 0 && ( + + + + + + )} + {/* ================================================= || Time || ================================================= */} - {/* ===================================================== || Drop || ===================================================== */} + + + + + { + setStartdate(e); + let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd'); + console.log('dateres11'); + console.log(dateres11); + setSelectedtime(''); + if (dateres11 <= 0) { + console.log('startdate', e); + setStartdate(e); - { - if (!isLocation) { - opentoast('Select Business Location', 'warning'); - } else { - setIsCustomerOpen(true); - setSearchCustList(''); - } - }} - > - Select Customers - - } - > - - - - - S.No - Customer - Address - Kms - Charge - Action - - - - {!dropCust && ( - - - - - - )} - {dropCust?.map((customer, index) => ( - - {index + 1} - {customer.firstname} - {customer.address} - {customer.distance} - {`₹${customer.totalcharge}.00`} - - { - handleCheckboxChange(event, customer)} - /> + let arr = []; + timeslotarr.map((val) => { + if (dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0) { + arr.push(val); + } + }); + } else { + setAlertmessage('choose Upcoming Date'); + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + value={dayjs(startdate)} + sx={{ width: 'auto', mt: 0 }} + disablePast + /> + + + {/* {timeslotarr.length > 0 && ( + + + + + + + Time + + + + + + { + setStartdate(e); + let dateres11 = dayjs().diff(dayjs(`${dayjs(e).format('YYYY-MM-DD')}`), 'd'); + console.log('dateres11'); + console.log(dateres11); + setSelectedtime(''); + if (dateres11 <= 0) { + console.log('startdate', e); + setStartdate(e); + + let arr = []; + timeslotarr.map((val) => { + if ( + dayjs().diff(dayjs(`${dayjs(e).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + arr.push(val); + } + }); + } else { + setAlertmessage('choose Upcoming Date'); + opentoast('choose Upcoming Date', 'warning'); + setStartdate(NaN); + } + }} + value={dayjs(startdate)} + sx={{ width: 'auto', mt: 0 }} + disablePast + /> + + + + + + + {timeslotarr.map((val, index) => { + if ( + dayjs().diff(dayjs(`${dayjs(startdate).format('MM-DD-YYYY')} ${dayjs(val).format('HH:mm:ss')}`), 'm') <= 0 + ) { + return ( + + + { + console.log('selectedtime', val); + setSelectedtime(val); + }} + // onClick={() => { + // if (distance > appLocaRadius) { + // setOpen(true); + // } else if (showDistance) { + // console.log('selectedtime', val); + // setSelectedtime(val); + // } else { + // opentoast('Out of city limit', 'error'); + // } + // }} + /> + + + ); + } + })} + + + + + + )} */} + + + { + if (reason === 'clear') { + setSelectedtime(null); + setPickupSlot(null); + } else { + // Convert to AM/PM and merge with date + const formattedTime = dayjs(newValue.time, 'HH:mm').format('hh:mm A'); + setSelectedtime(formattedTime); + const finalDateTime = dayjs(`${startdate} ${formattedTime}`, 'MM-DD-YYYY hh:mm A').format('YYYY-MM-DD hh:mm A'); + setPickupSlot(finalDateTime); } - - - ))} - {dropCust?.length != 0 && ( - - - Total - - - - - {`${totaldist} `} - - - {`₹${totalAmt}.00`} - - - - )} - -
-
-
- - {/* ================================================= || Riders Map || ================================================= */} - - {/* {showMap && dropCust.length >= 1 && } */} - - {/* ================================================= || Notes || ================================================= */} - {dropCust && ( - - - - setOtherinstructions(e.target.value)} - /> - - - - + }} + getOptionLabel={(option) => `${option.name} (${dayjs(option.time, 'HH:mm').format('hh:mm A')})`} + renderInput={(params) => } + /> + +
-
- )} + + {/* ===================================================== || Drop || ===================================================== */} + + + + + Drop ({dropCust?.length || 0}) + + + {/* ================= Upload CSV ================= */} + {uploadType === 0 && ( + <> + {fileName && ( + + + + {fileName} + + + )} + + + + + + )} + + {/* ================= Continue ================= */} + {users.length >= 1 && uploadType === 0 && ( + + )} + + {/* ================= Select Customers ================= */} + {uploadType === 1 && ( + + )} + + {/* ================= Upload Type ================= */} + + + Upload Type + { + if (!locationid) { + OpenToast('Please select Business Location!', 'warning', 3000); + return; + } + setUploadType(Number(e.target.value)); + setDropCust([]); + setUsers([]); + setFileName(''); + }} + > + } label="Excel / CSV" /> + } label="Selection" /> + + + + + + } + > + + + {dropCust?.length > 0 ? ( + <> + + + S.No + Customer + Address + Quantity + + + Cash Collect + + Kms + + Charge + Action + + + + + {dropCust?.map((customer, index) => ( + + {index + 1} + {customer.firstname} + {customer.address} + + {uploadType == 0 ? ( + {customer.quantity} + ) : ( + handleQuantityChange(customer.customerid, e.target.value)} + inputProps={{ min: 0 }} + /> + )} + + + {uploadType == 0 ? ( + ₹{Number(customer.collectionamt || 0).toFixed(2)} + ) : ( + { + if (e.target.value <= 0) { + handleCollectionAmtChange(customer.customerid, 0); + } else { + handleCollectionAmtChange(customer.customerid, e.target.value); + } + }} + inputProps={{ min: 0 }} + InputProps={{ + startAdornment: + }} + /> + )} + + + {customer.distance} + {`₹${customer?.totalcharge?.toFixed(2)}`} + + { + <> + handleCheckboxChange(event, customer)} + onClick={() => handleCheckboxChange1(customer)} + /> + + } + + + ))} + {dropCust?.length != 0 && ( + + Total + + + + {`${totalQty} `} + + + + {`${totalCash?.toFixed(2)} `} + + + {`${totaldist} `} + + + {`₹${totalAmt?.toFixed(2)}`} + + + + + )} + + + ) : ( + + {/* Header */} + + {' '} + + Important Instructions + + + + {/* Ordered List */} + + + Choose either Upload Type to upload CSV/Excel files, or + Selection Type to select from saved customers. + + + + Uploaded CSV or Excel files must follow the required format and contain the correct column names. + + + + Multiple files can be uploaded, but only one file at a time. + + + + Invalid or incorrectly formatted files will not be processed. + + + + )} +
+
+
+
+ + + {/* ================================================= || Notes || ================================================= */} + + + + setOtherinstructions(e.target.value)} + /> + + + + + + + + {/* ============================================= || saved address Dialog || ============================================= */} { fullWidth sx={{ minWidth: 'lg' }} > - {isLoading && } - {`Select Drop Customers (${dropCust?.length || 0})`} + {`Select Drop Customers (${dropCust.length || 0})`} { - {customerlist.length == 0 ? ( + {customerlist?.length == 0 ? ( ) : ( {customerlist && - customerlist.map((customer, index) => ( + customerlist?.map((customer, index) => ( cust.customerid === customer.customerid)} // Set the checked state of the checkbox based on whether the customer is in `dropCust` + checked={dropCust.some((cust) => cust.customerid === customer.customerid)} // Set the checked state of the checkbox based on whether the customer is in `dropCust` onChange={(event) => handleCheckboxChange(event, customer)} /> } @@ -872,22 +1634,19 @@ const MultipleOrders = () => { diff --git a/src/pages/nearle/orders/orders.js b/src/pages/nearle/orders/orders.js index 014376e..7691fa1 100644 --- a/src/pages/nearle/orders/orders.js +++ b/src/pages/nearle/orders/orders.js @@ -89,6 +89,9 @@ const Orders = () => { const [searchword, setSearchword] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); + const getValueColor = (value) => { + return value !== 0 ? 'red' : 'inherit'; + }; useEffect(() => { const handler = setTimeout(() => { @@ -162,7 +165,7 @@ const Orders = () => { anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: 2000 }); - getOrders(); + refetchOrders(); fetchorderscount(); setCancelOpen(false); } @@ -215,7 +218,8 @@ const Orders = () => { fetchNextPage, isLoading: isLoadingGeyOrders, hasNextPage, - isFetchingNextPage + isFetchingNextPage, + refetch: refetchOrders // status: rowdataStatus } = useInfiniteQuery({ queryKey: [tabstatus, startdate, enddate, page, rowsPerPage, debouncedSearch, locationId], @@ -528,7 +532,7 @@ const Orders = () => { sx={{ visibility: searchword ? 'visible' : 'hidden' }} onClick={() => { setSearchword(''); - getOrders(); + refetchOrders(); fetchorderscount(); }} > @@ -574,9 +578,16 @@ const Orders = () => { S.No - Orders + + {' '} + Orders Location + Pickup Drop + Qty + Cod + kms + Charges Notes Status {currentStatus == 'created' && ( @@ -623,9 +634,9 @@ const Orders = () => { {rows?.length == 0 && ( <> - + - + @@ -645,72 +656,99 @@ const Orders = () => { + + {row.locationname} {row.locationsuburb && `- ${row.locationsuburb}`} + {row.orderid} - - {dayjs(row.deliverydate).utc().format('DD/MM/YYYY')} - - - {dayjs(row.deliverydate).utc().format('hh:mm A')} - + + + {dayjs(row.pickupslot).format('DD/MM/YYYY')} + + + + {dayjs(row.pickupslot).format('hh:mm A')} + + + {' '} + {/* + + + {dayjs(row.orderdate).utc().format('DD/MM/YYYY')} + + + {dayjs(row.orderdate).utc().format('hh:mm A')} + + + - + + + {dayjs(row.deliverydate).utc().format('DD/MM/YYYY')} + + + {dayjs(row.deliverydate).utc().format('hh:mm A')} + + + */} - - {row.pickupcustomer} - - {row.pickupcontactno} - + {row.pickupcustomer} + {row.pickupcontactno} - - {row.pickupsuburb || row.pickupaddress.slice(0, 20)} - + {row.pickupsuburb || row.pickupaddress.slice(0, 20)} - - {row.deliverycustomer} - - {row.deliverycontactno} - + {row.deliverycustomer} + {row.deliverycontactno} - - {/* {row.pickupaddress.slice(0, 20)} */} - {row.deliverysuburb || row.deliveryaddress.slice(0, 20)} + + {row.deliverysuburb || + (row.deliveryaddress?.length > 20 ? `${row.deliveryaddress.slice(0, 20)}...` : row.deliveryaddress)} - {row.ordernotes} - {/* */} + + + + {row.quantity} + + + + + ₹{row.collectionamt.toFixed(2)} + + + + + {row.kms} + + + + + + ₹{row.deliverycharge.toFixed(2)} + + + + {row.ordernotes} + {row.orderstatus === 'pending' && } {row.orderstatus === 'modified' && } {row.orderstatus === 'cancelled' && } - {row.orderstatus === 'delivered' && } + {row.orderstatus === 'delivered' && } {row.orderstatus === 'processing' && } {row.orderstatus === 'ready' && } {row.orderstatus === 'confirmed' && } @@ -777,6 +815,16 @@ const Orders = () => { + {' '} - diff --git a/src/pages/nearle/reports/RiderLocationMap.js b/src/pages/nearle/reports/RiderLocationMap.js new file mode 100644 index 0000000..5dfabf2 --- /dev/null +++ b/src/pages/nearle/reports/RiderLocationMap.js @@ -0,0 +1,76 @@ +import { Button } from '@mui/material'; +import { LoadScriptNext, GoogleMap, Marker, OverlayView } from '@react-google-maps/api'; + +const containerStyle = { + width: '100%', + height: 'calc(100vh - 150px)' +}; + +export default function RiderLocationMap({ riderLocations }) { + console.log('riderLocations', riderLocations); + + const center = { + lat: Number(riderLocations?.[0]?.latitude || 11.0056), + lng: Number(riderLocations?.[0]?.longitude || 76.9661) + }; + const GreenIcon = { + url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png', + scaledSize: new window.google.maps.Size(25, 41), + anchor: new window.google.maps.Point(12, 41) + }; + + const RedIcon = { + url: 'https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png', + scaledSize: new window.google.maps.Size(25, 41), + anchor: new window.google.maps.Point(12, 41) + }; + + return ( + + + {riderLocations && + riderLocations?.map((r, index) => { + const lat = Number(r.latitude); + const lng = Number(r.longitude); + return ( +
+ {/* Marker */} + + +
+ +
+
+
+ ); + })} +
+
+ ); +} diff --git a/src/pages/nearle/reports/orderSummary.js b/src/pages/nearle/reports/orderSummary.js index 527ce7c..1239f9d 100644 --- a/src/pages/nearle/reports/orderSummary.js +++ b/src/pages/nearle/reports/orderSummary.js @@ -32,7 +32,8 @@ import { FormControl, OutlinedInput, InputAdornment, - Collapse + Collapse, + Badge } from '@mui/material'; import ClearIcon from '@mui/icons-material/Clear'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; @@ -64,6 +65,7 @@ import { useTheme } from '@mui/material/styles'; import TitleCard from '../titleCard'; import { getreportlocationsummary, gettenantlocations } from '../api/api'; import CircularLoader from 'components/nearle_components/CircularLoader'; +import { getValueColor } from 'components/nearle_components/getValueColor'; function formatNumberToRupees(value) { return new Intl.NumberFormat('en-IN', { @@ -104,12 +106,20 @@ export default function OrdersReport() { const [totalDeliComplete, setTotalDeliComplete] = useState(0); const [totalDeliCancel, setTotalDeliCancel] = useState(0); const [searchword, setSearchword] = useState(''); + const [debouncedSearch, setDebouncedSearch] = useState(''); const textFieldRef = useRef(null); const [ridersdata, setRidersdata] = useState(null); const [selectedLocation, setSelectedLocation] = useState(null); const [locationId, setLocationId] = useState(0); const [searchLocation, setSearchLocation] = useState(''); + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedSearch(searchword); + }, 400); + + return () => clearTimeout(handler); + }, [searchword]); useEffect(() => { console.log('openRow', openRow); }, [openRow]); @@ -154,7 +164,7 @@ export default function OrdersReport() { data: rows, error: reportsError } = useQuery({ - queryKey: [startdate, enddate, locationId], + queryKey: [startdate, enddate, locationId, debouncedSearch], queryFn: getreportlocationsummary }); // ==============================|| getriderlocationsummary by tenid (orders summary)||============================== // @@ -230,7 +240,9 @@ export default function OrdersReport() { {startdate && enddate && ( - + + + @@ -411,22 +423,40 @@ export default function OrdersReport() { {index + 1}
- - {row.locationname} + + {row.locationname} + Id : {row.locationid} - {row.totalorders} - {row.Orderspending} - {row.orderscompleted} - {row.orderscancelled} - {row.deliveriespending} - {row.deliveriescompleted} - {row.deliveriescancelled} + + {row.totalorders} + + + {row.Orderspending} + + + {row.orderscompleted} + + + {row.orderscancelled} + + + {' '} + {row.deliveriespending} + + + {' '} + {row.deliveriescompleted} + + + {' '} + {row.deliveriescancelled} + - + - {rider?.totalorders} - {rider?.pending} - {rider?.assigned} - {rider?.accepted} - {rider?.picked} - {rider?.arrived} - {rider?.skipped} - {rider?.delivered} + + {rider?.totalorders} + + + {rider?.pending} + + + {rider?.assigned} + + + {rider?.accepted} + + + {rider?.picked} + + + {rider?.arrived} + + + {rider?.skipped} + + + {rider?.delivered} + {/* */} - new Date('2024-01-01T' + timeString + 'Z').toLocaleTimeString('en-US', { - timeZone: 'UTC', - hour12: true, - hour: '2-digit', - minute: '2-digit' - }); +const drawerWidth = 350; -// ==============================|| RidersLogs ||============================== // - -export default function RidersLogs() { - const tenantid = localStorage.getItem('tenantid'); - const [rowsPerPage, setRowsPerPage] = useState(10); - const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD')); - const [searchword, setSearchword] = useState(''); - const [showClose, SetShowClose] = useState(false); - - /* ============================================= || fetchRidersLogs| ============================================= */ +const RidersLogs = () => { + const theme = useTheme(); + const isDesktop = useMediaQuery('(min-width:900px)'); + const [open, setOpen] = useState(false); + const [selectedRiders, setSelectedRiders] = useState([]); + const [riderSearch, setRiderSearch] = useState(''); + const appId = 1; const { - data: rows = [], // Default to empty array - isLoading: IsRiderLogsLoading, - isError: IsRiderLogsError, - error: RiderLogsError + data: riders, + isLoading: ridersIsLoading, + isFetching: riderIsFetching, + refetch: riderLogsRefetch, + error: riderLogsError } = useQuery({ - queryKey: [tenantid, startdate], // Meaningful query key + queryKey: [appId, dayjs().format('YYYY-MM-DD'), riderSearch], queryFn: fetchRidersLogs, - enabled: !!tenantid && !!startdate, // Fetch only if appId & startdate exist - refetchInterval: 300000 // Auto-fetch every 5 minutes + refetchInterval: 5 * 60 * 1000 }); - React.useEffect(() => { - setRowsPerPage(rows?.length + 1); - }, [rows]); - { - IsRiderLogsError && console.log('RiderLogsError', RiderLogsError); - } + useEffect(() => { + console.log('riders', riders); + // const sortedRiders = riders?.sort((a, b) => a.firstname.localeCompare(b.firstname)); + setSelectedRiders(riders); + }, [riders]); + + useEffect(() => { + console.log('selectedRiders', selectedRiders); + }, [selectedRiders]); + + useEffect(() => { + setOpen(isDesktop); + }, [isDesktop]); return ( - <> - {IsRiderLogsLoading && } - - - - - - - + + { + theme.zIndex.drawer + 1 + }} + open={ridersIsLoading || riderIsFetching} // when loader = true, backdrop covers the page + > + + + } + + + {/* Drawer */} + !isDesktop && setOpen(false)} + ModalProps={{ keepMounted: true }} + sx={{ + '& .MuiDrawer-paper': { + width: drawerWidth, + position: 'absolute', + left: 0, + top: 0, + height: '100%', + overflowY: 'auto', + transition: 'transform 0.35s ease-in-out', + zIndex: 13 + } + }} + > + {/* Search */} + + setRiderSearch(e.target.value)} + sx={{ + height: 60, + bgcolor: 'white', + '& .MuiOutlinedInput-notchedOutline': { + borderBottom: '1px solid', + borderColor: theme.palette.secondary.light } - endAdornment={ - showClose && ( - - { - setSearchword(''); - SetShowClose(false); - }} + }} + /> + + + + { + if (e.target.checked) { + setSelectedRiders(riders); + } + }} + /> + + + + + + + {/* Rider List */} + + {/* Individuals */} + {ridersIsLoading || riderIsFetching + ? Array.from({ length: 10 }).map((_, index) => ( + + + + + + + } + secondary={} /> - - ) - } - aria-describedby="header-search-text" - inputProps={{ - 'aria-label': 'weight' + + + + + + + + + + )) + : riders?.map((row) => { + return ( + + + + {row.userid} + + + {dayjs(row.logdate).format('DD/MM/YYYY hh:mm A')} + + + } + > + + { + if (e.target.checked) { + // SELECT ONE RIDER + setSelectedRiders([row]); + } else { + // UNCHECK -> SELECT ALL + setSelectedRiders(riders); + } + }} + /> + + + + {row.username?.slice(0, 25) || ''} + {row.username?.length > 25 && '...'} + {/* {row.status === 'active' && } */} + + ) : ( + + {row.firstname || ''} + {row.lastname ? ` ${row.lastname}` : ''} + + ) + } + secondary={ + + {row.contactno || '##########'} + + } + /> + + + + + ); + })} + + + + {/* AppBar */} + + + + + setOpen(!open)}> + + + + + Riders Locations + + + + + + + + + {/* Map */} + + {(ridersIsLoading || riderIsFetching) && ( + + {/* */} + - - - - } - > - - - - - S.No - ID - Rider - LogDate - Shift(HRS) - Login - Logout - WRK(HRS) - Shift(HRS) - BRK(HRS) - Status - - - - {rows.length == 0 ? ( - - - - - - ) : ( - rows.map((row, index) => ( - - {index + 1} - {row.userid} - {row.username} - - {' '} - - + + )} - {row.shifthours} - - {row.login != '' && ( - - )} - - - {row.logout != '' && ( - - )} - - - {row.workhours} - {row.shorthours} - {row.breakhours} - - {row.logstatus == 0 ? ( - - ) : ( - - )} - - - )) - )} - -
-
+ {selectedRiders?.length > 0 && } + {riderLogsError && ( + + mantis + + )} +
+
- + ); -} +}; + +export default RidersLogs; diff --git a/src/pages/nearle/reports/ridersummary.js b/src/pages/nearle/reports/ridersummary.js index fcab777..b091af2 100644 --- a/src/pages/nearle/reports/ridersummary.js +++ b/src/pages/nearle/reports/ridersummary.js @@ -49,6 +49,7 @@ import Loader from 'components/Loader'; import TitleCard from '../titleCard'; import { fetchRidersSummary } from '../api/api'; import { useTheme } from '@mui/material/styles'; +import { getValueColor } from 'components/nearle_components/getValueColor'; // table filter function descendingComparator(a, b, orderBy) { @@ -222,14 +223,15 @@ export default function RidersSummary() { # Rider - Total - {/* Pending + Deliveries + Pending Assigned Accepted Arrived Picked Active - Skipped */} + Skipped + Cancelled Delivered kms COD/PLA @@ -263,40 +265,45 @@ export default function RidersSummary() { {index + 1}
- - {row.firstname} - {row.status == 'Active' ? ( - - ) : ( - - )} + + + {row.firstname} {row.lastname} + + Id : {row.userid} - {row.totalorders} - {/* - {row.pending ? : row.pending} - - - {row.assigned ? : row.assigned} - - - {row.accepted ? : row.accepted} - - - {row.arrived ? : row.arrived} - - - {row.picked ? : row.picked} - - - {row.active ? : row.active} - - - {row.skipped ? : row.skipped} - */} + + {row.totalorders} + + + {row.pending ? : row.pending} + + + {row.assigned ? : row.assigned} + + + {row.accepted ? : row.accepted} + + + {row.arrived ? : row.arrived} + + + {row.picked ? : row.picked} + + + {row.active ? : row.active} + + + {row.skipped ? : row.skipped} + + + {row.cancelled ? : row.cancelled} + - {row.delivered} + + {row.delivered} + @@ -314,10 +321,10 @@ export default function RidersSummary() { }} /> */} - + # Location All - {/* Pending */} + Pending Completed - {/* Cancelled */} - Actual Kms + Cancelled + Kms COD / PLA Amount
@@ -446,14 +453,23 @@ export default function RidersSummary() { {index + 1} - - {row.locationname} + + {row.locationname} + Id: {row.locationid} - {row.totalorders} - {/* {row.deliveriespending} */} - {row.deliveriescompleted} - {/* {row.deliveriescancelled} */} + + {row.totalorders} + + + {row.deliveriespending} + + + {row.deliveriescompleted} + + + {row.deliveriescancelled} + {/* */} - + + + diff --git a/yarn.lock b/yarn.lock index 8faa5db..f4320fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3054,6 +3054,11 @@ adjust-sourcemap-loader@^4.0.0: loader-utils "^2.0.0" regex-parser "^2.2.11" +adler-32@~1.3.0: + version "1.3.1" + resolved "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz" + integrity sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A== + agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" @@ -3858,6 +3863,14 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +cfb@~1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz" + integrity sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA== + dependencies: + adler-32 "~1.3.0" + crc-32 "~1.2.0" + chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" @@ -4007,6 +4020,11 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" +codepage@~1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz" + integrity sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA== + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" @@ -4228,6 +4246,11 @@ country-flag-icons@^1.5.4: resolved "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.10.tgz" integrity sha512-x3elaK+ZY23W1YtFsNQknRdURzkV7g3Z93AoA7SHZJUEXbVjRsNh4h9Uf09+OjWF/4u8tXeAt37gezGRdwR/2g== +crc-32@~1.2.0, crc-32@~1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz" @@ -5709,6 +5732,11 @@ forwarded@0.2.0: resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +frac@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz" + integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA== + fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" @@ -7922,6 +7950,11 @@ p-try@^2.0.0: resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +papaparse@^5.5.3: + version "5.5.3" + resolved "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz" + integrity sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" @@ -10161,6 +10194,13 @@ sprintf-js@~1.0.2: resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +ssf@~0.11.2: + version "0.11.2" + resolved "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz" + integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g== + dependencies: + frac "~1.1.2" + stable@^0.1.8: version "0.1.8" resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" @@ -11144,11 +11184,21 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wmf@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz" + integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw== + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +word@~0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/word/-/word-0.3.0.tgz" + integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA== + workbox-background-sync@6.5.4: version "6.5.4" resolved "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz" @@ -11352,6 +11402,19 @@ ws@^8.4.2: resolved "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz" integrity sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg== +xlsx@^0.18.5: + version "0.18.5" + resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz" + integrity sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ== + dependencies: + adler-32 "~1.3.0" + cfb "~1.2.1" + codepage "~1.15.0" + crc-32 "~1.2.1" + ssf "~0.11.2" + wmf "~1.0.1" + word "~0.3.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz"