From 3e8e7ae7d7f423ec43fb9887f58a853fd2d9c941 Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 19 Jun 2025 16:29:22 +0700 Subject: [PATCH] update sanbox mode for product --- .DS_Store | Bin 6148 -> 6148 bytes auto-bid-admin/package-lock.json | 244 +++++++++++ auto-bid-admin/package.json | 2 + auto-bid-admin/src/apis/bid.ts | 243 +++++++---- .../src/components/bid/bid-modal.tsx | 399 ++++++++++++++---- auto-bid-admin/src/components/bid/index.ts | 2 + .../src/components/bid/record-modal.tsx | 168 ++++++++ .../components/bid/response-demo-modal.tsx | 193 +++++++++ .../src/components/dashboard/working-page.tsx | 110 +++-- auto-bid-admin/src/lib/table/ultils/index.ts | 2 - auto-bid-admin/src/pages/bids.tsx | 102 ++++- auto-bid-admin/src/pages/dashboard.tsx | 2 + auto-bid-admin/src/system/type/index.ts | 8 + auto-bid-admin/src/utils/index.ts | 39 +- .../src/modules/bids/bids.module.ts | 6 +- .../admin/admin-bid-metadata.controller.ts | 21 + .../admin/admin-bids.controller.ts | 15 + .../controllers/client/bids.controller.ts | 30 +- .../dto/bid/client-info-update-bid.dto.ts | 6 +- .../modules/bids/dto/bid/update-bid.dto.ts | 6 +- .../bids/entities/bid-metadata.entity.ts | 42 ++ .../src/modules/bids/entities/bid.entity.ts | 2 +- .../bids/pipes/image-compression-pipe.ts | 33 +- .../bids/services/bid-histories.service.ts | 2 +- .../bids/services/bid-metadatas.service.ts | 85 +++- .../src/modules/bids/services/bids.service.ts | 248 ++++++++++- .../modules/bids/services/tasks.servise.ts | 22 +- .../modules/bids/services/web-bids.service.ts | 4 +- .../src/modules/bids/utils/constant.ts | 1 + .../src/modules/bids/utils/events.ts | 1 + .../src/modules/mails/mails.module.ts | 5 +- .../modules/mails/services/mails.service.ts | 82 ++++ .../listeners/admin-notification.listener.ts | 18 +- .../modules/scraps/services/tasks.service.ts | 2 +- .../src/system/routes/exclude-route.ts | 12 + auto-bid-server/src/ultils/index.ts | 26 ++ auto-bid-tool/index.js | 18 +- .../allbids.com.au/allbids-product-bid.js | 118 ++++-- auto-bid-tool/models/api-bid.js | 14 +- .../models/grays.com/grays-api-bid.js | 7 +- .../grays.com/grays-product-bid copy.js | 322 -------------- .../models/grays.com/grays-product-bid.js | 87 +++- .../langtons.com.au/langtons-product-bid.js | 90 +++- .../lawsons.com.au/lawsons-product-bid.js | 85 +++- .../pickles.com.au/pickles-product-bid.js | 101 ++++- auto-bid-tool/models/product-bid.js | 108 ++++- auto-bid-tool/package-lock.json | 201 +++++++++ auto-bid-tool/package.json | 1 + auto-bid-tool/so_do_hoat_dong_tool.png | Bin 0 -> 1467957 bytes auto-bid-tool/system/apis/bid.js | 21 + auto-bid-tool/system/constants.js | 2 + auto-bid-tool/system/utils.js | 28 +- 52 files changed, 2732 insertions(+), 654 deletions(-) create mode 100644 auto-bid-admin/src/components/bid/record-modal.tsx create mode 100644 auto-bid-admin/src/components/bid/response-demo-modal.tsx create mode 100644 auto-bid-server/src/modules/bids/controllers/admin/admin-bid-metadata.controller.ts delete mode 100644 auto-bid-tool/models/grays.com/grays-product-bid copy.js create mode 100644 auto-bid-tool/so_do_hoat_dong_tool.png diff --git a/.DS_Store b/.DS_Store index 64dbd431df54334ec25995fc7a7ea8479507ac27..dc6c35119b98a97b6215c04ff2c58641ca45114a 100644 GIT binary patch delta 67 zcmZoMXffDum~}EQdzp}hrH+DyWvz}vwWWc9j)J9`+2pD0#+&QeSQxR(6>VnY_{R?b DaH$d~ delta 73 zcmZoMXffDun00b7OR0#3ftikiiJ?iYjzYDCDTr-kJoyic@#Z=<7Dgd6104l(u$-k4 WkZECHIN6)cc=B;}`_1eefB6CX;S@ap diff --git a/auto-bid-admin/package-lock.json b/auto-bid-admin/package-lock.json index a428dbb..73c884f 100644 --- a/auto-bid-admin/package-lock.json +++ b/auto-bid-admin/package-lock.json @@ -29,6 +29,8 @@ "react": "^19.0.0", "react-cropper": "^2.3.3", "react-dom": "^19.0.0", + "react-json-view": "^1.21.3", + "react-player": "^2.16.0", "react-router": "^6.29.0", "react-router-dom": "^6.29.0", "react-toastify": "^11.0.3", @@ -2106,6 +2108,12 @@ "punycode": "^2.1.0" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2133,6 +2141,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==", + "license": "MIT" + }, "node_modules/bcp-47-match": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", @@ -2393,6 +2407,15 @@ "integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==", "license": "MIT" }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2675,6 +2698,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3138,6 +3170,36 @@ "reusify": "^1.0.4" } }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "license": "BSD-3-Clause", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "license": "MIT", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -4077,12 +4139,30 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==", + "license": "MIT" + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==", + "license": "MIT" + }, + "node_modules/lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4413,6 +4493,12 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5099,6 +5185,26 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -5409,6 +5515,15 @@ "postcss": "^8.2.1" } }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5436,6 +5551,12 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5466,6 +5587,18 @@ "node": ">=0.10.0" } }, + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "license": "MIT", + "dependencies": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, "node_modules/react-cropper": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/react-cropper/-/react-cropper-2.3.3.tgz", @@ -5505,12 +5638,53 @@ "react": ">= 16.8 || 18.0.0" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "license": "MIT", + "dependencies": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" + } + }, + "node_modules/react-json-view/node_modules/flux": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", + "license": "BSD-3-Clause", + "dependencies": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + }, + "peerDependencies": { + "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, "node_modules/react-markdown": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.3.tgz", @@ -5547,6 +5721,22 @@ "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-player": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.16.0.tgz", + "integrity": "sha512-mAIPHfioD7yxO0GNYVFD1303QFtI3lyyQZLY229UEAp/a10cSW+hPcakg0Keq8uWJxT2OiT/4Gt+Lc9bD6bJmQ==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -6139,6 +6329,12 @@ "semver": "bin/semver.js" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/socket.io-client": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", @@ -6311,6 +6507,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -6386,6 +6588,32 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/ua-parser-js": { + "version": "1.0.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", + "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -7507,6 +7735,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", diff --git a/auto-bid-admin/package.json b/auto-bid-admin/package.json index 9a4930c..62a2b10 100644 --- a/auto-bid-admin/package.json +++ b/auto-bid-admin/package.json @@ -31,6 +31,8 @@ "react": "^19.0.0", "react-cropper": "^2.3.3", "react-dom": "^19.0.0", + "react-json-view": "^1.21.3", + "react-player": "^2.16.0", "react-router": "^6.29.0", "react-router-dom": "^6.29.0", "react-toastify": "^11.0.3", diff --git a/auto-bid-admin/src/apis/bid.ts b/auto-bid-admin/src/apis/bid.ts index 0813b94..39adeec 100644 --- a/auto-bid-admin/src/apis/bid.ts +++ b/auto-bid-admin/src/apis/bid.ts @@ -1,120 +1,187 @@ -import { generateNestParams, handleError, handleSuccess } from '.'; -import axios from '../lib/axios'; -import { IBid, IWebBid } from '../system/type'; -import { removeFalsyValues } from '../utils'; +import { generateNestParams, handleError, handleSuccess } from "."; +import axios from "../lib/axios"; +import { IBid, IMetadata, IWebBid } from "../system/type"; +import { removeFalsyValues } from "../utils"; export const getBids = async (params: Record) => { - return await axios({ - url: 'bids', - params: generateNestParams(params), - withCredentials: true, - method: 'GET', - }); + return await axios({ + url: "bids", + params: generateNestParams(params), + withCredentials: true, + method: "GET", + }); }; -export const createBid = async (bid: Omit) => { - const newData = removeFalsyValues(bid); +export const createBid = async ( + bid: Omit +) => { + const newData = removeFalsyValues(bid); - try { - const { data } = await axios({ - url: 'bids', - withCredentials: true, - method: 'POST', - data: newData, - }); + try { + const { data } = await axios({ + url: "bids", + withCredentials: true, + method: "POST", + data: newData, + }); - handleSuccess(data); + handleSuccess(data); - return data; - } catch (error) { - handleError(error); - } + return data; + } catch (error) { + handleError(error); + } }; export const updateBid = async (bid: Partial) => { - const { plus_price, max_price, quantity } = removeFalsyValues(bid, ['plus_price']); + const { plus_price, max_price, quantity, metadata } = removeFalsyValues(bid, [ + "plus_price", + ]); - try { - const { data } = await axios({ - url: 'bids/' + bid.id, - withCredentials: true, - method: 'PUT', - data: { plus_price, max_price, quantity }, - }); + try { + const { data } = await axios({ + url: "bids/" + bid.id, + withCredentials: true, + method: "PUT", + data: { plus_price, max_price, quantity, metadata }, + }); - handleSuccess(data); + handleSuccess(data); - return data; - } catch (error) { - handleError(error); - } + return data; + } catch (error) { + handleError(error); + } }; export const toggleBid = async (bid: Partial) => { - try { - const { data } = await axios({ - url: 'bids/toggle/' + bid.id, - withCredentials: true, - method: 'POST', - }); + try { + const { data } = await axios({ + url: "bids/toggle/" + bid.id, + withCredentials: true, + method: "POST", + }); - handleSuccess(data); + handleSuccess(data); - return data; - } catch (error) { - handleError(error); - } + return data; + } catch (error) { + handleError(error); + } }; export const deleteBid = async (bid: IBid) => { - try { - const { data } = await axios({ - url: 'bids/' + bid.id, - withCredentials: true, - method: 'DELETE', - }); + try { + const { data } = await axios({ + url: "bids/" + bid.id, + withCredentials: true, + method: "DELETE", + }); - handleSuccess(data); + handleSuccess(data); - return data; - } catch (error) { - handleError(error); - } + return data; + } catch (error) { + handleError(error); + } }; export const deletesBid = async (bids: IBid[]) => { - const ids = bids.reduce((prev, cur) => { - prev.push(cur.id); - return prev; - }, [] as number[]); - try { - const { data } = await axios({ - url: 'bids/deletes', - withCredentials: true, - method: 'POST', - data: { - ids, - }, - }); + const ids = bids.reduce((prev, cur) => { + prev.push(cur.id); + return prev; + }, [] as number[]); + try { + const { data } = await axios({ + url: "bids/deletes", + withCredentials: true, + method: "POST", + data: { + ids, + }, + }); - handleSuccess(data); + handleSuccess(data); - return data; - } catch (error) { - handleError(error); - } + return data; + } catch (error) { + handleError(error); + } }; -export const getImagesWorking = async (values: (IBid | IWebBid) & { type: string }) => { - try { - const { data } = await axios({ - url: `bids/images-working/${values.type.toLocaleLowerCase().replace('_', '-')}/${values.id}`, - withCredentials: true, - method: 'GET', - }); +export const getImagesWorking = async ( + values: (IBid | IWebBid) & { type: string } +) => { + try { + const { data } = await axios({ + url: `bids/images-working/${values.type + .toLocaleLowerCase() + .replace("_", "-")}/${values.id}`, + withCredentials: true, + method: "GET", + }); - return data; - } catch (error) { - console.log('%csrc/apis/bid.ts:118 error', 'color: #007acc;', error); - } + return data; + } catch (error) { + console.log("%csrc/apis/bid.ts:118 error", "color: #007acc;", error); + } +}; + +export const getRecords = async (values: IBid) => { + try { + const { data } = await axios({ + url: `bids/records/${values.id}`, + withCredentials: true, + method: "GET", + }); + + return data; + } catch (error) { + console.log("%csrc/apis/bid.ts:118 error", "color: #007acc;", error); + } +}; + +export const deleteRecord = async (name: string) => { + try { + const { data } = await axios({ + url: `bids/record/${name}`, + withCredentials: true, + method: "DELETE", + }); + + return data; + } catch (error) { + console.log("%csrc/apis/bid.ts:154 error", "color: #007acc;", error); + } +}; + +export const deleteMetadata = async (id: IMetadata["id"]) => { + try { + const { data } = await axios({ + url: `bid-metadatas/${id}`, + withCredentials: true, + method: "DELETE", + }); + + return data; + } catch (error) { + console.log("%csrc/apis/bid.ts:154 error", "color: #007acc;", error); + } +}; + +export const updateMetadata = async (id: IMetadata["id"], value: string) => { + try { + const { data } = await axios({ + url: `bid-metadatas/${id}`, + withCredentials: true, + method: "PUT", + data: { + value, + }, + }); + + return data; + } catch (error) { + console.log("%csrc/apis/bid.ts:154 error", "color: #007acc;", error); + } }; diff --git a/auto-bid-admin/src/components/bid/bid-modal.tsx b/auto-bid-admin/src/components/bid/bid-modal.tsx index 141dcdd..9423698 100644 --- a/auto-bid-admin/src/components/bid/bid-modal.tsx +++ b/auto-bid-admin/src/components/bid/bid-modal.tsx @@ -1,117 +1,330 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, LoadingOverlay, Modal, ModalProps, NumberInput, TextInput } from '@mantine/core'; -import { useForm, zodResolver } from '@mantine/form'; -import _ from 'lodash'; -import { useEffect, useRef, useState } from 'react'; -import { z } from 'zod'; -import { createBid, updateBid } from '../../apis/bid'; -import { useConfirmStore } from '../../lib/zustand/use-confirm'; -import { IBid } from '../../system/type'; +import { + Button, + LoadingOverlay, + Modal, + ModalProps, + NumberInput, + Select, + TextInput, +} from "@mantine/core"; +import { useForm, zodResolver } from "@mantine/form"; +import _ from "lodash"; +import { useEffect, useRef, useState } from "react"; +import { z } from "zod"; +import { createBid, updateBid } from "../../apis/bid"; +import { useConfirmStore } from "../../lib/zustand/use-confirm"; +import { IBid, IMetadata } from "../../system/type"; +import { formatTimeFromMinutes } from "../../utils"; export interface IBidModelProps extends ModalProps { - data: IBid | null; - onUpdated?: () => void; + data: IBid | null; + onUpdated?: () => void; } const schema = { - url: z.string({ message: 'Url is required' }).url('Invalid url format'), - max_price: z.number({ message: 'Max price is required' }).min(1, 'Max price must be at least 1'), - plus_price: z.number().min(0, 'Plus price must be at least 1').optional(), - quantity: z.number().min(1, 'Quantity must be at least 1').optional(), + url: z.string({ message: "Url is required" }).url("Invalid url format"), + max_price: z + .number({ message: "Max price is required" }) + .min(1, "Max price must be at least 1"), + plus_price: z.number().min(0, "Plus price must be at least 1").optional(), + quantity: z.number().min(1, "Quantity must be at least 1").optional(), + arrival_offset_seconds_live: z + .number({ message: "Arrival offset seconds is required" }) + .refine((val) => val >= 60, { + message: "Arrival offset seconds must be at least 60 seconds (1 minute)", + }) + .optional(), + early_tracking_seconds_live: z + .number({ message: "Early login seconds is required" }) + .refine((val) => val >= 600, { + message: "Early login seconds must be at least 600 seconds (10 minute)", + }) + .optional(), + arrival_offset_seconds_sandbox: z + .number({ message: "Arrival offset seconds is required" }) + .refine((val) => val >= 60, { + message: "Arrival offset seconds must be at least 60 seconds (1 minute)", + }) + .optional(), + early_tracking_seconds_sandbox: z + .number({ message: "Early login seconds is required" }) + .refine((val) => val >= 600, { + message: "Early login seconds must be at least 600 seconds (10 minute)", + }) + .optional(), }; -export default function BidModal({ data, onUpdated, ...props }: IBidModelProps) { - const form = useForm({ - validate: zodResolver(z.object(schema)), +export default function BidModal({ + data, + onUpdated, + ...props +}: IBidModelProps) { + const form = useForm({ + validate: zodResolver(z.object(schema)), + }); + + const prevData = useRef(data); + + const { setConfirm } = useConfirmStore(); + + const [loading, setLoading] = useState(false); + + const handleSubmit = async (values: typeof form.values) => { + if (data) { + setConfirm({ + title: "Update ?", + message: `This product will be update`, + handleOk: async () => { + setLoading(true); + + const metadata = valuesToMetadata( + values as IBid & Record + ); + + const result = await updateBid({ ...values, metadata }); + setLoading(false); + + if (!result) return; + + props.onClose(); + + if (onUpdated) { + onUpdated(); + } + }, + okButton: { + color: "blue", + value: "Update", + }, + }); + } else { + const { url, max_price, plus_price } = values; + + setLoading(true); + const result = await createBid({ url, max_price, plus_price } as IBid); + + setLoading(false); + + if (!result) return; + + props.onClose(); + + if (onUpdated) { + onUpdated(); + } + } + }; + + const mappingValues = (ignore: string[] = []) => { + if (!data) return {}; + let values: IBid & Record = data; + + const followKey = ["arrival_offset_seconds", "early_tracking_seconds"]; + + if ( + data.metadata.length && + data.metadata.some((item) => item.key_name === "mode_key") + ) { + data.metadata.reduce((prev, cur) => { + if (ignore.includes(cur.key_name)) { + prev[cur.key_name as string] = form.values[cur.key_name]; + + return prev; + } + + if (cur.key_name === "mode_key") { + prev[cur.key_name as string] = cur.value; + return prev; + } + + prev[cur.key_name as string] = cur.value; + + return prev; + }, values); + } else { + const metadata = Object.assign( + { mode_key: "live" }, + ...followKey.map((item) => ({ + [`${item}_live`]: data.web_bid[item as keyof typeof data.web_bid], + [`${item}_sandbox`]: data.web_bid[item as keyof typeof data.web_bid], + })) + ); + + values = { ...values, ...metadata }; + } + + return values; + }; + + const valuesToMetadata = (values: IBid & Record) => { + const keys = [ + "mode_key", + "arrival_offset_seconds_live", + "arrival_offset_seconds_sandbox", + "early_tracking_seconds_live", + "early_tracking_seconds_sandbox", + ]; + + if (values.metadata.length <= 0) { + return keys.map((item) => { + return { + key_name: item, + value: values[item], + } as IMetadata; + }); + } + + return values.metadata.map((item) => { + return { + ...item, + value: values[item.key_name], + }; }); + }; - const prevData = useRef(data); + useEffect(() => { + form.reset(); + if (!data) return; - const { setConfirm } = useConfirmStore(); + const values = mappingValues(); - const [loading, setLoading] = useState(false); + form.setValues(values); - const handleSubmit = async (values: typeof form.values) => { - if (data) { - setConfirm({ - title: 'Update ?', - message: `This product will be update`, - handleOk: async () => { - setLoading(true); - const result = await updateBid(values); - setLoading(false); + prevData.current = data; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data]); - if (!result) return; + useEffect(() => { + if (!props.opened) { + form.reset(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.opened]); - props.onClose(); + useEffect(() => { + const values = mappingValues(["mode_key"]); + form.setValues(values); - if (onUpdated) { - onUpdated(); - } - }, - okButton: { - color: 'blue', - value: 'Update', - }, - }); - } else { - const { url, max_price, plus_price } = values; + prevData.current = data; - setLoading(true); - const result = await createBid({ url, max_price, plus_price } as IBid); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [form.values["mode_key"]]); - setLoading(false); + return ( + Bid} + centered + > +
+ {!!data && ( +