diff --git a/apps/frontend-react/index.html b/apps/frontend-react/index.html
new file mode 100644
index 00000000..a730e0cb
--- /dev/null
+++ b/apps/frontend-react/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ order-site react
+
+
+
+
+
+
diff --git a/apps/frontend-react/package-lock.json b/apps/frontend-react/package-lock.json
new file mode 100644
index 00000000..512f8932
--- /dev/null
+++ b/apps/frontend-react/package-lock.json
@@ -0,0 +1,2687 @@
+{
+ "name": "order-site-frontend-react",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "order-site-frontend-react",
+ "version": "0.1.0",
+ "dependencies": {
+ "@ant-design/icons": "^6.1.0",
+ "@tanstack/react-query": "^5.90.12",
+ "antd": "^6.1.1",
+ "axios": "^1.13.2",
+ "qrcode": "^1.5.4",
+ "react": "^19.2.3",
+ "react-dom": "^19.2.3",
+ "react-router": "^7.10.1"
+ },
+ "devDependencies": {
+ "@types/node": "^25.6.0",
+ "@types/qrcode": "^1.5.6",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "typescript": "~6.0.2",
+ "vite": "^8.0.7"
+ }
+ },
+ "node_modules/@ant-design/colors": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz",
+ "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^3.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.1.2.tgz",
+ "integrity": "sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@emotion/hash": "^0.8.0",
+ "@emotion/unitless": "^0.7.5",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "stylis": "^4.3.4"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs-utils": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.2.tgz",
+ "integrity": "sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/cssinjs": "^2.1.2",
+ "@babel/runtime": "^7.23.2",
+ "@rc-component/util": "^1.4.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/@ant-design/fast-color": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz",
+ "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@ant-design/icons": {
+ "version": "6.3.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.3.2.tgz",
+ "integrity": "sha512-B6O5a5XJ4wjtNOfZejXYwHW5zvKV5gYkjGf11dHGLEbKn0ABDGndo41+gfIiXyTFhvESj4XTotuud33mUFid0g==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^8.0.1",
+ "@ant-design/icons-svg": "^4.5.0",
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/icons-svg": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.5.0.tgz",
+ "integrity": "sha512-1BTUFyKPTBZ53MuTP8s0k5SFEXL7o3VHEOwLgzaoWKwnBeqIcqUtVshc4SKzhI6uACfqhJqBwBUE9FsWR3uULA==",
+ "license": "MIT"
+ },
+ "node_modules/@ant-design/react-slick": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-2.0.0.tgz",
+ "integrity": "sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4",
+ "clsx": "^2.1.1",
+ "json2mq": "^0.2.0",
+ "throttle-debounce": "^5.0.0"
+ },
+ "peerDependencies": {
+ "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
+ "license": "MIT"
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.138.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz",
+ "integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@rc-component/async-validator": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-6.0.0.tgz",
+ "integrity": "sha512-D3AGQwdyE58gmvx6waVSXJ80JGO+IY5L2O8HDnSOex7JNlzB3GuN/4hyHNTdhy2qtOhkpbIjmeAN3tL993wKbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.4"
+ },
+ "engines": {
+ "node": ">=14.x"
+ }
+ },
+ "node_modules/@rc-component/cascader": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/cascader/-/cascader-1.17.0.tgz",
+ "integrity": "sha512-3cVNG0zrQF1PoXq262L3wGCU+/YLEC1mGSVHDl577dQmA0ZKkXFbY6nwyXo+beCcM7buo49t24jkr+QZdL7O8w==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/select": "~1.8.0",
+ "@rc-component/tree": "~1.3.2",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/checkbox": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/checkbox/-/checkbox-2.0.0.tgz",
+ "integrity": "sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/collapse": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/collapse/-/collapse-1.2.0.tgz",
+ "integrity": "sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/color-picker": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-3.1.1.tgz",
+ "integrity": "sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^3.0.1",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/context": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-2.0.2.tgz",
+ "integrity": "sha512-uiGpAlblCNlziHPwj4S4Iy/oemeuz/hR03mbiEjTCXwsqOIN3BOzsRMyDwpyO5Fm0vIEEJRUf9ZtbRLbhksuTA==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.11.0"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/dialog": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/dialog/-/dialog-1.10.0.tgz",
+ "integrity": "sha512-eDukNlz9vNszAGv7i3zKXdxEd3wgVmNxuJijYt8zvTh17QwTu8KK/bdURRd/lU4qaMzhO1HKKmMrwOnkaw0BvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/motion": "^1.3.3",
+ "@rc-component/portal": "^2.1.0",
+ "@rc-component/util": "^1.9.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/drawer": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/drawer/-/drawer-1.4.2.tgz",
+ "integrity": "sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/portal": "^2.1.3",
+ "@rc-component/util": "^1.9.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/dropdown": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/dropdown/-/dropdown-1.0.2.tgz",
+ "integrity": "sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.11.0",
+ "react-dom": ">=16.11.0"
+ }
+ },
+ "node_modules/@rc-component/form": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@rc-component/form/-/form-1.8.5.tgz",
+ "integrity": "sha512-d24EYtvUOBhxEtSd/EqIu9DaMuqrWF2IRIvAFCTM6NQ/GJIYNr8DvEpUSUlv2uPxEJ0ZPwYQ+wwlGIAaiHvdrw==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/async-validator": "^6.0.0",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/image": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/image/-/image-1.9.0.tgz",
+ "integrity": "sha512-khF7w7xkBH5B1bsBcI1FSUZdkyd1aqpl2eYyILCqCzzQH3XdfehGUaZTnptyaJJfs09/R5hv9jXWyazOMFIClQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/motion": "^1.0.0",
+ "@rc-component/portal": "^2.1.2",
+ "@rc-component/util": "^1.10.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/input": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/input/-/input-1.3.1.tgz",
+ "integrity": "sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/resize-observer": "^1.1.1",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@rc-component/input-number": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/input-number/-/input-number-1.6.2.tgz",
+ "integrity": "sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/mini-decimal": "^1.0.1",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mentions": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/mentions/-/mentions-1.10.0.tgz",
+ "integrity": "sha512-CI1njYUVY0NjHtLhNoVmXlJyy568Sfep9Wsak6vmGjtT6uazx98djGYlCXz2xkHhEm73g91Y3MTvzUyE5avI7w==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/input": "~1.3.0",
+ "@rc-component/menu": "~1.4.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/menu": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/menu/-/menu-1.4.1.tgz",
+ "integrity": "sha512-3GsVRoQ4cnF/AoIQ4P+Z1haBfgfBPQfLT1RJY3Nu4DzOnheTslfCiGSPj7bv/cLj5sW5pHqN25dDXGP3JELAlQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/overflow": "^1.0.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mini-decimal": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.4.tgz",
+ "integrity": "sha512-xiuXcaCwyOWpD8a8scdExFl+bntNphAW8XeenL1ig2en0AAZY0Pcp4pC0dI22qJ+NvxKn9RoNIoRdqYU3BLH4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@rc-component/motion": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@rc-component/motion/-/motion-1.3.3.tgz",
+ "integrity": "sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mutate-observer": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz",
+ "integrity": "sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/notification": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.0.7.tgz",
+ "integrity": "sha512-nqZzpf6BPdaj+3ILx7si79LLmqPKyUmQoXa+/9gg0SkH0v1DbD66oJgRMSBEVnd/zUT3D4gwxWIHUKebYf2ZXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/overflow": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/overflow/-/overflow-1.0.1.tgz",
+ "integrity": "sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@rc-component/resize-observer": "^1.0.1",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/pagination": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.4.0.tgz",
+ "integrity": "sha512-CW1g7P9V8u+e8JQdUsl2RWg+GCsoee0mtJjZUCCxn/vb3jzOwDKm6hAdwddHCVBfWJ58eGUBZz3IvnU8rRktjw==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/picker": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/picker/-/picker-1.11.0.tgz",
+ "integrity": "sha512-6qXGKtoJvO8sUd17m5cyNEbEJub0zflCHnaZTBBmj63DPRZYc0WEHN8rp6hFSl+yMCJS/dJY5G+1fQ8bLCuD7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/overflow": "^1.0.0",
+ "@rc-component/resize-observer": "^1.0.0",
+ "@rc-component/trigger": "^3.6.15",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12.x"
+ },
+ "peerDependencies": {
+ "date-fns": ">= 2.x",
+ "dayjs": ">= 1.x",
+ "luxon": ">= 3.x",
+ "moment": ">= 2.x",
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ },
+ "peerDependenciesMeta": {
+ "date-fns": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rc-component/portal": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-2.2.1.tgz",
+ "integrity": "sha512-ck+r1kW/JSv0wxPji3KN2ss9K6Z0qqwusw/mf/0JobXhZ8hC2ejZwCJObW/SvDi0uhA0VzmCnx0CaCci95tcmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.11.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/progress": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/progress/-/progress-1.0.2.tgz",
+ "integrity": "sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/qrcode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-2.0.0.tgz",
+ "integrity": "sha512-aAv3QhPP1xyafuTZOxub6a54pCeBnN3IwQkpETrBtthq4BL5IgxnCbuoBWPDpdLw1y1j6BgBUCAKV92+yX06Dw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/rate": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/rate/-/rate-1.0.1.tgz",
+ "integrity": "sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/resize-observer": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/resize-observer/-/resize-observer-1.1.2.tgz",
+ "integrity": "sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/segmented": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/segmented/-/segmented-1.3.0.tgz",
+ "integrity": "sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@rc-component/select": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.8.2.tgz",
+ "integrity": "sha512-HQ9zuYqjfZTlcEMWlU1GAPBajd2OHIMVHyjZSGVTCVARwkfCgvXZMTEn0cduy3L+ejAKkaZluOQvxovZoaJaQw==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/overflow": "^1.0.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.11.1",
+ "@rc-component/virtual-list": "^1.2.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/@rc-component/slider": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/slider/-/slider-1.1.1.tgz",
+ "integrity": "sha512-LSzgWGYDgeCDgR4r1XlU29gbYws6HpLnvJd/uMhLeW/vQgxldeR+Wb4uzHDCHiYEbr1bnEHWdjkPxjJRHxuiig==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/steps": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/steps/-/steps-1.2.2.tgz",
+ "integrity": "sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/switch": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@rc-component/switch/-/switch-1.0.3.tgz",
+ "integrity": "sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/table": {
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/@rc-component/table/-/table-1.10.3.tgz",
+ "integrity": "sha512-AiONZrgXVIewFC2Pk3GTqbov+sJhWDZZ96QbJZQGNSrX/vcMzkqoFJVcipGH4OY6EmFSKApwbOhQth6DVyB6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/context": "^2.0.1",
+ "@rc-component/resize-observer": "^1.0.0",
+ "@rc-component/util": "^1.11.1",
+ "@rc-component/virtual-list": "^1.0.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/tabs": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/tabs/-/tabs-1.11.0.tgz",
+ "integrity": "sha512-hA/drZYOVa/MMIb4M2fWf3yaTyTG4qVuIABmghvEhyfw2nBob5VTH69lMCDjSVKmgODjO6nWlCV+gVn3xBrj5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/dropdown": "~1.0.0",
+ "@rc-component/menu": "~1.4.0",
+ "@rc-component/motion": "^1.1.3",
+ "@rc-component/resize-observer": "^1.0.0",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/tooltip": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/tooltip/-/tooltip-1.4.0.tgz",
+ "integrity": "sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/trigger": "^3.7.1",
+ "@rc-component/util": "^1.3.0",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/tour": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-2.4.0.tgz",
+ "integrity": "sha512-aui4r4TqmTzwaBgcQxHYep8kM8PTjZFufjokObpy35KfFeZ0k9ArquWFZqegQlH24P14t+F0qO0mGTgzlav1yg==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/portal": "^2.2.0",
+ "@rc-component/trigger": "^3.0.0",
+ "@rc-component/util": "^1.7.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/tree": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/tree/-/tree-1.3.2.tgz",
+ "integrity": "sha512-bJFj46wEkpBPnWyTm18XmgAgNQ/4YvprxMOPPY2a6rmhGJYxLuNKEFiL5Qej4Qctu9wHJm8WW+v2SYskafE0kA==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/motion": "^1.0.0",
+ "@rc-component/util": "^1.11.1",
+ "@rc-component/virtual-list": "^1.2.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=10.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/@rc-component/tree-select": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/tree-select/-/tree-select-1.11.0.tgz",
+ "integrity": "sha512-EhS0X0wtUhBfK4S5TlpSY3MR9ndPMGgujtt1PJW3Ej+ToAlnS/6ohYURtCoXBYGqazUwHmgQGVUDsfpVwhWPkg==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/select": "~1.8.0",
+ "@rc-component/tree": "~1.3.2",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/@rc-component/trigger": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.9.1.tgz",
+ "integrity": "sha512-LNsYvz60mrLJ/kRvKcHE7boUvcQfVMCfRqZ71x3Fo9AOiZ1KKIEqkzMA8DNvz2V3Bcvir/vwQNn7JF1NPODQ7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/motion": "^1.1.4",
+ "@rc-component/portal": "^2.2.0",
+ "@rc-component/resize-observer": "^1.1.1",
+ "@rc-component/util": "^1.2.1",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/upload": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.1.tgz",
+ "integrity": "sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA==",
+ "license": "MIT",
+ "dependencies": {
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/util": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.11.1.tgz",
+ "integrity": "sha512-awVlI3ub2vqfqkYxOBc/uQ0efm3jw0wcrhtO/YWLyZfxiKXczKwNbVuhlnyxytDt7H9pbbVQiqr+O6MLATtRYg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-mobile": "^5.0.0",
+ "react-is": "^18.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rc-component/virtual-list": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/virtual-list/-/virtual-list-1.2.0.tgz",
+ "integrity": "sha512-iavRm1Jo4GDbASQwdGa7jFyk93RvSOo9xHyBT4QL1pgFJj/Fdf1G+3RErH7/7BmAMvx2AkF62mjGYxDbXsK9TQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.0",
+ "@rc-component/resize-observer": "^1.0.1",
+ "@rc-component/util": "^1.4.0",
+ "clsx": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz",
+ "integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz",
+ "integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz",
+ "integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz",
+ "integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz",
+ "integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz",
+ "integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz",
+ "integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz",
+ "integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz",
+ "integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz",
+ "integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz",
+ "integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz",
+ "integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz",
+ "integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.11.1",
+ "@emnapi/runtime": "1.11.1",
+ "@napi-rs/wasm-runtime": "^1.1.6"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz",
+ "integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz",
+ "integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tanstack/query-core": {
+ "version": "5.101.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.2.tgz",
+ "integrity": "sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.101.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.2.tgz",
+ "integrity": "sha512-seDkr6kzGzX1okaaTtZPtgA688CDPlXUz1C6xSg0ESqn04Vuc8tlrYms1s3de+znBqhPVxFRfpAfUf+6XvfPWg==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.101.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "25.9.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.4.tgz",
+ "integrity": "sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": ">=7.24.0 <7.24.7"
+ }
+ },
+ "node_modules/@types/qrcode": {
+ "version": "1.5.6",
+ "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz",
+ "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
+ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz",
+ "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "^1.0.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0",
+ "babel-plugin-react-compiler": "^1.0.0",
+ "vite": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rolldown/plugin-babel": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/antd": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/antd/-/antd-6.5.0.tgz",
+ "integrity": "sha512-9zbVc9UukfGuqCvIAov01nlpDQWfARNmZQyt21ZhqLX7ilXmi4cdkp12xA48WEmXRXwZvno8A03qQuGE9JG8fg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^8.0.1",
+ "@ant-design/cssinjs": "^2.1.2",
+ "@ant-design/cssinjs-utils": "^2.1.2",
+ "@ant-design/fast-color": "^3.0.1",
+ "@ant-design/icons": "^6.3.1",
+ "@ant-design/react-slick": "~2.0.0",
+ "@babel/runtime": "^7.29.2",
+ "@rc-component/cascader": "~1.17.0",
+ "@rc-component/checkbox": "~2.0.0",
+ "@rc-component/collapse": "~1.2.0",
+ "@rc-component/color-picker": "~3.1.1",
+ "@rc-component/dialog": "~1.10.0",
+ "@rc-component/drawer": "~1.4.2",
+ "@rc-component/dropdown": "~1.0.2",
+ "@rc-component/form": "~1.8.5",
+ "@rc-component/image": "~1.9.0",
+ "@rc-component/input": "~1.3.1",
+ "@rc-component/input-number": "~1.6.2",
+ "@rc-component/mentions": "~1.10.0",
+ "@rc-component/menu": "~1.4.1",
+ "@rc-component/motion": "^1.3.3",
+ "@rc-component/mutate-observer": "^2.0.1",
+ "@rc-component/notification": "~2.0.7",
+ "@rc-component/pagination": "~1.4.0",
+ "@rc-component/picker": "~1.11.0",
+ "@rc-component/progress": "~1.0.2",
+ "@rc-component/qrcode": "~2.0.0",
+ "@rc-component/rate": "~1.0.1",
+ "@rc-component/resize-observer": "^1.1.2",
+ "@rc-component/segmented": "~1.3.0",
+ "@rc-component/select": "~1.8.2",
+ "@rc-component/slider": "~1.1.1",
+ "@rc-component/steps": "~1.2.2",
+ "@rc-component/switch": "~1.0.3",
+ "@rc-component/table": "~1.10.2",
+ "@rc-component/tabs": "~1.11.0",
+ "@rc-component/tooltip": "~1.4.0",
+ "@rc-component/tour": "~2.4.0",
+ "@rc-component/tree": "~1.3.2",
+ "@rc-component/tree-select": "~1.11.0",
+ "@rc-component/trigger": "^3.9.1",
+ "@rc-component/upload": "~1.1.1",
+ "@rc-component/util": "^1.11.1",
+ "clsx": "^2.1.1",
+ "dayjs": "^1.11.11",
+ "scroll-into-view-if-needed": "^3.1.0",
+ "throttle-debounce": "^5.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ant-design"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz",
+ "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/compute-scroll-into-view": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
+ "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.21",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
+ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "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",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dijkstrajs": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
+ "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
+ "license": "MIT"
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-mobile": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz",
+ "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==",
+ "license": "MIT"
+ },
+ "node_modules/json2mq": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+ "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "string-convert": "^0.2.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.15",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
+ "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pngjs": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
+ "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.16",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
+ "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/qrcode": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
+ "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
+ "license": "MIT",
+ "dependencies": {
+ "dijkstrajs": "^1.0.1",
+ "pngjs": "^5.0.0",
+ "yargs": "^15.3.1"
+ },
+ "bin": {
+ "qrcode": "bin/qrcode"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.7"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/react-router": {
+ "version": "7.18.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz",
+ "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "license": "ISC"
+ },
+ "node_modules/rolldown": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz",
+ "integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.138.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.1.4",
+ "@rolldown/binding-darwin-arm64": "1.1.4",
+ "@rolldown/binding-darwin-x64": "1.1.4",
+ "@rolldown/binding-freebsd-x64": "1.1.4",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.1.4",
+ "@rolldown/binding-linux-arm64-gnu": "1.1.4",
+ "@rolldown/binding-linux-arm64-musl": "1.1.4",
+ "@rolldown/binding-linux-ppc64-gnu": "1.1.4",
+ "@rolldown/binding-linux-s390x-gnu": "1.1.4",
+ "@rolldown/binding-linux-x64-gnu": "1.1.4",
+ "@rolldown/binding-linux-x64-musl": "1.1.4",
+ "@rolldown/binding-openharmony-arm64": "1.1.4",
+ "@rolldown/binding-wasm32-wasi": "1.1.4",
+ "@rolldown/binding-win32-arm64-msvc": "1.1.4",
+ "@rolldown/binding-win32-x64-msvc": "1.1.4"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/scroll-into-view-if-needed": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+ "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "license": "ISC"
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string-convert": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+ "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",
+ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",
+ "license": "MIT"
+ },
+ "node_modules/throttle-debounce": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+ "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.22"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.24.6",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
+ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "8.1.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz",
+ "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lightningcss": "^1.32.0",
+ "picomatch": "^4.0.4",
+ "postcss": "^8.5.16",
+ "rolldown": "~1.1.3",
+ "tinyglobby": "^0.2.17"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "@vitejs/devtools": "^0.3.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which-module": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
+ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
+ "license": "ISC"
+ },
+ "node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ }
+ }
+}
diff --git a/apps/frontend-react/package.json b/apps/frontend-react/package.json
new file mode 100644
index 00000000..6d5474d6
--- /dev/null
+++ b/apps/frontend-react/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "order-site-frontend-react",
+ "private": true,
+ "version": "0.1.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "typecheck": "tsc -b --noEmit",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@ant-design/icons": "^6.1.0",
+ "@tanstack/react-query": "^5.90.12",
+ "antd": "^6.1.1",
+ "axios": "^1.13.2",
+ "qrcode": "^1.5.4",
+ "react": "^19.2.3",
+ "react-dom": "^19.2.3",
+ "react-router": "^7.10.1"
+ },
+ "devDependencies": {
+ "@types/node": "^25.6.0",
+ "@types/qrcode": "^1.5.6",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^6.0.1",
+ "typescript": "~6.0.2",
+ "vite": "^8.0.7"
+ }
+}
diff --git a/apps/frontend-react/src/App.tsx b/apps/frontend-react/src/App.tsx
new file mode 100644
index 00000000..61111624
--- /dev/null
+++ b/apps/frontend-react/src/App.tsx
@@ -0,0 +1,79 @@
+import { Suspense, lazy } from 'react'
+import { Spin } from 'antd'
+import { HashRouter, Navigate, Outlet, Route, Routes, useLocation } from 'react-router'
+
+import { getAdminRole, hasAdminSession } from '@/utils/admin-auth'
+import AdminLayout from '@/layouts/AdminLayout'
+
+const AdminDashboardPage = lazy(() => import('@/pages/admin/AdminDashboardPage'))
+const AdminLoginPage = lazy(() => import('@/pages/admin/AdminLoginPage'))
+const AdminOrderDetailPage = lazy(() => import('@/pages/admin/AdminOrderDetailPage'))
+const AdminOrdersPage = lazy(() => import('@/pages/admin/AdminOrdersPage'))
+const AdminTaskDetailPage = lazy(() => import('@/pages/admin/AdminTaskDetailPage'))
+const AdminTasksPage = lazy(() => import('@/pages/admin/AdminTasksPage'))
+const NotFoundPage = lazy(() => import('@/pages/NotFoundPage'))
+
+function RequireAdmin() {
+ const location = useLocation()
+
+ if (!hasAdminSession()) {
+ return
+ }
+
+ return
+}
+
+function RequireRole({ roles }: { roles: Array<'admin' | 'operator' | 'support'> }) {
+ if (!roles.includes(getAdminRole())) {
+ return
+ }
+
+ return
+}
+
+function LoginRoute() {
+ if (hasAdminSession()) {
+ return
+ }
+
+ return
+}
+
+export default function App() {
+ return (
+
+ }>
+
+ } />
+ } />
+ }>
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ }>
+ } />
+ } />
+ } />
+ } />
+
+
+
+ } />
+ } />
+
+
+
+ )
+}
+
+function RouteLoading() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/frontend-react/src/components/admin/JsonPreview.tsx b/apps/frontend-react/src/components/admin/JsonPreview.tsx
new file mode 100644
index 00000000..f9bebf23
--- /dev/null
+++ b/apps/frontend-react/src/components/admin/JsonPreview.tsx
@@ -0,0 +1,7 @@
+type JsonPreviewProps = {
+ value: unknown
+}
+
+export default function JsonPreview({ value }: JsonPreviewProps) {
+ return {JSON.stringify(value ?? {}, null, 2)}
+}
diff --git a/apps/frontend-react/src/components/admin/PageHeader.tsx b/apps/frontend-react/src/components/admin/PageHeader.tsx
new file mode 100644
index 00000000..051850a8
--- /dev/null
+++ b/apps/frontend-react/src/components/admin/PageHeader.tsx
@@ -0,0 +1,22 @@
+import { Space, Typography } from 'antd'
+import type { ReactNode } from 'react'
+
+type PageHeaderProps = {
+ title: string
+ description?: string
+ extra?: ReactNode
+}
+
+export default function PageHeader({ title, description, extra }: PageHeaderProps) {
+ return (
+
+
+
+ {title}
+
+ {description ?
{description}
: null}
+
+ {extra ? {extra} : null}
+
+ )
+}
diff --git a/apps/frontend-react/src/components/admin/StatusTag.tsx b/apps/frontend-react/src/components/admin/StatusTag.tsx
new file mode 100644
index 00000000..5569fd9d
--- /dev/null
+++ b/apps/frontend-react/src/components/admin/StatusTag.tsx
@@ -0,0 +1,75 @@
+import { Tag } from 'antd'
+
+type StatusTagProps = {
+ value?: string | null
+ kind?: 'task' | 'pay' | 'delivery' | 'resource' | 'customer' | 'role' | 'plain'
+}
+
+const STATUS_LABELS: Record = {
+ active: '生效中',
+ claimed: '已创建会话',
+ closed: '已关闭',
+ completed: '已完成',
+ consumed: '已核销',
+ dispatched_pending_return: '待退号',
+ failed: '失败',
+ link_generated: '已生成链接',
+ manual_review: '人工处理',
+ paid: '已支付',
+ pending: '待处理',
+ pending_binding_prepare: '待准备资源',
+ pending_claim: '待领取',
+ pending_payment: '待支付',
+ redeemed: '已兑换',
+ redeeming: '兑换中',
+ refunded: '已退款',
+ retry_pending: '待重试',
+ role_confirmed: '已确认角色',
+ unpaid: '未支付',
+ waiting_binding: '待绑定',
+}
+
+const STATUS_COLORS: Record = {
+ active: 'green',
+ claimed: 'blue',
+ closed: 'default',
+ completed: 'green',
+ consumed: 'green',
+ failed: 'red',
+ link_generated: 'cyan',
+ manual_review: 'orange',
+ paid: 'green',
+ pending: 'gold',
+ pending_binding_prepare: 'gold',
+ pending_claim: 'gold',
+ pending_payment: 'default',
+ redeemed: 'green',
+ redeeming: 'blue',
+ refunded: 'purple',
+ retry_pending: 'volcano',
+ role_confirmed: 'blue',
+ unpaid: 'default',
+ waiting_binding: 'gold',
+}
+
+export default function StatusTag({ value, kind = 'plain' }: StatusTagProps) {
+ const normalized = String(value || '').trim()
+
+ if (!normalized) {
+ return -
+ }
+
+ const key = normalized.toLowerCase()
+ const label = STATUS_LABELS[key] || normalized
+ const color = STATUS_COLORS[key] || resolveFallbackColor(kind)
+
+ return {label === normalized ? normalized : `${label} (${normalized})`}
+}
+
+function resolveFallbackColor(kind: StatusTagProps['kind']) {
+ if (kind === 'pay' || kind === 'delivery') return 'blue'
+ if (kind === 'task') return 'geekblue'
+ if (kind === 'resource' || kind === 'customer') return 'cyan'
+ if (kind === 'role') return 'purple'
+ return 'default'
+}
diff --git a/apps/frontend-react/src/domain/task-status.ts b/apps/frontend-react/src/domain/task-status.ts
new file mode 100644
index 00000000..796e27da
--- /dev/null
+++ b/apps/frontend-react/src/domain/task-status.ts
@@ -0,0 +1,59 @@
+export const TASK_STATUS = {
+ PENDING_PAYMENT: "pending_payment",
+ PAID: "paid",
+ LINK_GENERATED: "link_generated",
+ CLAIMED: "claimed",
+ PENDING_BINDING_PREPARE: "pending_binding_prepare",
+ WAITING_BINDING: "waiting_binding",
+ ROLE_CONFIRMED: "role_confirmed",
+ REDEEMING: "redeeming",
+ DISPATCHED_PENDING_RETURN: "dispatched_pending_return",
+ COMPLETED: "completed",
+ REDEEMED: "redeemed",
+ RETRY_PENDING: "retry_pending",
+ MANUAL_REVIEW: "manual_review",
+ FAILED: "failed",
+ EXPIRED: "expired",
+ CLOSED: "closed",
+} as const;
+
+export type KnownTaskStatus = (typeof TASK_STATUS)[keyof typeof TASK_STATUS];
+export type TaskStatus = KnownTaskStatus | (string & {});
+
+const CLAIM_INACTIVE_STATUSES = new Set([
+ TASK_STATUS.COMPLETED,
+ TASK_STATUS.REDEEMED,
+ TASK_STATUS.MANUAL_REVIEW,
+ TASK_STATUS.FAILED,
+ TASK_STATUS.EXPIRED,
+ TASK_STATUS.CLOSED,
+]);
+
+const KUAISHOU_CLOUD_RESULT_STATUSES = new Set([
+ TASK_STATUS.DISPATCHED_PENDING_RETURN,
+ TASK_STATUS.COMPLETED,
+ TASK_STATUS.REDEEMED,
+ TASK_STATUS.MANUAL_REVIEW,
+ TASK_STATUS.FAILED,
+]);
+
+export function normalizeTaskStatus(value: unknown): TaskStatus {
+ return String(value || "").trim() as TaskStatus;
+}
+
+export function isClaimInactiveTaskStatus(status: unknown): boolean {
+ return CLAIM_INACTIVE_STATUSES.has(normalizeTaskStatus(status));
+}
+
+export function isKuaishouCloudRoleConfirmedStatus(status: unknown): boolean {
+ return normalizeTaskStatus(status) === TASK_STATUS.ROLE_CONFIRMED;
+}
+
+export function isKuaishouCloudCompletedStatus(status: unknown): boolean {
+ const normalized = normalizeTaskStatus(status);
+ return normalized === TASK_STATUS.COMPLETED || normalized === TASK_STATUS.REDEEMED;
+}
+
+export function hasKuaishouCloudRedeemResultStatus(status: unknown): boolean {
+ return KUAISHOU_CLOUD_RESULT_STATUSES.has(normalizeTaskStatus(status));
+}
diff --git a/apps/frontend-react/src/layouts/AdminLayout.tsx b/apps/frontend-react/src/layouts/AdminLayout.tsx
new file mode 100644
index 00000000..5e09b2be
--- /dev/null
+++ b/apps/frontend-react/src/layouts/AdminLayout.tsx
@@ -0,0 +1,149 @@
+import {
+ AuditOutlined,
+ DashboardOutlined,
+ FileSearchOutlined,
+ MenuFoldOutlined,
+ MenuUnfoldOutlined,
+ OrderedListOutlined,
+ SettingOutlined,
+ ShopOutlined,
+ TeamOutlined,
+ UnorderedListOutlined,
+ UserOutlined,
+} from '@ant-design/icons'
+import { App, Button, Dropdown, Layout, Menu, Space, Typography } from 'antd'
+import type { MenuProps } from 'antd'
+import { useMemo, useState } from 'react'
+import { Outlet, useLocation, useNavigate } from 'react-router'
+
+import { logoutAdmin } from '@/services/admin'
+import {
+ clearAdminSession,
+ getAdminRole,
+ getAdminTokenExpiresAt,
+ getAdminUsername,
+} from '@/utils/admin-auth'
+import { formatAdminDateTime } from '@/utils/admin-time'
+
+const SIDEBAR_COLLAPSED_KEY = 'react-admin-sidebar-collapsed'
+
+export default function AdminLayout() {
+ const navigate = useNavigate()
+ const location = useLocation()
+ const { message } = App.useApp()
+ const [collapsed, setCollapsed] = useState(
+ () => window.localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === '1',
+ )
+ const role = getAdminRole()
+ const username = getAdminUsername() || 'admin'
+ const expiresAt = getAdminTokenExpiresAt()
+
+ const menuItems = useMemo(() => {
+ const items: MenuProps['items'] = [
+ { key: '/admin/dashboard', icon: , label: '概览' },
+ { key: '/admin/orders', icon: , label: '订单' },
+ { key: '/admin/tasks', icon: , label: '任务' },
+ ]
+
+ if (role === 'admin') {
+ items.splice(1, 0, { key: '/admin/users', icon: , label: '用户' })
+ items.push(
+ { key: '/admin/cloudtentacles-records', icon: , label: '发货记录' },
+ {
+ key: '/admin/platform-shops',
+ icon: ,
+ label: '平台配置',
+ },
+ { key: '/admin/platform-fulfillment', icon: , label: '履约配置' },
+ { key: '/admin/audit-logs', icon: , label: '审计' },
+ )
+ }
+
+ return items
+ }, [role])
+
+ async function submitLogout() {
+ try {
+ await logoutAdmin()
+ } catch {
+ // 后端退出是无状态的,本地清理优先。
+ } finally {
+ clearAdminSession()
+ message.success('已退出后台')
+ navigate('/admin/login', { replace: true })
+ }
+ }
+
+ function toggleCollapsed() {
+ const next = !collapsed
+ setCollapsed(next)
+ window.localStorage.setItem(SIDEBAR_COLLAPSED_KEY, next ? '1' : '0')
+ }
+
+ const selectedKey = resolveSelectedKey(location.pathname)
+
+ return (
+
+
+
+ {collapsed ? 'OS' : '运营后台'}
+ {!collapsed ? 订单与交付管理 : null}
+
+
+
+
+ : }
+ onClick={toggleCollapsed}
+ />
+
+
+ {username}
+
+ {roleLabel(role)}
+ {expiresAt ? ` · ${formatAdminDateTime(expiresAt)} 到期` : ''}
+
+
+
+ }>账号
+
+
+
+
+
+
+
+
+ )
+}
+
+function resolveSelectedKey(pathname: string) {
+ if (pathname.startsWith('/admin/orders')) return '/admin/orders'
+ if (pathname.startsWith('/admin/tasks')) return '/admin/tasks'
+ return pathname
+}
+
+function roleLabel(role: string) {
+ if (role === 'admin') return '管理员'
+ if (role === 'operator') return '普通运营'
+ return '客服'
+}
diff --git a/apps/frontend-react/src/lib/feedback.ts b/apps/frontend-react/src/lib/feedback.ts
new file mode 100644
index 00000000..ecd22e13
--- /dev/null
+++ b/apps/frontend-react/src/lib/feedback.ts
@@ -0,0 +1,86 @@
+import { Input, Modal, message as antdMessage } from 'antd'
+import type { ModalFuncProps } from 'antd'
+import { createElement } from 'react'
+
+type MessageOptions = {
+ duration?: number
+}
+
+type PromptResult = {
+ value: string
+}
+
+export function showSuccess(message: string, options: MessageOptions = {}) {
+ return antdMessage.success({
+ content: message,
+ duration: options.duration,
+ })
+}
+
+export function showError(message: string, options: MessageOptions = {}) {
+ return antdMessage.error({
+ content: message,
+ duration: options.duration,
+ })
+}
+
+export function showConfirm(
+ message: string,
+ title = '确认操作',
+ options: ModalFuncProps = {},
+) {
+ return new Promise((resolve, reject) => {
+ Modal.confirm({
+ title,
+ content: message,
+ okText: '继续执行',
+ cancelText: '取消',
+ centered: true,
+ ...options,
+ onOk: () => {
+ options.onOk?.()
+ resolve()
+ },
+ onCancel: () => {
+ options.onCancel?.()
+ reject('cancel')
+ },
+ })
+ })
+}
+
+export function showPrompt(message: string, title: string, options: ModalFuncProps = {}) {
+ let value = ''
+
+ return new Promise((resolve, reject) => {
+ Modal.confirm({
+ title,
+ content: createElement('div', { className: 'feedback-prompt' }, [
+ createElement('p', { key: 'message' }, message),
+ createElement(Input, {
+ key: 'input',
+ autoFocus: true,
+ onChange: (event) => {
+ value = event.target.value
+ },
+ }),
+ ]),
+ okText: '提交',
+ cancelText: '取消',
+ centered: true,
+ ...options,
+ onOk: () => {
+ options.onOk?.()
+ resolve({ value })
+ },
+ onCancel: () => {
+ options.onCancel?.()
+ reject('cancel')
+ },
+ })
+ })
+}
+
+export function isFeedbackDismissed(error: unknown) {
+ return error === 'cancel' || error === 'close'
+}
diff --git a/apps/frontend-react/src/lib/http.ts b/apps/frontend-react/src/lib/http.ts
new file mode 100644
index 00000000..dfb08252
--- /dev/null
+++ b/apps/frontend-react/src/lib/http.ts
@@ -0,0 +1,136 @@
+import axios from 'axios'
+import { clearAdminSession, getAdminToken } from '@/utils/admin-auth'
+
+export interface ApiEnvelope {
+ code: number
+ msg: string
+ data: T
+ errorCode?: string
+}
+
+const http = axios.create({
+ baseURL: '/',
+ timeout: 50_000,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+})
+
+http.interceptors.response.use(
+ (response) => {
+ const data = response.data as ApiEnvelope
+
+ // Business-level error: HTTP 200 but code !== 0
+ if (typeof data?.code === 'number' && data.code !== 0) {
+ const error = new Error(data.msg || '操作失败') as Error & {
+ errorCode?: string
+ code?: number
+ }
+ error.errorCode = data.errorCode
+ error.code = data.code
+ return Promise.reject(error)
+ }
+
+ return response.data
+ },
+ (error) => {
+ const responseMessage =
+ typeof error?.response?.data?.msg === 'string' ? error.response.data.msg.trim() : ''
+ const fallbackMessage = resolveFallbackHttpMessage(error)
+ const normalizedError = new Error(responseMessage || fallbackMessage) as Error & {
+ errorCode?: string
+ status?: number
+ }
+
+ if (error?.response?.data?.errorCode) {
+ normalizedError.errorCode = String(error.response.data.errorCode)
+ }
+
+ if (typeof error?.response?.status === 'number') {
+ normalizedError.status = error.response.status
+ }
+
+ if (error?.config?.url && String(error.config.url).startsWith('/api/v1/admin')) {
+ const status = Number(error?.response?.status || 0)
+ const errorCode = String(error?.response?.data?.errorCode || '').trim()
+
+ if (status === 401 && shouldClearAdminSession(errorCode)) {
+ clearAdminSession()
+
+ if (
+ window.location.hash.startsWith('#/admin') &&
+ !window.location.hash.startsWith('#/admin/login')
+ ) {
+ window.location.hash = '#/admin/login'
+ }
+ }
+ }
+
+ return Promise.reject(normalizedError)
+ },
+)
+
+http.interceptors.request.use((config) => {
+ if (String(config.url || '').startsWith('/api/v1/admin')) {
+ const token = getAdminToken()
+
+ if (token) {
+ config.headers.Authorization = `Bearer ${token}`
+ }
+ }
+
+ return config
+})
+
+function resolveFallbackHttpMessage(error: unknown) {
+ const message = String((error as { message?: string })?.message ?? '')
+
+ if (message.includes('timeout')) {
+ return '网络超时'
+ }
+
+ if (message === 'Network Error') {
+ return '网络连接错误'
+ }
+
+ if (typeof (error as { response?: { statusText?: string } })?.response?.statusText === 'string') {
+ return String((error as { response: { statusText: string } }).response.statusText).trim()
+ }
+
+ return '接口请求失败'
+}
+
+function shouldClearAdminSession(errorCode: string) {
+ return [
+ 'admin_auth_required',
+ 'admin_auth_invalid',
+ 'admin_auth_expired',
+ 'admin_auth_user_invalid',
+ 'admin_auth_stale',
+ ].includes(errorCode)
+}
+
+function request(config: Parameters>>[0]) {
+ return http.request, ApiEnvelope>(config)
+}
+
+export function apiGet(url: string, params?: Record) {
+ return request({ method: 'GET', url, params })
+}
+
+export function apiGetBlob(url: string, params?: Record) {
+ return http.request({
+ method: 'GET',
+ url,
+ params,
+ responseType: 'blob',
+ })
+}
+
+export function apiPost(url: string, data?: unknown) {
+ return request({ method: 'POST', url, data: data as Record })
+}
+
+export function apiDelete(url: string) {
+ return request({ method: 'DELETE', url })
+}
diff --git a/apps/frontend-react/src/main.tsx b/apps/frontend-react/src/main.tsx
new file mode 100644
index 00000000..eaa672db
--- /dev/null
+++ b/apps/frontend-react/src/main.tsx
@@ -0,0 +1,50 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { App as AntdApp, ConfigProvider } from 'antd'
+import zhCN from 'antd/locale/zh_CN'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import 'antd/dist/reset.css'
+
+import App from './App'
+import './styles/main.css'
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ refetchOnWindowFocus: false,
+ retry: 1,
+ },
+ },
+})
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+
+
+
+ ,
+)
diff --git a/apps/frontend-react/src/pages/NotFoundPage.tsx b/apps/frontend-react/src/pages/NotFoundPage.tsx
new file mode 100644
index 00000000..d97996d9
--- /dev/null
+++ b/apps/frontend-react/src/pages/NotFoundPage.tsx
@@ -0,0 +1,20 @@
+import { Button, Empty } from 'antd'
+import { useNavigate } from 'react-router'
+
+type NotFoundPageProps = {
+ title?: string
+}
+
+export default function NotFoundPage({ title = '页面不存在' }: NotFoundPageProps) {
+ const navigate = useNavigate()
+
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/frontend-react/src/pages/admin/AdminDashboardPage.tsx b/apps/frontend-react/src/pages/admin/AdminDashboardPage.tsx
new file mode 100644
index 00000000..e64e58eb
--- /dev/null
+++ b/apps/frontend-react/src/pages/admin/AdminDashboardPage.tsx
@@ -0,0 +1,51 @@
+import { Alert, Card, Col, Row, Skeleton, Statistic } from 'antd'
+import { useQuery } from '@tanstack/react-query'
+
+import PageHeader from '@/components/admin/PageHeader'
+import { fetchAdminDashboardSummary } from '@/services/admin'
+
+const cards = [
+ { key: 'todayOrders', label: '今日订单' },
+ { key: 'paidPendingClaim', label: '待领取' },
+ { key: 'claimingTasks', label: '领取中' },
+ { key: 'redeemedToday', label: '今日成功' },
+ { key: 'abnormalTasks', label: '异常任务' },
+] as const
+
+export default function AdminDashboardPage() {
+ const query = useQuery({
+ queryKey: ['admin-dashboard-summary'],
+ queryFn: () => fetchAdminDashboardSummary(),
+ })
+ const summary = query.data?.data
+
+ return (
+
+
+
+ {query.error ? (
+
+ ) : null}
+
+ {query.isLoading ? (
+
+
+
+ ) : (
+
+ {cards.map((card) => (
+
+
+
+
+
+ ))}
+
+ )}
+
+ )
+}
diff --git a/apps/frontend-react/src/pages/admin/AdminLoginPage.tsx b/apps/frontend-react/src/pages/admin/AdminLoginPage.tsx
new file mode 100644
index 00000000..4b2bbfab
--- /dev/null
+++ b/apps/frontend-react/src/pages/admin/AdminLoginPage.tsx
@@ -0,0 +1,74 @@
+import { LockOutlined, UserOutlined } from '@ant-design/icons'
+import { App, Button, Card, Form, Input, Typography } from 'antd'
+import { useState } from 'react'
+import { useNavigate } from 'react-router'
+
+import { loginAdmin } from '@/services/admin'
+import { setAdminSession } from '@/utils/admin-auth'
+
+type LoginForm = {
+ username: string
+ password: string
+}
+
+export default function AdminLoginPage() {
+ const navigate = useNavigate()
+ const { message } = App.useApp()
+ const [loading, setLoading] = useState(false)
+
+ async function submitLogin(values: LoginForm) {
+ setLoading(true)
+
+ try {
+ const response = await loginAdmin({
+ username: values.username.trim(),
+ password: values.password.trim(),
+ })
+ setAdminSession(response.data.token, response.data.expiresAt, response.data.user)
+ message.success('登录成功')
+ navigate('/admin/dashboard', { replace: true })
+ } catch (error) {
+ message.error(error instanceof Error ? error.message : '后台登录失败')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+
+ Order Site Admin
+ 运营后台登录
+
+ 使用账号密码进入后台,并按角色开放不同操作权限。
+
+
+
+
+ } placeholder="输入账号" autoComplete="username" />
+
+
+ }
+ placeholder="输入密码"
+ autoComplete="current-password"
+ />
+
+
+
+
+
+ )
+}
diff --git a/apps/frontend-react/src/pages/admin/AdminOrderDetailPage.tsx b/apps/frontend-react/src/pages/admin/AdminOrderDetailPage.tsx
new file mode 100644
index 00000000..e783d40e
--- /dev/null
+++ b/apps/frontend-react/src/pages/admin/AdminOrderDetailPage.tsx
@@ -0,0 +1,161 @@
+import { ArrowLeftOutlined } from '@ant-design/icons'
+import { Alert, Button, Card, Descriptions, Skeleton, Space, Table, Typography } from 'antd'
+import type { TableColumnsType } from 'antd'
+import { useQuery } from '@tanstack/react-query'
+import { useNavigate, useParams } from 'react-router'
+
+import JsonPreview from '@/components/admin/JsonPreview'
+import PageHeader from '@/components/admin/PageHeader'
+import StatusTag from '@/components/admin/StatusTag'
+import { fetchAdminOrderDetail } from '@/services/admin'
+import type { AdminOrderDetail, AdminTaskListItem } from '@/types/admin'
+import { formatAdminDateTime } from '@/utils/admin-time'
+
+export default function AdminOrderDetailPage() {
+ const navigate = useNavigate()
+ const { orderId = '' } = useParams()
+ const query = useQuery({
+ queryKey: ['admin-order-detail', orderId],
+ queryFn: () => fetchAdminOrderDetail(orderId),
+ enabled: Boolean(orderId),
+ })
+ const detail = query.data?.data
+
+ if (query.isLoading) {
+ return (
+
+ )
+ }
+
+ if (query.error || !detail) {
+ return (
+
+ )
+ }
+
+ const taskColumns: TableColumnsType = [
+ {
+ title: '任务号',
+ dataIndex: 'taskNo',
+ minWidth: 180,
+ render: (_, row) => (
+ navigate(`/admin/tasks/${row.taskId}`)}>
+ {row.taskNo}
+
+ ),
+ },
+ {
+ title: '状态',
+ minWidth: 220,
+ render: (_, row) => (
+
+
+
+
+
+ ),
+ },
+ {
+ title: '角色',
+ minWidth: 160,
+ render: (_, row) => (
+
+ {row.roleName || '-'}
+ {row.roleId || '-'}
+
+ ),
+ },
+ {
+ title: '错误',
+ dataIndex: 'lastError',
+ minWidth: 220,
+ render: (value) => {value || '-'},
+ },
+ ]
+
+ return (
+
+ }
+ />
+
+
+
+ {detail.order.orderId}
+
+ {detail.order.shopName || detail.order.shopId || '-'}
+
+ {detail.order.platformOrderId}
+
+
+
+
+
+
+
+ {detail.order.totalAmount} {detail.order.currency}
+
+
+ {formatAdminDateTime(detail.order.createdAt)}
+
+
+ {formatAdminDateTime(detail.order.updatedAt)}
+
+ {detail.order.itemSummary || '-'}
+
+
+
+
+
+ rowKey="orderItemId"
+ pagination={false}
+ dataSource={detail.items}
+ scroll={{ x: 760 }}
+ columns={[
+ { title: '商品', dataIndex: 'itemTitle', minWidth: 240 },
+ { title: 'SKU', dataIndex: 'skuCode', minWidth: 180 },
+ { title: '数量', dataIndex: 'quantity', width: 90 },
+ { title: '履约模式', dataIndex: 'deliveryMode', minWidth: 140 },
+ ]}
+ />
+
+
+
+
+ rowKey="taskId"
+ pagination={false}
+ dataSource={detail.tasks}
+ columns={taskColumns}
+ scroll={{ x: 900 }}
+ />
+
+
+
+
+
+
+ )
+}
+
+function BackButton() {
+ const navigate = useNavigate()
+ return (
+ } onClick={() => navigate('/admin/orders')}>
+ 返回订单
+
+ )
+}
diff --git a/apps/frontend-react/src/pages/admin/AdminOrdersPage.tsx b/apps/frontend-react/src/pages/admin/AdminOrdersPage.tsx
new file mode 100644
index 00000000..af2263eb
--- /dev/null
+++ b/apps/frontend-react/src/pages/admin/AdminOrdersPage.tsx
@@ -0,0 +1,169 @@
+import { SearchOutlined, ReloadOutlined } from '@ant-design/icons'
+import { Button, Card, Form, Input, Select, Space, Table, Typography } from 'antd'
+import type { TableColumnsType } from 'antd'
+import { useQuery } from '@tanstack/react-query'
+import { useMemo } from 'react'
+import { useNavigate, useSearchParams } from 'react-router'
+
+import PageHeader from '@/components/admin/PageHeader'
+import StatusTag from '@/components/admin/StatusTag'
+import { fetchAdminOrders } from '@/services/admin'
+import type { AdminOrderListItem } from '@/types/admin'
+import { adminPayStatusOptions } from '@/utils/admin-options'
+import { formatAdminDateTime } from '@/utils/admin-time'
+
+type OrderFilterForm = {
+ platformOrderId?: string
+ payStatus?: string
+ skuCode?: string
+}
+
+export default function AdminOrdersPage() {
+ const navigate = useNavigate()
+ const [searchParams, setSearchParams] = useSearchParams()
+ const page = Number(searchParams.get('page') || 1) || 1
+ const pageSize = Number(searchParams.get('pageSize') || 20) || 20
+ const filters = {
+ platformOrderId: searchParams.get('platformOrderId') || '',
+ payStatus: searchParams.get('payStatus') || '',
+ skuCode: searchParams.get('skuCode') || '',
+ }
+
+ const queryParams = useMemo(
+ () => ({ page, pageSize, ...filters }),
+ [filters.platformOrderId, filters.payStatus, filters.skuCode, page, pageSize],
+ )
+ const query = useQuery({
+ queryKey: ['admin-orders', queryParams],
+ queryFn: () => fetchAdminOrders(queryParams),
+ })
+ const data = query.data?.data
+
+ const columns: TableColumnsType = [
+ {
+ title: '订单信息',
+ dataIndex: 'platformOrderId',
+ minWidth: 220,
+ render: (_, row) => (
+
+ navigate(`/admin/orders/${row.orderId}`)}>
+ {row.platformOrderId}
+
+ {row.shopName || row.shopId || row.provider}
+
+ ),
+ },
+ {
+ title: '商品 / 任务',
+ minWidth: 220,
+ render: (_, row) => (
+
+ {row.itemSummary || '-'}
+
+ 商品 {row.itemCount} · 数量 {row.totalQuantity} · 任务 {row.taskCount}
+
+
+ ),
+ },
+ {
+ title: '状态',
+ minWidth: 220,
+ render: (_, row) => (
+
+
+
+
+
+ ),
+ },
+ {
+ title: '金额 / 时间',
+ minWidth: 180,
+ render: (_, row) => (
+
+ {row.totalAmount || '0.00'} {row.currency}
+ {formatAdminDateTime(row.createdAt)}
+
+ ),
+ },
+ ]
+
+ function applyFilters(values: OrderFilterForm) {
+ setSearchParams(cleanParams({ ...values, page: 1, pageSize }))
+ }
+
+ function resetFilters() {
+ setSearchParams(cleanParams({ page: 1, pageSize }))
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ } htmlType="submit">
+ 查询
+
+ } onClick={resetFilters}>
+ 重置
+
+
+
+
+
+
+
+
+ rowKey="orderId"
+ loading={query.isLoading || query.isFetching}
+ columns={columns}
+ dataSource={data?.items || []}
+ scroll={{ x: 900 }}
+ pagination={{
+ current: data?.pagination.page || page,
+ pageSize: data?.pagination.pageSize || pageSize,
+ total: data?.pagination.total || 0,
+ showSizeChanger: true,
+ showTotal: (total) => `共 ${total} 条`,
+ onChange: (nextPage, nextPageSize) => {
+ setSearchParams(cleanParams({ ...filters, page: nextPage, pageSize: nextPageSize }))
+ },
+ }}
+ />
+
+
+ )
+}
+
+function cleanParams(params: Record) {
+ const next = new URLSearchParams()
+ Object.entries(params).forEach(([key, value]) => {
+ const normalized = String(value ?? '').trim()
+ if (normalized) {
+ next.set(key, normalized)
+ }
+ })
+ return next
+}
diff --git a/apps/frontend-react/src/pages/admin/AdminTaskDetailPage.tsx b/apps/frontend-react/src/pages/admin/AdminTaskDetailPage.tsx
new file mode 100644
index 00000000..61fe67b1
--- /dev/null
+++ b/apps/frontend-react/src/pages/admin/AdminTaskDetailPage.tsx
@@ -0,0 +1,237 @@
+import { ArrowLeftOutlined, CopyOutlined, ReloadOutlined } from '@ant-design/icons'
+import {
+ Alert,
+ App,
+ Button,
+ Card,
+ Descriptions,
+ Empty,
+ Skeleton,
+ Space,
+ Table,
+ Tabs,
+ Typography,
+} from 'antd'
+import type { TableColumnsType } from 'antd'
+import { useQuery } from '@tanstack/react-query'
+import { useNavigate, useParams } from 'react-router'
+
+import JsonPreview from '@/components/admin/JsonPreview'
+import PageHeader from '@/components/admin/PageHeader'
+import StatusTag from '@/components/admin/StatusTag'
+import { fetchAdminTaskDetail } from '@/services/admin'
+import type { AdminTaskDetail } from '@/types/admin'
+import { formatTaskEventPayload, formatTaskEventType } from '@/utils/admin-display'
+import { formatAdminDateTime } from '@/utils/admin-time'
+
+type TaskEvent = AdminTaskDetail['events'][number]
+
+export default function AdminTaskDetailPage() {
+ const navigate = useNavigate()
+ const { taskId = '' } = useParams()
+ const { message } = App.useApp()
+ const query = useQuery({
+ queryKey: ['admin-task-detail', taskId],
+ queryFn: () => fetchAdminTaskDetail(taskId),
+ enabled: Boolean(taskId),
+ })
+ const detail = query.data?.data
+
+ if (query.isLoading) {
+ return (
+
+ )
+ }
+
+ if (query.error || !detail) {
+ return (
+
+ )
+ }
+
+ const resolvedDetail = detail
+ const flow = resolvedDetail.kuaishouCloudFulfillment
+ const eventColumns: TableColumnsType = [
+ {
+ title: '时间',
+ dataIndex: 'createdAt',
+ width: 180,
+ render: (value) => formatAdminDateTime(value),
+ },
+ {
+ title: '事件',
+ dataIndex: 'eventType',
+ width: 180,
+ render: (value) => formatTaskEventType(value),
+ },
+ {
+ title: '摘要',
+ render: (_, row) => formatTaskEventPayload(row.payload),
+ },
+ ]
+
+ async function copyClaimUrl() {
+ const claimUrl = resolvedDetail.claimToken?.claimUrl || ''
+ if (!claimUrl) return
+ await navigator.clipboard.writeText(claimUrl)
+ message.success('领取链接已复制')
+ }
+
+ return (
+
+
+ } onClick={() => query.refetch()}>
+ 刷新
+
+
+
+ }
+ />
+
+
+
+ {resolvedDetail.task.taskId}
+ {resolvedDetail.task.taskNo}
+
+ {resolvedDetail.task.platformOrderId || resolvedDetail.order?.platformOrderId || '-'}
+
+
+
+
+
+
+
+
+
+
+
+ {resolvedDetail.orderItem?.skuName || resolvedDetail.task.skuName || '-'}
+
+
+ {resolvedDetail.task.roleName || '-'} {resolvedDetail.task.roleId ? `(${resolvedDetail.task.roleId})` : ''}
+
+
+ {formatAdminDateTime(resolvedDetail.task.updatedAt)}
+
+
+ {resolvedDetail.task.lastError || '-'}
+
+
+
+
+ {resolvedDetail.claimToken ? (
+
+
+
+ {resolvedDetail.claimToken.claimUrl}
+
+
+
+
+ 到期:{formatAdminDateTime(resolvedDetail.claimToken.expiredAt)}
+
+ } onClick={copyClaimUrl}>
+ 复制链接
+
+
+
+
+ ) : null}
+
+ : ,
+ },
+ {
+ key: 'events',
+ label: '事件流水',
+ children: (
+
+ rowKey="eventId"
+ pagination={false}
+ columns={eventColumns}
+ dataSource={resolvedDetail.events}
+ scroll={{ x: 760 }}
+ />
+ ),
+ },
+ {
+ key: 'raw',
+ label: '原始数据',
+ children: ,
+ },
+ ]}
+ />
+
+ )
+}
+
+function KuaishouCloudPanel({ flow }: { flow: NonNullable }) {
+ return (
+
+
+
+ {flow.binding.resolvedSourceLabel || flow.binding.resolvedSourceKey || '-'}
+
+
+ {flow.binding.vnPhone || flow.binding.vnId || '-'}
+
+
+
+
+
+ {flow.role.name || '-'} {flow.role.rid ? `(${flow.role.rid})` : ''}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {flow.binding.bindUrl ? (
+
+ {flow.binding.bindUrl}
+
+ ) : (
+ '-'
+ )}
+
+
+
+ )
+}
+
+function BackButton() {
+ const navigate = useNavigate()
+ return (
+ } onClick={() => navigate('/admin/tasks')}>
+ 返回任务
+
+ )
+}
diff --git a/apps/frontend-react/src/pages/admin/AdminTasksPage.tsx b/apps/frontend-react/src/pages/admin/AdminTasksPage.tsx
new file mode 100644
index 00000000..e655b274
--- /dev/null
+++ b/apps/frontend-react/src/pages/admin/AdminTasksPage.tsx
@@ -0,0 +1,191 @@
+import { ReloadOutlined, SearchOutlined } from '@ant-design/icons'
+import { Button, Card, Form, Input, Select, Space, Table, Typography } from 'antd'
+import type { TableColumnsType } from 'antd'
+import { useQuery } from '@tanstack/react-query'
+import { useMemo } from 'react'
+import { useNavigate, useSearchParams } from 'react-router'
+
+import PageHeader from '@/components/admin/PageHeader'
+import StatusTag from '@/components/admin/StatusTag'
+import { fetchAdminTasks } from '@/services/admin'
+import type { AdminTaskListItem } from '@/types/admin'
+import { adminTaskStatusOptions } from '@/utils/admin-options'
+import { formatAdminDateTime } from '@/utils/admin-time'
+
+type TaskFilterForm = {
+ status?: string
+ platformOrderId?: string
+ taskNo?: string
+ skuCode?: string
+ roleId?: string
+}
+
+export default function AdminTasksPage() {
+ const navigate = useNavigate()
+ const [searchParams, setSearchParams] = useSearchParams()
+ const page = Number(searchParams.get('page') || 1) || 1
+ const pageSize = Number(searchParams.get('pageSize') || 20) || 20
+ const filters = {
+ status: searchParams.get('status') || '',
+ platformOrderId: searchParams.get('platformOrderId') || '',
+ taskNo: searchParams.get('taskNo') || '',
+ skuCode: searchParams.get('skuCode') || '',
+ roleId: searchParams.get('roleId') || '',
+ }
+
+ const queryParams = useMemo(
+ () => ({ page, pageSize, ...filters }),
+ [
+ filters.platformOrderId,
+ filters.roleId,
+ filters.skuCode,
+ filters.status,
+ filters.taskNo,
+ page,
+ pageSize,
+ ],
+ )
+ const query = useQuery({
+ queryKey: ['admin-tasks', queryParams],
+ queryFn: () => fetchAdminTasks(queryParams),
+ })
+ const data = query.data?.data
+
+ const columns: TableColumnsType = [
+ {
+ title: '任务 / 订单',
+ minWidth: 230,
+ render: (_, row) => (
+
+ navigate(`/admin/tasks/${row.taskId}`)}>
+ {row.taskNo}
+
+ {row.platformOrderId}
+
+ ),
+ },
+ {
+ title: '商品',
+ minWidth: 210,
+ render: (_, row) => (
+
+ {row.skuName || '-'}
+ {row.skuCode || row.executorKey}
+
+ ),
+ },
+ {
+ title: '状态',
+ minWidth: 250,
+ render: (_, row) => (
+
+
+
+
+
+ ),
+ },
+ {
+ title: '角色',
+ minWidth: 180,
+ render: (_, row) => (
+
+ {row.roleName || '-'}
+ {row.roleId || row.loginType || '-'}
+
+ ),
+ },
+ {
+ title: '时间 / 异常',
+ minWidth: 220,
+ render: (_, row) => (
+
+ {formatAdminDateTime(row.updatedAt)}
+ {row.lastError || '-'}
+
+ ),
+ },
+ ]
+
+ function applyFilters(values: TaskFilterForm) {
+ setSearchParams(cleanParams({ ...values, page: 1, pageSize }))
+ }
+
+ function resetFilters() {
+ setSearchParams(cleanParams({ page: 1, pageSize }))
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ } htmlType="submit">
+ 查询
+
+ } onClick={resetFilters}>
+ 重置
+
+
+
+
+
+
+
+
+ rowKey="taskId"
+ loading={query.isLoading || query.isFetching}
+ columns={columns}
+ dataSource={data?.items || []}
+ scroll={{ x: 1080 }}
+ pagination={{
+ current: data?.pagination.page || page,
+ pageSize: data?.pagination.pageSize || pageSize,
+ total: data?.pagination.total || 0,
+ showSizeChanger: true,
+ showTotal: (total) => `共 ${total} 条`,
+ onChange: (nextPage, nextPageSize) => {
+ setSearchParams(cleanParams({ ...filters, page: nextPage, pageSize: nextPageSize }))
+ },
+ }}
+ />
+
+
+ )
+}
+
+function cleanParams(params: Record) {
+ const next = new URLSearchParams()
+ Object.entries(params).forEach(([key, value]) => {
+ const normalized = String(value ?? '').trim()
+ if (normalized) {
+ next.set(key, normalized)
+ }
+ })
+ return next
+}
diff --git a/apps/frontend-react/src/services/admin/audit-logs.ts b/apps/frontend-react/src/services/admin/audit-logs.ts
new file mode 100644
index 00000000..66030ebe
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/audit-logs.ts
@@ -0,0 +1,9 @@
+import { apiGet } from '@/lib/http'
+import type { AdminAuditLogItem, AdminPagination } from '@/types/admin'
+
+export function fetchAdminAuditLogs(params?: Record) {
+ return apiGet<{ items: AdminAuditLogItem[]; pagination: AdminPagination }>(
+ '/api/v1/admin/audit-logs',
+ params,
+ )
+}
diff --git a/apps/frontend-react/src/services/admin/auth.ts b/apps/frontend-react/src/services/admin/auth.ts
new file mode 100644
index 00000000..aab2ddd1
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/auth.ts
@@ -0,0 +1,14 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type { AdminLoginResponse, AdminSessionSummary } from '@/types/admin'
+
+export function loginAdmin(payload: { username: string; password: string }) {
+ return apiPost('/api/v1/admin/auth/login', payload)
+}
+
+export function fetchAdminSession() {
+ return apiGet('/api/v1/admin/auth/session')
+}
+
+export function logoutAdmin() {
+ return apiPost<{ success: boolean }>('/api/v1/admin/auth/logout', {})
+}
diff --git a/apps/frontend-react/src/services/admin/dashboard.ts b/apps/frontend-react/src/services/admin/dashboard.ts
new file mode 100644
index 00000000..518d3902
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/dashboard.ts
@@ -0,0 +1,6 @@
+import { apiGet } from '@/lib/http'
+import type { AdminDashboardSummary } from '@/types/admin'
+
+export function fetchAdminDashboardSummary() {
+ return apiGet('/api/v1/admin/dashboard/summary')
+}
diff --git a/apps/frontend-react/src/services/admin/index.ts b/apps/frontend-react/src/services/admin/index.ts
new file mode 100644
index 00000000..99c97f94
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/index.ts
@@ -0,0 +1,7 @@
+export * from './auth'
+export * from './dashboard'
+export * from './users'
+export * from './audit-logs'
+export * from './platform-config'
+export * from './orders'
+export * from './tasks'
diff --git a/apps/frontend-react/src/services/admin/orders.ts b/apps/frontend-react/src/services/admin/orders.ts
new file mode 100644
index 00000000..02de9ed5
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/orders.ts
@@ -0,0 +1,13 @@
+import { apiGet } from '@/lib/http'
+import type { AdminOrderDetail, AdminOrderListItem, AdminPagination } from '@/types/admin'
+
+export function fetchAdminOrders(params?: Record) {
+ return apiGet<{ items: AdminOrderListItem[]; pagination: AdminPagination }>(
+ '/api/v1/admin/orders',
+ params,
+ )
+}
+
+export function fetchAdminOrderDetail(orderId: number | string) {
+ return apiGet(`/api/v1/admin/orders/${orderId}`)
+}
diff --git a/apps/frontend-react/src/services/admin/platform-config/cloudtentacles.ts b/apps/frontend-react/src/services/admin/platform-config/cloudtentacles.ts
new file mode 100644
index 00000000..30e5890a
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/platform-config/cloudtentacles.ts
@@ -0,0 +1,336 @@
+import { apiDelete, apiGet, apiPost } from '@/lib/http'
+import type {
+ AdminCloudtentaclesLoginTestResult,
+ AdminCloudtentaclesDeliveryRecordListResult,
+ AdminCloudtentaclesOverrideRuleConfig,
+ AdminCloudtentaclesSkuListResult,
+ AdminCloudtentaclesSendSmsResult,
+ AdminCloudtentaclesSourceConfigResponse,
+ AdminCloudtentaclesSourceItem,
+ AdminCloudtentaclesSourcesConfig,
+ AdminCloudtentaclesValidateSessionResult,
+} from '@/types/admin'
+
+export function fetchAdminCloudtentaclesSourceConfig() {
+ return apiGet(
+ '/api/v1/admin/platform-config/cloudtentacles-source',
+ )
+}
+
+export function saveAdminCloudtentaclesSourceConfig(
+ payload:
+ | AdminCloudtentaclesSourcesConfig
+ | ({ sourceKey: string } & Partial),
+) {
+ return apiPost(
+ '/api/v1/admin/platform-config/cloudtentacles-source',
+ payload,
+ )
+}
+
+export function deleteAdminCloudtentaclesSource(sourceKey: string) {
+ return apiDelete<{ success: boolean }>(
+ `/api/v1/admin/platform-config/cloudtentacles-source/${sourceKey}`,
+ )
+}
+
+export function fetchAdminCloudtentaclesOverrideRules() {
+ return apiGet(
+ '/api/v1/admin/platform-config/cloudtentacles/override-rules',
+ )
+}
+
+export function saveAdminCloudtentaclesOverrideRules(
+ payload: {
+ enabled: boolean
+ rules: Array<{
+ id?: string
+ enabled?: boolean
+ productName?: string
+ sourceKey?: string
+ deliveryItems?: Array<{
+ cloudSkuId?: number
+ cloudSkuName?: string
+ quantity?: number
+ }>
+ notes?: string
+ }>
+ },
+) {
+ return apiPost(
+ '/api/v1/admin/platform-config/cloudtentacles/override-rules',
+ payload,
+ )
+}
+
+export function sendAdminCloudtentaclesSmsCode(payload: {
+ baseUrl?: string
+ username?: string
+ phone?: string
+ deviceId?: string
+ deviceType?: number
+ sourceKey?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/cloudtentacles/send-sms-code',
+ payload,
+ )
+}
+
+export function testAdminCloudtentaclesLogin(payload: {
+ baseUrl?: string
+ username?: string
+ password?: string
+ phone?: string
+ code?: string
+ deviceId?: string
+ deviceType?: number
+ sourceKey?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/cloudtentacles/test-login',
+ payload,
+ )
+}
+
+export function validateAdminCloudtentaclesSession(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ sourceKey?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/cloudtentacles/validate-session',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesAsset(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/asset',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesCategories(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/categories',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesSkuList(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ sourceKey?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/cloudtentacles/sku/list',
+ payload,
+ )
+}
+
+export function buyAdminCloudtentaclesSku(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ id?: number
+ count?: number
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/sku/buy',
+ payload,
+ )
+}
+
+export function useAdminCloudtentaclesSku(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ id?: number
+ virtualNumberId?: number
+ phone?: string
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/sku/use',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesKnapsack(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/knapsack',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesDeliveryRecords(payload: {
+ sourceKey?: string
+ page?: number
+ size?: number
+ startDate?: string
+ endDate?: string
+ platformOrderId?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/cloudtentacles-records',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesRecordSources() {
+ return apiGet<{
+ sources: Array<{
+ key: string
+ label: string
+ enabled: boolean
+ hasToken: boolean
+ loggedInAt: string
+ }>
+ }>('/api/v1/admin/cloudtentacles-records/sources')
+}
+
+export function fetchAdminCloudtentaclesVnList(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ key?: string
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/vn/list',
+ payload,
+ )
+}
+
+export function appointAdminCloudtentaclesVn(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ key?: string
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/vn/appoint',
+ payload,
+ )
+}
+
+export function generateAdminCloudtentaclesVnLoginCode(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ key?: string
+ id?: number
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/vn/generate-login-code',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesVnCode(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ key?: string
+ phone?: string
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/vn/fetch-code',
+ payload,
+ )
+}
+
+export function verifyAdminCloudtentaclesVnCode(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ key?: string
+ id?: number
+ code?: string
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/vn/verify-code',
+ payload,
+ )
+}
+
+export function fetchAdminCloudtentaclesBindUrl(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ key?: string
+ id?: number
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/vn/bind-url',
+ payload,
+ )
+}
+
+export function backAdminCloudtentaclesVn(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ key?: string
+ id?: number
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/vn/back',
+ payload,
+ )
+}
+
+export function runAdminCloudtentaclesFullFlow(payload: {
+ baseUrl?: string
+ token?: string
+ deviceId?: string
+ deviceType?: number
+ skuId?: number
+ skuCount?: number
+ vnKey?: string
+ sourceKey?: string
+}) {
+ return apiPost>(
+ '/api/v1/admin/platform-config/cloudtentacles/debug/full-flow',
+ payload,
+ )
+}
diff --git a/apps/frontend-react/src/services/admin/platform-config/index.ts b/apps/frontend-react/src/services/admin/platform-config/index.ts
new file mode 100644
index 00000000..9f73f850
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/platform-config/index.ts
@@ -0,0 +1,6 @@
+export * from './notifications'
+export * from './scheduled-jobs'
+export * from './ninetyone'
+export * from './kuaishou-eticket'
+export * from './kuaishou-feifei'
+export * from './cloudtentacles'
diff --git a/apps/frontend-react/src/services/admin/platform-config/kuaishou-eticket.ts b/apps/frontend-react/src/services/admin/platform-config/kuaishou-eticket.ts
new file mode 100644
index 00000000..cd324b2e
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/platform-config/kuaishou-eticket.ts
@@ -0,0 +1,60 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type {
+ AdminKuaishouEticketConsumeResult,
+ AdminKuaishouEticketDetailResult,
+ AdminKuaishouEticketShopInfoResult,
+ AdminKuaishouEticketSourceConfig,
+} from '@/types/admin'
+
+export function fetchAdminKuaishouEticketSourceConfig() {
+ return apiGet<{
+ filePath: string
+ source: AdminKuaishouEticketSourceConfig
+ }>('/api/v1/admin/platform-config/kuaishou-eticket-source')
+}
+
+export function saveAdminKuaishouEticketSourceConfig(payload: AdminKuaishouEticketSourceConfig) {
+ return apiPost<{
+ filePath: string
+ source: AdminKuaishouEticketSourceConfig
+ }>('/api/v1/admin/platform-config/kuaishou-eticket-source', payload)
+}
+
+export function queryAdminKuaishouEticketDetail(payload: {
+ baseUrl?: string
+ shopId?: string
+ cookie?: string
+ eTicketId: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-eticket/query-detail',
+ payload,
+ )
+}
+
+export function queryAdminKuaishouEticketShopInfo(payload: {
+ baseUrl?: string
+ shopId?: string
+ cookie?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-eticket/query-shop-info',
+ payload,
+ )
+}
+
+export function consumeAdminKuaishouEticket(payload: {
+ baseUrl?: string
+ shopId?: string
+ cookie?: string
+ eTicketId: string
+ oid?: string
+ formToken?: string
+ num?: number
+ storeId?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-eticket/consume',
+ payload,
+ )
+}
diff --git a/apps/frontend-react/src/services/admin/platform-config/kuaishou-feifei.ts b/apps/frontend-react/src/services/admin/platform-config/kuaishou-feifei.ts
new file mode 100644
index 00000000..b5324f95
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/platform-config/kuaishou-feifei.ts
@@ -0,0 +1,79 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type {
+ AdminKuaishouFeifeiConfig,
+ AdminKuaishouFeifeiConfigResponse,
+ AdminKuaishouFeifeiMatchResult,
+ AdminKuaishouFeifeiOrderResult,
+ AdminKuaishouFeifeiProductListResult,
+ AdminKuaishouFeifeiProductSyncResult,
+} from '@/types/admin'
+
+export function fetchAdminKuaishouFeifeiConfig() {
+ return apiGet(
+ '/api/v1/admin/platform-config/kuaishou-feifei',
+ )
+}
+
+export function saveAdminKuaishouFeifeiConfig(payload: AdminKuaishouFeifeiConfig) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-feifei',
+ payload,
+ )
+}
+
+export function matchAdminKuaishouFeifeiProduct(productName: string) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-feifei/match',
+ { productName },
+ )
+}
+
+export function fetchAdminKuaishouFeifeiProducts(payload: {
+ page?: number
+ perPage?: number
+ status?: string
+ supplyProductName?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-feifei/products',
+ payload,
+ )
+}
+
+export function syncAdminKuaishouFeifeiProducts(payload: {
+ status?: string
+ supplyProductName?: string
+} = {}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-feifei/sync-products',
+ payload,
+ )
+}
+
+export function createAdminKuaishouFeifeiTestOrder(payload: {
+ platformOrderNo: string
+ productCode: string
+ platformBuyNum?: number
+ platformAmount?: number | null
+ playerAccount?: string
+ playerGameRegion?: string
+ playerGameSrv?: string
+ playerGameRole?: string
+ submitPlayer?: boolean
+ notifyUrl?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-feifei/test-order',
+ payload,
+ )
+}
+
+export function queryAdminKuaishouFeifeiOrder(payload: {
+ platformOrderNo?: string
+ orderNo?: string
+}) {
+ return apiPost(
+ '/api/v1/admin/platform-config/kuaishou-feifei/query-order',
+ payload,
+ )
+}
diff --git a/apps/frontend-react/src/services/admin/platform-config/ninetyone.ts b/apps/frontend-react/src/services/admin/platform-config/ninetyone.ts
new file mode 100644
index 00000000..c5ef2aeb
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/platform-config/ninetyone.ts
@@ -0,0 +1,42 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type {
+ AdminNinetyoneOrderActionResult,
+ AdminNinetyoneOrderListResult,
+} from '@/types/admin'
+
+export function fetchAdminNinetyoneOrders(
+ params: {
+ page?: number
+ pageSize?: number
+ status?: string
+ } = {},
+) {
+ const searchParams = new URLSearchParams()
+ if (params.page) {
+ searchParams.set('page', String(params.page))
+ }
+ if (params.pageSize) {
+ searchParams.set('pageSize', String(params.pageSize))
+ }
+ if (params.status) {
+ searchParams.set('status', params.status)
+ }
+ const queryString = searchParams.toString()
+ return apiGet(
+ `/api/v1/admin/platform-config/ninetyone/orders${queryString ? `?${queryString}` : ''}`,
+ )
+}
+
+export function retryAdminNinetyoneOrder(orderId: number | string) {
+ return apiPost(
+ `/api/v1/admin/platform-config/ninetyone/orders/${orderId}/retry`,
+ {},
+ )
+}
+
+export function failAdminNinetyoneOrder(orderId: number | string, payload: { reason?: string }) {
+ return apiPost(
+ `/api/v1/admin/platform-config/ninetyone/orders/${orderId}/fail`,
+ payload,
+ )
+}
diff --git a/apps/frontend-react/src/services/admin/platform-config/notifications.ts b/apps/frontend-react/src/services/admin/platform-config/notifications.ts
new file mode 100644
index 00000000..f5056037
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/platform-config/notifications.ts
@@ -0,0 +1,23 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type { AdminNotificationConfig, AdminNotificationTestResult } from '@/types/admin'
+
+export function fetchAdminNotificationConfig() {
+ return apiGet<{
+ filePath: string
+ source: AdminNotificationConfig
+ }>('/api/v1/admin/platform-config/notifications')
+}
+
+export function saveAdminNotificationConfig(payload: AdminNotificationConfig) {
+ return apiPost<{
+ filePath: string
+ source: AdminNotificationConfig
+ }>('/api/v1/admin/platform-config/notifications', payload)
+}
+
+export function testAdminNotification(payload: { title?: string; body?: string; url?: string }) {
+ return apiPost(
+ '/api/v1/admin/platform-config/notifications/test',
+ payload,
+ )
+}
diff --git a/apps/frontend-react/src/services/admin/platform-config/scheduled-jobs.ts b/apps/frontend-react/src/services/admin/platform-config/scheduled-jobs.ts
new file mode 100644
index 00000000..55089011
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/platform-config/scheduled-jobs.ts
@@ -0,0 +1,20 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type { AdminScheduledJobsConfig, AdminScheduledJobsResponse } from '@/types/admin'
+
+export function fetchAdminScheduledJobsConfig() {
+ return apiGet('/api/v1/admin/platform-config/scheduled-jobs')
+}
+
+export function saveAdminScheduledJobsConfig(payload: AdminScheduledJobsConfig) {
+ return apiPost(
+ '/api/v1/admin/platform-config/scheduled-jobs',
+ payload,
+ )
+}
+
+export function runAdminScheduledJob(jobId: string) {
+ return apiPost<{
+ result: Record
+ runtime: AdminScheduledJobsResponse['runtime']
+ }>(`/api/v1/admin/platform-config/scheduled-jobs/${jobId}/run`, {})
+}
diff --git a/apps/frontend-react/src/services/admin/tasks.ts b/apps/frontend-react/src/services/admin/tasks.ts
new file mode 100644
index 00000000..4e715205
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/tasks.ts
@@ -0,0 +1,93 @@
+import { apiGet, apiGetBlob, apiPost } from '@/lib/http'
+import type {
+ AdminPagination,
+ AdminTaskActionResponse,
+ AdminTaskDetail,
+ AdminTaskListItem,
+} from '@/types/admin'
+
+export function fetchAdminTasks(params?: Record) {
+ return apiGet<{ items: AdminTaskListItem[]; pagination: AdminPagination }>(
+ '/api/v1/admin/tasks',
+ params,
+ )
+}
+
+export function fetchAdminTaskDetail(taskId: number | string) {
+ return apiGet(`/api/v1/admin/tasks/${taskId}`)
+}
+
+export function fetchAdminTaskScreenshot(taskId: number | string) {
+ return apiGetBlob(`/api/v1/admin/tasks/${taskId}/screenshot`)
+}
+
+export function retryAdminTask(taskId: number | string) {
+ return apiPost(`/api/v1/admin/tasks/${taskId}/retry`, {})
+}
+
+export function regenerateAdminTaskClaimLink(taskId: number | string) {
+ return apiPost(`/api/v1/admin/tasks/${taskId}/regenerate-claim-link`, {})
+}
+
+export function closeAdminTask(taskId: number | string) {
+ return apiPost(`/api/v1/admin/tasks/${taskId}/close`, {})
+}
+
+export function markAdminTaskManualReview(taskId: number | string) {
+ return apiPost(`/api/v1/admin/tasks/${taskId}/mark-manual-review`, {})
+}
+
+export function completeAdminTaskManualDispatch(
+ taskId: number | string,
+ payload: {
+ outcome: 'delivered' | 'failed'
+ resultMessage?: string
+ deliveryReference?: string
+ deliveredCredential?: string
+ },
+) {
+ return apiPost(
+ `/api/v1/admin/tasks/${taskId}/complete-manual-dispatch`,
+ payload,
+ )
+}
+
+export function prepareAdminTaskKuaishouCloudFulfillment(taskId: number | string) {
+ return apiPost(
+ `/api/v1/admin/tasks/${taskId}/kuaishou-cloud/prepare`,
+ {},
+ )
+}
+
+export function refreshAdminTaskKuaishouCloudRoleInfo(taskId: number | string) {
+ return apiPost(
+ `/api/v1/admin/tasks/${taskId}/kuaishou-cloud/refresh-role-info`,
+ {},
+ )
+}
+
+export function rebindAdminTaskKuaishouCloudRole(taskId: number | string) {
+ return apiPost(
+ `/api/v1/admin/tasks/${taskId}/kuaishou-cloud/rebind-role`,
+ {},
+ )
+}
+
+export function dispatchAdminTaskKuaishouCloudFulfillment(
+ taskId: number | string,
+ payload: {
+ ticketCode?: string
+ } = {},
+) {
+ return apiPost(
+ `/api/v1/admin/tasks/${taskId}/kuaishou-cloud/dispatch`,
+ payload,
+ )
+}
+
+export function returnNumberAdminTaskKuaishouCloudFulfillment(taskId: number | string) {
+ return apiPost(
+ `/api/v1/admin/tasks/${taskId}/kuaishou-cloud/return-number`,
+ {},
+ )
+}
diff --git a/apps/frontend-react/src/services/admin/users.ts b/apps/frontend-react/src/services/admin/users.ts
new file mode 100644
index 00000000..039a1ddb
--- /dev/null
+++ b/apps/frontend-react/src/services/admin/users.ts
@@ -0,0 +1,33 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type { AdminPagination, AdminUserListItem } from '@/types/admin'
+
+export function fetchAdminUsers(params?: Record) {
+ return apiGet<{ items: AdminUserListItem[]; pagination: AdminPagination }>(
+ '/api/v1/admin/users',
+ params,
+ )
+}
+
+export function createAdminUser(payload: {
+ username: string
+ password: string
+ role: string
+ status?: string
+}) {
+ return apiPost<{ user: AdminUserListItem }>('/api/v1/admin/users', payload)
+}
+
+export function updateAdminUserRole(userId: number | string, payload: { role: string }) {
+ return apiPost<{ user: AdminUserListItem }>(`/api/v1/admin/users/${userId}/role`, payload)
+}
+
+export function updateAdminUserStatus(userId: number | string, payload: { status: string }) {
+ return apiPost<{ user: AdminUserListItem }>(`/api/v1/admin/users/${userId}/status`, payload)
+}
+
+export function resetAdminUserPassword(userId: number | string, payload: { password: string }) {
+ return apiPost<{ user: AdminUserListItem }>(
+ `/api/v1/admin/users/${userId}/reset-password`,
+ payload,
+ )
+}
diff --git a/apps/frontend-react/src/services/claim.ts b/apps/frontend-react/src/services/claim.ts
new file mode 100644
index 00000000..e5b508c2
--- /dev/null
+++ b/apps/frontend-react/src/services/claim.ts
@@ -0,0 +1,27 @@
+import { apiGet, apiPost } from '@/lib/http'
+import type { ClaimDetailData } from '@/types/claim'
+
+export function fetchClaimDetail(token: string) {
+ return apiGet(`/api/v1/claim/${token}`)
+}
+
+export function verifyKuaishouCloudClaimTicket(
+ token: string,
+ payload: {
+ ticketCode: string
+ },
+) {
+ return apiPost(`/api/v1/claim/${token}/kuaishou-cloud/verify-ticket`, payload)
+}
+
+export function confirmKuaishouCloudClaimRole(token: string) {
+ return apiPost(`/api/v1/claim/${token}/kuaishou-cloud/confirm-role`, {})
+}
+
+export function rebindKuaishouCloudClaimRole(token: string) {
+ return apiPost(`/api/v1/claim/${token}/kuaishou-cloud/rebind-role`, {})
+}
+
+export function redeemKuaishouCloudClaim(token: string) {
+ return apiPost(`/api/v1/claim/${token}/kuaishou-cloud/redeem`, {})
+}
diff --git a/apps/frontend-react/src/styles/main.css b/apps/frontend-react/src/styles/main.css
new file mode 100644
index 00000000..d02360f0
--- /dev/null
+++ b/apps/frontend-react/src/styles/main.css
@@ -0,0 +1,195 @@
+:root {
+ color: #1f2937;
+ background: #f5f7fb;
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+button,
+input,
+textarea,
+select {
+ font: inherit;
+}
+
+.login-page {
+ min-height: 100vh;
+ display: grid;
+ place-items: center;
+ padding: 24px;
+ background:
+ linear-gradient(135deg, rgba(22, 119, 255, 0.1), rgba(19, 194, 194, 0.08)),
+ #f5f7fb;
+}
+
+.login-panel {
+ width: min(440px, 100%);
+ box-shadow: 0 18px 50px rgba(15, 23, 42, 0.12);
+}
+
+.login-heading {
+ margin-bottom: 24px;
+}
+
+.login-heading h1 {
+ margin: 8px 0 0;
+ font-size: 28px;
+}
+
+.eyebrow {
+ color: #1677ff;
+ font-weight: 700;
+ letter-spacing: 0;
+}
+
+.admin-shell {
+ min-height: 100vh;
+}
+
+.admin-sider {
+ border-right: 1px solid #edf0f5;
+ box-shadow: 4px 0 18px rgba(15, 23, 42, 0.04);
+ z-index: 2;
+}
+
+.admin-brand {
+ height: 64px;
+ display: grid;
+ align-content: center;
+ padding: 0 20px;
+ border-bottom: 1px solid #edf0f5;
+}
+
+.admin-brand strong {
+ color: #111827;
+ font-size: 18px;
+}
+
+.admin-brand span {
+ color: #6b7280;
+ font-size: 12px;
+}
+
+.admin-topbar {
+ height: 64px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 20px;
+ border-bottom: 1px solid #edf0f5;
+}
+
+.admin-user {
+ margin-left: auto;
+}
+
+.admin-user-copy {
+ display: grid;
+ justify-items: end;
+ line-height: 1.3;
+}
+
+.admin-content {
+ padding: 20px;
+}
+
+.page-stack {
+ display: grid;
+ gap: 16px;
+}
+
+.page-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 16px;
+}
+
+.page-title {
+ margin: 0 !important;
+}
+
+.page-description {
+ margin: 6px 0 0;
+ color: #6b7280;
+}
+
+.filter-form {
+ row-gap: 12px;
+}
+
+.cell-stack {
+ display: grid;
+ gap: 4px;
+}
+
+.muted {
+ color: #6b7280;
+}
+
+.json-preview {
+ max-height: 460px;
+ overflow: auto;
+ margin: 0;
+ padding: 14px;
+ border: 1px solid #edf0f5;
+ border-radius: 6px;
+ background: #0f172a;
+ color: #dbeafe;
+ font-size: 12px;
+ line-height: 1.6;
+}
+
+.empty-page {
+ min-height: 50vh;
+ display: grid;
+ place-items: center;
+}
+
+.route-loading {
+ min-height: 100vh;
+ display: grid;
+ place-items: center;
+}
+
+.full-width {
+ width: 100%;
+}
+
+.feedback-prompt p {
+ margin-top: 0;
+}
+
+@media (max-width: 900px) {
+ .admin-sider {
+ position: fixed !important;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ height: 100vh;
+ }
+
+ .admin-content {
+ padding: 14px;
+ }
+
+ .admin-user-copy {
+ display: none;
+ }
+
+ .page-header {
+ display: grid;
+ }
+}
diff --git a/apps/frontend-react/src/types/admin/audit-logs.ts b/apps/frontend-react/src/types/admin/audit-logs.ts
new file mode 100644
index 00000000..d54fc10b
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/audit-logs.ts
@@ -0,0 +1,13 @@
+import type { AdminRole } from './auth'
+
+export interface AdminAuditLogItem {
+ logId: number
+ actorUserId: number
+ actorUsername: string
+ actorRole: AdminRole
+ action: string
+ targetType: string
+ targetId: string
+ payload: Record
+ createdAt: string
+}
diff --git a/apps/frontend-react/src/types/admin/auth.ts b/apps/frontend-react/src/types/admin/auth.ts
new file mode 100644
index 00000000..d9d23fc7
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/auth.ts
@@ -0,0 +1,22 @@
+export type AdminRole = 'admin' | 'operator' | 'support'
+export type AdminUserStatus = 'active' | 'disabled'
+
+export interface AdminLoginResponse {
+ token: string
+ expiresAt: string
+ user: {
+ userId: number
+ username: string
+ role: AdminRole
+ }
+}
+
+export interface AdminSessionSummary {
+ authenticated: boolean
+ expiresAt: string
+ user: {
+ userId: number
+ username: string
+ role: AdminRole
+ }
+}
diff --git a/apps/frontend-react/src/types/admin/common.ts b/apps/frontend-react/src/types/admin/common.ts
new file mode 100644
index 00000000..29821fba
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/common.ts
@@ -0,0 +1,5 @@
+export interface AdminPagination {
+ page: number
+ pageSize: number
+ total: number
+}
diff --git a/apps/frontend-react/src/types/admin/dashboard.ts b/apps/frontend-react/src/types/admin/dashboard.ts
new file mode 100644
index 00000000..7d666bf0
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/dashboard.ts
@@ -0,0 +1,7 @@
+export interface AdminDashboardSummary {
+ todayOrders: number
+ paidPendingClaim: number
+ claimingTasks: number
+ redeemedToday: number
+ abnormalTasks: number
+}
diff --git a/apps/frontend-react/src/types/admin/index.ts b/apps/frontend-react/src/types/admin/index.ts
new file mode 100644
index 00000000..db08037a
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/index.ts
@@ -0,0 +1,81 @@
+// Common types
+export type { AdminPagination } from './common'
+
+// Auth types
+export type { AdminRole, AdminUserStatus, AdminLoginResponse, AdminSessionSummary } from './auth'
+
+// Users types
+export type { AdminUserListItem } from './users'
+
+// Audit logs types
+export type { AdminAuditLogItem } from './audit-logs'
+
+// Dashboard types
+export type { AdminDashboardSummary } from './dashboard'
+
+// Orders types
+export type {
+ AdminOrderFulfillmentProgress,
+ AdminOrderListItem,
+ AdminOrderDetail,
+} from './orders'
+
+// Tasks types
+export type {
+ AdminTaskListItem,
+ AdminTaskOperations,
+ AdminTaskActionResponse,
+ AdminTaskDetail,
+} from './tasks'
+
+// Platform config types
+export type {
+ AdminNotificationBarkRecipient,
+ AdminNotificationWpushRecipient,
+ AdminNotificationConfig,
+ AdminNotificationSendResult,
+ AdminNotificationTestResult,
+ AdminCloudtentaclesMonitorAccount,
+ AdminScheduledJobAccountRuntime,
+ AdminScheduledJobCloudtentaclesAccount,
+ AdminScheduledJobItem,
+ AdminScheduledJobsConfig,
+ AdminScheduledJobRuntimeState,
+ AdminScheduledJobsResponse,
+ AdminNinetyoneOrderItem,
+ AdminNinetyoneOrderListResult,
+ AdminNinetyoneOrderActionResult,
+ AdminKuaishouEticketSourceConfig,
+ AdminKuaishouEticketShopConfigItem,
+ AdminKuaishouEticketShopInfoResult,
+ AdminKuaishouEticketDetailResult,
+ AdminKuaishouEticketConsumeResult,
+ AdminKuaishouFeifeiProductRule,
+ AdminKuaishouFeifeiConfig,
+ AdminKuaishouFeifeiEffectiveConfig,
+ AdminKuaishouFeifeiConfigResponse,
+ AdminKuaishouFeifeiProductSyncResult,
+ AdminKuaishouFeifeiProductMatch,
+ AdminKuaishouFeifeiMatchResult,
+ AdminKuaishouFeifeiProductItem,
+ AdminKuaishouFeifeiProductListResult,
+ AdminKuaishouFeifeiOrderResult,
+ AdminCloudtentaclesSourceItem,
+ AdminCloudtentaclesSourcesConfig,
+ AdminCloudtentaclesSourceConfigResponse,
+ AdminCloudtentaclesOverrideDeliveryItem,
+ AdminCloudtentaclesOverrideRule,
+ AdminCloudtentaclesOverrideRuleConfig,
+ AdminCloudtentaclesSessionsMap,
+ AdminCloudtentaclesSourceConfig,
+ AdminCloudtentaclesPersistedSession,
+ AdminCloudtentaclesSessionItem,
+ AdminCloudtentaclesSendSmsResult,
+ AdminCloudtentaclesSessionSummary,
+ AdminCloudtentaclesLoginTestResult,
+ AdminCloudtentaclesValidateSessionResult,
+ AdminCloudtentaclesSkuItem,
+ AdminCloudtentaclesSkuListResult,
+ AdminCloudtentaclesDeliveryRecordItem,
+ AdminCloudtentaclesDeliveryRecordListResult,
+} from './platform-config'
diff --git a/apps/frontend-react/src/types/admin/orders.ts b/apps/frontend-react/src/types/admin/orders.ts
new file mode 100644
index 00000000..c41d39aa
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/orders.ts
@@ -0,0 +1,72 @@
+import type { AdminTaskListItem } from './tasks'
+
+export interface AdminOrderFulfillmentProgress {
+ totalTaskCount: number
+ preparedTaskCount: number
+ completedTaskCount: number
+ resourceStatus: string
+ customerStatus: string
+}
+
+export interface AdminOrderListItem {
+ orderId: number
+ provider: string
+ platform: string
+ shopId: string
+ shopName: string
+ platformOrderId: string
+ orderStatus: string
+ payStatus: string
+ buyerId: string
+ buyerName: string
+ receiverContact: string
+ totalAmount: string
+ totalAmountFen: number
+ currency: string
+ paidAt: string | null
+ createdAt: string
+ updatedAt: string
+ itemCount: number
+ totalQuantity: number
+ itemSummary: string
+ taskCount: number
+ resourceStatus: string
+ customerStatus: string
+ preparedTaskCount: number
+ completedTaskCount: number
+}
+
+export interface AdminOrderDetail {
+ order: {
+ orderId: number
+ provider: string
+ platform: string
+ shopId: string
+ shopName: string
+ platformOrderId: string
+ orderStatus: string
+ payStatus: string
+ buyerId: string
+ buyerName: string
+ receiverContact: string
+ totalAmount: string
+ totalAmountFen: number
+ currency: string
+ paidAt: string | null
+ createdAt: string
+ updatedAt: string
+ itemSummary: string
+ rawPayload: Record
+ fulfillmentProgress: AdminOrderFulfillmentProgress
+ }
+ items: Array<{
+ orderItemId: number
+ skuCode: string
+ skuName: string
+ itemTitle: string
+ quantity: number
+ deliveryMode: string
+ spec: Record
+ }>
+ tasks: AdminTaskListItem[]
+}
diff --git a/apps/frontend-react/src/types/admin/platform-config/cloudtentacles.ts b/apps/frontend-react/src/types/admin/platform-config/cloudtentacles.ts
new file mode 100644
index 00000000..6442d35f
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/platform-config/cloudtentacles.ts
@@ -0,0 +1,181 @@
+export interface AdminCloudtentaclesSourceItem {
+ key: string
+ label: string
+ enabled: boolean
+ baseUrl: string
+ username: string
+ password: string
+ phone: string
+ deviceId: string
+ deviceType: number
+}
+
+export interface AdminCloudtentaclesSourcesConfig {
+ enabled: boolean
+ sources: AdminCloudtentaclesSourceItem[]
+}
+
+export interface AdminCloudtentaclesPersistedSession {
+ token: string
+ tokenMasked: string
+ baseUrl: string
+ username: string
+ phoneMasked: string
+ loggedInAt: string
+ deviceId: string
+ deviceType: number
+ hasToken: boolean
+}
+
+export type AdminCloudtentaclesSessionItem = AdminCloudtentaclesPersistedSession
+
+export interface AdminCloudtentaclesSessionsMap {
+ [sourceKey: string]: AdminCloudtentaclesPersistedSession
+}
+
+export interface AdminCloudtentaclesSourceConfigResponse {
+ filePath: string
+ sessionFilePath: string
+ enabled: boolean
+ sources: AdminCloudtentaclesSourceItem[]
+ sessions: AdminCloudtentaclesSessionsMap
+}
+
+export interface AdminCloudtentaclesOverrideDeliveryItem {
+ cloudSkuId: number
+ cloudSkuName: string
+ quantity: number
+}
+
+export interface AdminCloudtentaclesOverrideRule {
+ id: string
+ enabled: boolean
+ productName: string
+ normalizedProductName: string
+ sourceKey: string
+ deliveryItems: AdminCloudtentaclesOverrideDeliveryItem[]
+ notes: string
+}
+
+export interface AdminCloudtentaclesOverrideRuleConfig {
+ filePath: string
+ enabled: boolean
+ rules: AdminCloudtentaclesOverrideRule[]
+}
+
+export interface AdminCloudtentaclesSendSmsResult {
+ baseUrl: string
+ username: string
+ phone: string
+ phoneMasked: string
+ sentAt: string
+ responseMessage: string
+}
+
+export interface AdminCloudtentaclesSessionSummary {
+ tokenMasked: string
+ permissionCount: number
+ permissions: string[]
+ persisted?: boolean
+}
+
+export interface AdminCloudtentaclesLoginTestResult {
+ baseUrl: string
+ username: string
+ phoneMasked: string
+ loggedInAt: string
+ responseMessage: string
+ token: string
+ session: AdminCloudtentaclesSessionSummary
+ userInfo: Record
+ asset: number
+}
+
+export interface AdminCloudtentaclesValidateSessionResult {
+ baseUrl: string
+ loggedInAt: string
+ session: AdminCloudtentaclesSessionSummary
+ userInfo: Record
+ asset: number
+}
+
+export interface AdminCloudtentaclesSkuItem {
+ id: number
+ categoriesId: number
+ name: string
+ description: string
+ image: string
+ inventory: number
+ price: number
+ listingTime: string
+ delistingTime: string
+ buyLimitMin: number
+ buyLimitMax: number
+ raw: Record
+}
+
+export interface AdminCloudtentaclesSkuListResult {
+ baseUrl: string
+ itemCount: number
+ items: AdminCloudtentaclesSkuItem[]
+ rawItems: Record[]
+}
+
+export interface AdminCloudtentaclesDeliveryRecordItem {
+ createdAt: string
+ recordId: string
+ virtualNumberId: number
+ userId: number
+ count: number
+ cdk: string
+ exchangeUrl: string
+ name: string
+ skuId: number
+ skuName: string
+ skuImage: string
+ phone: string
+ status: number
+ fulfillUser: string
+ operatorName: string
+ platformOrderId: string
+ taskId: number
+ taskNo: string
+ orderShopId: string
+ orderShopName: string
+ consumeShopId: string
+ consumeShopName: string
+ roleName: string
+ roleId: string
+ localTaskStatus: string
+ localDeliveryStatus: string
+ localDispatchStatus: string
+ localDispatchAt: string
+ localResultCode: string
+ localResultMessage: string
+ matchConfidence: string
+ raw: Record
+}
+
+export interface AdminCloudtentaclesDeliveryRecordListResult {
+ baseUrl: string
+ page: number
+ size: number
+ total: number
+ sourceKey?: string
+ queryStartDate?: string
+ queryEndDate?: string
+ localOrderMatched?: boolean
+ items: AdminCloudtentaclesDeliveryRecordItem[]
+ raw: Record
+}
+
+/** @deprecated Use AdminCloudtentaclesSourceItem + AdminCloudtentaclesSourcesConfig instead */
+export interface AdminCloudtentaclesSourceConfig {
+ enabled: boolean
+ baseUrl: string
+ username: string
+ password: string
+ phone: string
+ deviceId: string
+ deviceType: number
+}
diff --git a/apps/frontend-react/src/types/admin/platform-config/index.ts b/apps/frontend-react/src/types/admin/platform-config/index.ts
new file mode 100644
index 00000000..560c4c6d
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/platform-config/index.ts
@@ -0,0 +1,65 @@
+export type {
+ AdminNotificationBarkRecipient,
+ AdminNotificationWpushRecipient,
+ AdminNotificationConfig,
+ AdminNotificationSendResult,
+ AdminNotificationTestResult,
+} from './notifications'
+
+export type {
+ AdminCloudtentaclesMonitorAccount,
+ AdminScheduledJobAccountRuntime,
+ AdminScheduledJobCloudtentaclesAccount,
+ AdminScheduledJobItem,
+ AdminScheduledJobsConfig,
+ AdminScheduledJobRuntimeState,
+ AdminScheduledJobsResponse,
+} from './scheduled-jobs'
+
+export type {
+ AdminNinetyoneOrderItem,
+ AdminNinetyoneOrderListResult,
+ AdminNinetyoneOrderActionResult,
+} from './ninetyone'
+
+export type {
+ AdminKuaishouEticketSourceConfig,
+ AdminKuaishouEticketShopConfigItem,
+ AdminKuaishouEticketShopInfoResult,
+ AdminKuaishouEticketDetailResult,
+ AdminKuaishouEticketConsumeResult,
+} from './kuaishou-eticket'
+
+export type {
+ AdminKuaishouFeifeiProductRule,
+ AdminKuaishouFeifeiConfig,
+ AdminKuaishouFeifeiEffectiveConfig,
+ AdminKuaishouFeifeiConfigResponse,
+ AdminKuaishouFeifeiProductSyncResult,
+ AdminKuaishouFeifeiProductMatch,
+ AdminKuaishouFeifeiMatchResult,
+ AdminKuaishouFeifeiProductItem,
+ AdminKuaishouFeifeiProductListResult,
+ AdminKuaishouFeifeiOrderResult,
+} from './kuaishou-feifei'
+
+export type {
+ AdminCloudtentaclesSourceItem,
+ AdminCloudtentaclesSourcesConfig,
+ AdminCloudtentaclesSourceConfigResponse,
+ AdminCloudtentaclesOverrideDeliveryItem,
+ AdminCloudtentaclesOverrideRule,
+ AdminCloudtentaclesOverrideRuleConfig,
+ AdminCloudtentaclesSessionsMap,
+ AdminCloudtentaclesSourceConfig,
+ AdminCloudtentaclesPersistedSession,
+ AdminCloudtentaclesSessionItem,
+ AdminCloudtentaclesSendSmsResult,
+ AdminCloudtentaclesSessionSummary,
+ AdminCloudtentaclesLoginTestResult,
+ AdminCloudtentaclesValidateSessionResult,
+ AdminCloudtentaclesSkuItem,
+ AdminCloudtentaclesSkuListResult,
+ AdminCloudtentaclesDeliveryRecordItem,
+ AdminCloudtentaclesDeliveryRecordListResult,
+} from './cloudtentacles'
diff --git a/apps/frontend-react/src/types/admin/platform-config/kuaishou-eticket.ts b/apps/frontend-react/src/types/admin/platform-config/kuaishou-eticket.ts
new file mode 100644
index 00000000..046bca9c
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/platform-config/kuaishou-eticket.ts
@@ -0,0 +1,83 @@
+export interface AdminKuaishouEticketShopConfigItem {
+ shopId: string
+ kshopName: string
+ cookie: string
+ cookieMasked: string
+ hasCookie: boolean
+ userAvatar: string
+ enabled: boolean
+}
+
+export interface AdminKuaishouEticketSourceConfig {
+ enabled: boolean
+ baseUrl: string
+ shops: AdminKuaishouEticketShopConfigItem[]
+}
+
+export interface AdminKuaishouEticketShopInfoResult {
+ baseUrl: string
+ ok: boolean
+ result: number
+ errorMessage: string
+ requestId: string
+ shop: {
+ shopId: string
+ kshopName: string
+ userAvatar: string
+ settleStatus: number
+ hasCookie: boolean
+ }
+ raw: Record
+}
+
+export interface AdminKuaishouEticketDetailResult {
+ baseUrl: string
+ eTicketId: string
+ ok: boolean
+ alreadyConsumed: boolean
+ result: number
+ errorMessage: string
+ requestId: string
+ serverTimestamp: string
+ detail: {
+ uid: string
+ fulfillDetailId: string
+ sellerId: string
+ formToken: string
+ validEndTime: string
+ validStartTime: string
+ leftReverseCount: number
+ eTicketId: string
+ oid: string
+ totalCount: number
+ leftCount: number
+ status: string
+ } | null
+ goods: {
+ itemId: string
+ itemPicUrl: string
+ itemTitle: string
+ price: string
+ skuDesc: string
+ skuId: string
+ } | null
+ raw: Record
+}
+
+export interface AdminKuaishouEticketConsumeResult {
+ baseUrl: string
+ eTicketId: string
+ oid: string
+ formToken: string
+ num: number
+ storeId: string
+ ok: boolean
+ consumed: boolean
+ alreadyConsumed: boolean
+ result: number
+ errorMessage: string
+ requestId: string
+ serverTimestamp: string
+ detail: AdminKuaishouEticketDetailResult['detail']
+ raw: Record
+}
diff --git a/apps/frontend-react/src/types/admin/platform-config/kuaishou-feifei.ts b/apps/frontend-react/src/types/admin/platform-config/kuaishou-feifei.ts
new file mode 100644
index 00000000..ecea64ef
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/platform-config/kuaishou-feifei.ts
@@ -0,0 +1,104 @@
+export interface AdminKuaishouFeifeiProductRule {
+ id: string
+ enabled: boolean
+ productName: string
+ normalizedProductName: string
+ productCode: string
+ skuName: string
+ notes: string
+}
+
+export interface AdminKuaishouFeifeiConfig {
+ enabled: boolean
+ baseUrl: string
+ appKey: string
+ appSecret: string
+ timeoutMs: number
+ notifyUrl: string
+ productRules: AdminKuaishouFeifeiProductRule[]
+}
+
+export interface AdminKuaishouFeifeiEffectiveConfig {
+ enabled: boolean
+ baseUrl: string
+ timeoutMs: number
+ notifyUrl: string
+ hasAppKey: boolean
+ hasAppSecret: boolean
+ productRuleCount: number
+}
+
+export interface AdminKuaishouFeifeiConfigResponse {
+ filePath: string
+ source: AdminKuaishouFeifeiConfig
+ effective: AdminKuaishouFeifeiEffectiveConfig
+}
+
+export interface AdminKuaishouFeifeiProductSyncResult extends AdminKuaishouFeifeiConfigResponse {
+ sync: {
+ productCount: number
+ ruleCount: number
+ status: string
+ supplyProductName: string
+ }
+}
+
+export interface AdminKuaishouFeifeiProductMatch {
+ matchMode: string
+ productName: string
+ normalizedProductName: string
+ productCode: string
+ skuName: string
+}
+
+export interface AdminKuaishouFeifeiMatchResult {
+ productName: string
+ normalizedProductName: string
+ matched: boolean
+ match: AdminKuaishouFeifeiProductMatch | null
+}
+
+export interface AdminKuaishouFeifeiProductItem {
+ productCode: string
+ name: string
+ channel: string
+ externalProductId: string
+ imageUrl: string
+ status: string
+ supplyStatus: string
+ supplyPricePoints: number
+ feePoints: number
+ unitCostPoints: number
+ maxOrderQuantity: number
+ salePricePoints: number | null
+ raw: Record
+}
+
+export interface AdminKuaishouFeifeiProductListResult {
+ items: AdminKuaishouFeifeiProductItem[]
+ total: number
+ page: number
+ perPage: number
+ raw: Record
+}
+
+export interface AdminKuaishouFeifeiOrderResult {
+ orderNo: string
+ platformOrderNo: string
+ productCode: string
+ productName: string
+ rechargeStatus: number
+ rechargeStatusLabel: string
+ pointsCharged: number
+ playerAccount: string
+ platformBuyNum: number
+ rechargeResultMessage: string
+ createdAt: string
+ updatedAt: string
+ rechargeFinishAt: string
+ h5: {
+ entryUrl: string
+ rechargeUrl: string
+ }
+ raw: Record
+}
diff --git a/apps/frontend-react/src/types/admin/platform-config/ninetyone.ts b/apps/frontend-react/src/types/admin/platform-config/ninetyone.ts
new file mode 100644
index 00000000..19df7c49
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/platform-config/ninetyone.ts
@@ -0,0 +1,32 @@
+export interface AdminNinetyoneOrderItem {
+ orderId: number
+ orderNo: string
+ outTradeNo: string
+ orderStatus: string
+ payStatus: string
+ shopId: string
+ shopName: string
+ productNo: string
+ productName: string
+ buyNum: number
+ taskCount: number
+ failReason: string
+ createdAt: string
+ updatedAt: string
+ items: Array>
+}
+
+export interface AdminNinetyoneOrderListResult {
+ page: number
+ pageSize: number
+ total: number
+ items: AdminNinetyoneOrderItem[]
+}
+
+export interface AdminNinetyoneOrderActionResult {
+ orderId: number
+ orderNo: string
+ orderStatus: string
+ orderItemCount: number
+ taskCount: number
+}
diff --git a/apps/frontend-react/src/types/admin/platform-config/notifications.ts b/apps/frontend-react/src/types/admin/platform-config/notifications.ts
new file mode 100644
index 00000000..1a847acb
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/platform-config/notifications.ts
@@ -0,0 +1,50 @@
+export interface AdminNotificationBarkRecipient {
+ id: string
+ name: string
+ deviceKey: string
+ deviceKeyMasked: string
+ enabled: boolean
+}
+
+export interface AdminNotificationWpushRecipient {
+ id: string
+ name: string
+ apiKey: string
+ apiKeyMasked: string
+ enabled: boolean
+}
+
+export interface AdminNotificationConfig {
+ enabled: boolean
+ channels: {
+ bark: {
+ enabled: boolean
+ serverUrl: string
+ recipients: AdminNotificationBarkRecipient[]
+ }
+ wpush: {
+ enabled: boolean
+ recipients: AdminNotificationWpushRecipient[]
+ }
+ }
+}
+
+export interface AdminNotificationSendResult {
+ channel: string
+ recipientId: string
+ recipientName: string
+ recipientKeyMasked: string
+ ok: boolean
+ status: number
+ errorMessage: string
+ response: unknown
+}
+
+export interface AdminNotificationTestResult {
+ enabled: boolean
+ channel: string
+ successCount: number
+ failedCount: number
+ skippedCount: number
+ results: AdminNotificationSendResult[]
+}
diff --git a/apps/frontend-react/src/types/admin/platform-config/scheduled-jobs.ts b/apps/frontend-react/src/types/admin/platform-config/scheduled-jobs.ts
new file mode 100644
index 00000000..5a0d82b1
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/platform-config/scheduled-jobs.ts
@@ -0,0 +1,74 @@
+export interface AdminScheduledJobCloudtentaclesAccount {
+ sourceKey: string
+ label: string
+ enabled: boolean
+ assetThreshold: number
+}
+
+export interface AdminScheduledJobItem {
+ id: string
+ type: string
+ enabled: boolean
+ intervalSeconds: number
+ cooldownSeconds: number
+ config: {
+ assetThreshold: number
+ accounts: AdminScheduledJobCloudtentaclesAccount[]
+ }
+}
+
+export interface AdminScheduledJobsConfig {
+ enabled: boolean
+ jobs: AdminScheduledJobItem[]
+}
+
+export interface AdminScheduledJobRuntimeState {
+ id: string
+ enabled: boolean
+ running: boolean
+ lastRunAt: string
+ lastFinishedAt: string
+ nextRunAt: string
+ lastStatus: string
+ lastMessage: string
+ lastAsset: number | null
+ lastThreshold: number | null
+ lastAccounts: AdminScheduledJobAccountRuntime[]
+ lastAccountCount: number
+ lastCheckedCount: number
+ lastOkCount: number
+ lastLowAssetCount: number
+ lastFailedCount: number
+ lastManual: boolean
+}
+
+export interface AdminScheduledJobAccountRuntime {
+ sourceKey: string
+ label: string
+ enabled: boolean
+ hasToken: boolean
+ ok: boolean
+ skipped: boolean
+ status: string
+ message: string
+ asset: number | null
+ threshold: number
+ checkedAt: string
+}
+
+export interface AdminCloudtentaclesMonitorAccount {
+ sourceKey: string
+ label: string
+ enabled: boolean
+ username: string
+ phoneMasked: string
+ hasToken: boolean
+ loggedInAt: string
+}
+
+export interface AdminScheduledJobsResponse {
+ filePath: string
+ source: AdminScheduledJobsConfig
+ runtime: AdminScheduledJobRuntimeState[]
+ cloudtentaclesAccounts: AdminCloudtentaclesMonitorAccount[]
+}
diff --git a/apps/frontend-react/src/types/admin/tasks.ts b/apps/frontend-react/src/types/admin/tasks.ts
new file mode 100644
index 00000000..20193cb5
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/tasks.ts
@@ -0,0 +1,223 @@
+export interface AdminTaskListItem {
+ taskId: number
+ taskNo: string
+ platformOrderId: string
+ skuCode: string
+ skuName: string
+ status: string
+ executorKey: string
+ deliveryStatus: string
+ resultCode: string
+ resultMessage: string
+ resourceStatus: string
+ customerStatus: string
+ loginType: string
+ roleName: string
+ roleId: string
+ runtimeSessionId: string
+ claimedAt: string | null
+ roleConfirmedAt: string | null
+ redeemedAt: string | null
+ retryCount: number
+ lastError: string
+ createdAt: string
+ updatedAt: string
+ claimToken: string
+ screenshotPath: string
+}
+
+export interface AdminTaskOperations {
+ canRetry: boolean
+ canRegenerateClaimLink: boolean
+ canClose: boolean
+ canMarkManualReview: boolean
+ canCompleteManualDispatch: boolean
+ canPrepareKuaishouCloudFulfillment: boolean
+ canRefreshKuaishouCloudRoleInfo: boolean
+ canRebindKuaishouCloudRole: boolean
+ canDispatchKuaishouCloudFulfillment: boolean
+ canReturnKuaishouCloudFulfillment: boolean
+ canViewSensitiveTaskData: boolean
+}
+
+export interface AdminTaskActionResponse {
+ task: {
+ taskId: number
+ taskNo: string
+ status: string
+ deliveryStatus: string
+ resultCode: string
+ resultMessage: string
+ primaryClaimTokenId: number | null
+ lastError: string
+ updatedAt: string
+ }
+ claimUrl?: string
+ token?: string
+}
+
+export interface AdminTaskDetail {
+ task: AdminTaskListItem
+ order: null | {
+ orderId: number
+ provider: string
+ platform: string
+ shopId: string
+ shopName: string
+ platformOrderId: string
+ payStatus: string
+ orderStatus: string
+ }
+ orderItem: null | {
+ orderItemId: number
+ skuCode: string
+ skuName: string
+ quantity: number
+ }
+ claimToken: null | {
+ primaryClaimTokenId: number
+ token: string
+ status: string
+ expiredAt: string
+ claimUrl: string
+ }
+ artifacts: Record
+ screenshotUrl: string
+ review: {
+ required: boolean
+ screenshotCapturedAt: string | null
+ roleId: string
+ roleName: string
+ }
+ redeemResolution: null | {
+ status: string
+ taskStatus: string
+ replacementCount: number
+ finishedAt: string | null
+ attempts: Array<{
+ attempt: number
+ codeMasked: string
+ credentialType: string
+ outcome: string
+ resultCode: string
+ resultMessage: string
+ }>
+ }
+ kuaishouCloudFulfillment: null | {
+ flowType: string
+ configId: string
+ internalSkuCode: string
+ internalSkuName: string
+ ticket: {
+ code: string
+ capturedAt: string | null
+ status: string
+ verifiedAt: string | null
+ goodsTitle: string
+ leftCount: number
+ }
+ binding: {
+ prepareStatus: string
+ cloudSourceKeys: string[]
+ cloudSourceLabels: string[]
+ resolvedSourceKey: string
+ resolvedSourceLabel: string
+ skuId: number
+ skuName: string
+ vnKey: string
+ vnId: number
+ vnPhone: string
+ bindUrl: string
+ bindPreparedAt: string | null
+ bindExpiresAt: string | null
+ bindProbeAt: string | null
+ bindProbeStatus: string
+ bindProbeMessage: string
+ }
+ role: {
+ status: string
+ name: string
+ rid: string
+ refreshedAt: string | null
+ errorMessage: string
+ rawInfo: Record | null
+ }
+ purchase: {
+ autoBuyEnabled: boolean
+ minAssetReserve: number
+ usedKnapsack: boolean
+ purchaseTriggered: boolean
+ assetBefore: number
+ assetAfter: number
+ purchaseAt: string | null
+ }
+ dispatch: {
+ status: string
+ dispatchAt: string | null
+ sendType: number
+ note: string
+ }
+ returnNumber: {
+ status: string
+ returnedAt: string | null
+ autoReturnEnabled: boolean
+ }
+ consume: {
+ status: string
+ shopId: string
+ shopName: string
+ autoConsumeEnabled: boolean
+ consumedAt: string | null
+ errorMessage: string
+ }
+ rebind: {
+ currentAttempt: number
+ history: Array<{
+ attempt: number
+ source: string
+ requestedAt: string | null
+ requestedBy: Record | null
+ status: string
+ errorMessage: string
+ oldBinding: {
+ vnKey: string
+ vnId: number
+ vnPhone: string
+ bindUrl: string
+ bindPreparedAt: string | null
+ bindExpiresAt: string | null
+ roleName: string
+ roleId: string
+ }
+ newBinding: null | {
+ vnKey: string
+ vnId: number
+ vnPhone: string
+ bindUrl: string
+ bindPreparedAt: string | null
+ bindExpiresAt: string | null
+ }
+ }>
+ }
+ notes: string
+ }
+ manualDispatch: null | {
+ outcome: string
+ deliveryReference: string
+ deliveredCredential: string
+ resultMessage: string
+ completedAt: string | null
+ completedBy: null | {
+ userId: number
+ username: string
+ role: string
+ }
+ }
+ events: Array<{
+ eventId: number
+ eventType: string
+ payload: Record
+ createdAt: string
+ }>
+ operations: AdminTaskOperations
+}
diff --git a/apps/frontend-react/src/types/admin/users.ts b/apps/frontend-react/src/types/admin/users.ts
new file mode 100644
index 00000000..7c5438a2
--- /dev/null
+++ b/apps/frontend-react/src/types/admin/users.ts
@@ -0,0 +1,10 @@
+import type { AdminRole, AdminUserStatus } from './auth'
+
+export interface AdminUserListItem {
+ userId: number
+ username: string
+ role: AdminRole
+ status: AdminUserStatus
+ createdAt: string
+ updatedAt: string
+}
diff --git a/apps/frontend-react/src/types/claim.ts b/apps/frontend-react/src/types/claim.ts
new file mode 100644
index 00000000..d1370129
--- /dev/null
+++ b/apps/frontend-react/src/types/claim.ts
@@ -0,0 +1,168 @@
+import type { KnownTaskStatus } from '@/domain/task-status'
+
+export type ClaimTokenStatus = 'active' | 'used' | 'expired' | 'revoked' | (string & {})
+
+export type ClaimTaskStatus = KnownTaskStatus | (string & {})
+
+export interface ClaimTaskInfo {
+ taskId: number
+ taskNo: string
+ status: ClaimTaskStatus
+ executorKey: string
+ requiresSupportReview: boolean
+ expiresAt: string | null
+ claimedAt: string | null
+ roleConfirmedAt: string | null
+ redeemedAt: string | null
+ loginType: string
+ lastError: string
+ runtimeSessionId: string
+}
+
+export interface ClaimOrderInfo {
+ orderId: number
+ platform: string
+ platformOrderId: string
+ payStatus: string
+ orderStatus: string
+ totalAmount: string
+ totalAmountFen: number
+ currency: string
+}
+
+export interface ClaimOrderItemInfo {
+ orderItemId: number
+ skuCode: string
+ skuName: string
+ quantity: number
+}
+
+export interface ClaimProductInfo {
+ title: string
+ skuCode: string
+ quantity: number
+ isBundle: boolean
+ items: Array<{
+ cloudSkuId: number
+ name: string
+ quantity: number
+ }>
+}
+
+export interface ClaimResultInfo {
+ resultCode: string
+ resultMessage: string
+ screenshotReady: boolean
+ screenshotUrl: string
+}
+
+export interface ClaimKuaishouCloudFlowInfo {
+ flowType: 'kuaishou_cloud'
+ shopId: string
+ shopName: string
+ guideImages: string[]
+ ticket: {
+ code: string
+ status: string
+ capturedAt: string | null
+ verifiedAt: string | null
+ goodsTitle: string
+ leftCount: number
+ }
+ binding: {
+ prepareStatus: string
+ cloudSourceKeys: string[]
+ resolvedSourceKey: string
+ skuId: number
+ skuName: string
+ vnKey: string
+ vnId: number
+ bindUrl: string
+ bindPreparedAt: string | null
+ bindExpiresAt: string | null
+ bindProbeAt: string | null
+ bindProbeStatus: string
+ bindProbeMessage: string
+ vnPhone: string
+ roleName: string
+ roleId: string
+ }
+ role: {
+ status: string
+ name: string
+ rid: string
+ refreshedAt: string | null
+ errorMessage: string
+ defaultName: string
+ defaultRid: string
+ defaultCapturedAt: string | null
+ defaultCaptureStatus: string
+ defaultErrorMessage: string
+ isDefaultRole: boolean
+ }
+ purchase: {
+ autoBuyEnabled: boolean
+ minAssetReserve: number
+ usedKnapsack: boolean
+ purchaseTriggered: boolean
+ assetBefore: number
+ assetAfter: number
+ purchaseAt: string | null
+ }
+ dispatch: {
+ status: string
+ dispatchAt: string | null
+ failedAt: string | null
+ failedStage: string
+ errorCode: string
+ errorMessage: string
+ note: string
+ }
+ returnNumber: {
+ status: string
+ returnedAt: string | null
+ }
+ consume: {
+ status: string
+ shopId: string
+ shopName: string
+ consumedAt: string | null
+ errorMessage: string
+ }
+ rebind?: {
+ currentAttempt: number
+ history: Array>
+ }
+}
+
+export interface ClaimKuaishouFeifeiFlowInfo {
+ flowType: 'kuaishou_feifei'
+ productCode: string
+ productName: string
+ platformOrderNo: string
+ orderNo: string
+ rechargeStatus: number
+ rechargeStatusLabel: string
+ rechargeResultMessage: string
+ claimUrl: string
+ consumeStatus: string
+ h5: {
+ entryUrl: string
+ rechargeUrl: string
+ }
+ lastSyncedAt: string | null
+}
+
+export interface ClaimDetailData {
+ tokenStatus: ClaimTokenStatus
+ claimUrl: string
+ flowType: 'kuaishou_cloud' | 'kuaishou_ct_assisted' | 'kuaishou_feifei' | (string & {})
+ task: ClaimTaskInfo
+ order: ClaimOrderInfo
+ orderItem: ClaimOrderItemInfo
+ product: ClaimProductInfo
+ session: unknown | null
+ kuaishouCloudFulfillment: ClaimKuaishouCloudFlowInfo | null
+ kuaishouFeifei: ClaimKuaishouFeifeiFlowInfo | null
+ result: ClaimResultInfo | null
+}
diff --git a/apps/frontend-react/src/utils/admin-auth.ts b/apps/frontend-react/src/utils/admin-auth.ts
new file mode 100644
index 00000000..cbb374e6
--- /dev/null
+++ b/apps/frontend-react/src/utils/admin-auth.ts
@@ -0,0 +1,99 @@
+const ADMIN_TOKEN_KEY = 'order-site-admin-token'
+const ADMIN_EXPIRES_AT_KEY = 'order-site-admin-token-expires-at'
+const ADMIN_USER_ID_KEY = 'order-site-admin-user-id'
+const ADMIN_USERNAME_KEY = 'order-site-admin-username'
+const ADMIN_ROLE_KEY = 'order-site-admin-role'
+
+const ADMIN_ROLE_LEVEL: Record<'support' | 'operator' | 'admin', number> = {
+ support: 1,
+ operator: 2,
+ admin: 3,
+}
+
+export function getAdminToken() {
+ return localStorage.getItem(ADMIN_TOKEN_KEY) || ''
+}
+
+export function getAdminTokenExpiresAt() {
+ return localStorage.getItem(ADMIN_EXPIRES_AT_KEY) || ''
+}
+
+export function getAdminUserId() {
+ const value = Number(localStorage.getItem(ADMIN_USER_ID_KEY) || 0)
+ return Number.isFinite(value) && value > 0 ? value : 0
+}
+
+export function getAdminUsername() {
+ return localStorage.getItem(ADMIN_USERNAME_KEY) || ''
+}
+
+export function getAdminRole() {
+ const role = localStorage.getItem(ADMIN_ROLE_KEY)
+ if (role === 'admin' || role === 'operator' || role === 'support') {
+ return role
+ }
+
+ return 'support'
+}
+
+export function hasAdminRole(role: 'admin' | 'operator' | 'support') {
+ if (!hasAdminSession()) {
+ return false
+ }
+
+ return ADMIN_ROLE_LEVEL[getAdminRole()] >= ADMIN_ROLE_LEVEL[role]
+}
+
+export function setAdminSession(
+ token: string,
+ expiresAt: string,
+ user?: { userId: number; username: string; role: 'admin' | 'operator' | 'support' },
+) {
+ localStorage.setItem(ADMIN_TOKEN_KEY, token)
+ localStorage.setItem(ADMIN_EXPIRES_AT_KEY, expiresAt)
+
+ if (user?.userId) {
+ localStorage.setItem(ADMIN_USER_ID_KEY, String(user.userId))
+ }
+
+ if (user?.username) {
+ localStorage.setItem(ADMIN_USERNAME_KEY, user.username)
+ }
+
+ if (user?.role) {
+ localStorage.setItem(ADMIN_ROLE_KEY, user.role)
+ }
+}
+
+export function clearAdminSession() {
+ localStorage.removeItem(ADMIN_TOKEN_KEY)
+ localStorage.removeItem(ADMIN_EXPIRES_AT_KEY)
+ localStorage.removeItem(ADMIN_USER_ID_KEY)
+ localStorage.removeItem(ADMIN_USERNAME_KEY)
+ localStorage.removeItem(ADMIN_ROLE_KEY)
+}
+
+export function hasAdminSession() {
+ const token = getAdminToken()
+ const expiresAt = getAdminTokenExpiresAt()
+
+ if (!token) {
+ return false
+ }
+
+ if (isExpired(expiresAt)) {
+ clearAdminSession()
+ return false
+ }
+
+ return true
+}
+
+function isExpired(expiresAt: string) {
+ if (!expiresAt) {
+ return false
+ }
+
+ const expiresAtMs = Date.parse(expiresAt)
+ return Number.isFinite(expiresAtMs) && expiresAtMs <= Date.now()
+}
diff --git a/apps/frontend-react/src/utils/admin-display.ts b/apps/frontend-react/src/utils/admin-display.ts
new file mode 100644
index 00000000..c579d923
--- /dev/null
+++ b/apps/frontend-react/src/utils/admin-display.ts
@@ -0,0 +1,135 @@
+export function formatStatusWithRaw(status: string, labelMap: Record = {}) {
+ const normalized = String(status || '').trim()
+
+ if (!normalized) {
+ return '-'
+ }
+
+ const key = normalized.toLowerCase()
+ const label = labelMap[key]
+
+ if (!label || label === normalized) {
+ return normalized
+ }
+
+ return `${label} (${normalized})`
+}
+
+export function formatAuditAction(action: string) {
+ const labelMap: Record = {
+ admin_user_created: '创建后台用户',
+ admin_user_role_updated: '修改用户角色',
+ admin_user_status_updated: '修改用户状态',
+ admin_user_password_reset: '重置用户密码',
+ task_regenerate_claim_link: '重发领取链接',
+ task_kuaishou_cloud_rebind_role: '换绑角色',
+ task_closed: '关闭任务',
+ task_mark_manual_review: '转人工处理',
+ task_complete_manual_dispatch: '完成人工履约',
+ platform_shop_config_updated: '更新店铺配置',
+ platform_fulfillment_config_updated: '更新履约配置',
+ }
+
+ return formatStatusWithRaw(action, labelMap)
+}
+
+export function formatAuditTargetType(targetType: string) {
+ const labelMap: Record = {
+ admin_user: '后台用户',
+ task: '交付任务',
+ platform_config: '平台配置',
+ }
+
+ return formatStatusWithRaw(targetType, labelMap)
+}
+
+export function formatTaskEventType(eventType: string) {
+ const labelMap: Record = {
+ manual_dispatch_completed: '人工履约已回写',
+ claim_redeem_code_used: '兑换码已使用',
+ claim_redeem_code_invalid: '兑换码错误',
+ claim_redeem_completed: '兑换链路完成',
+ kuaishou_cloud_rebind_requested: '发起角色换绑',
+ kuaishou_cloud_rebind_old_number_returned: '换绑旧号已退还',
+ kuaishou_cloud_rebind_prepared: '换绑资源已准备',
+ kuaishou_cloud_rebind_failed: '换绑失败',
+ }
+
+ return formatStatusWithRaw(eventType, labelMap)
+}
+
+export function formatRedeemOutcomeLabel(value: string) {
+ const normalized = String(value || '').trim()
+
+ switch (normalized) {
+ case 'success':
+ return '兑换成功'
+ case 'code_used':
+ return '兑换码已使用'
+ case 'code_invalid':
+ return '兑换码错误'
+ case 'captcha_rejected':
+ return '验证码错误'
+ case 'failed':
+ return '兑换失败'
+ default:
+ return normalized || '-'
+ }
+}
+
+export function formatRedeemResolutionStatus(value: string) {
+ return value === 'success' ? '已完成' : value === 'failed' ? '失败收口' : value || '-'
+}
+
+export function formatKuaishouRoleInfoLabel(value: string) {
+ const normalized = String(value || '').trim()
+
+ switch (normalized) {
+ case 'name':
+ return '角色名称'
+ case 'rid':
+ return '角色 ID'
+ default:
+ return normalized || '-'
+ }
+}
+
+export function formatTaskEventPayload(payload: Record) {
+ const parts = [
+ payload.outcome ? `结果 ${payload.outcome}` : '',
+ payload.resultCode ? `代码 ${payload.resultCode}` : '',
+ payload.resultMessage ? `说明 ${payload.resultMessage}` : '',
+ payload.codeMasked ? `凭据 ${payload.codeMasked}` : '',
+ payload.previousCodeMasked ? `旧码 ${payload.previousCodeMasked}` : '',
+ payload.nextCodeMasked ? `新码 ${payload.nextCodeMasked}` : '',
+ payload.ticketCodeMasked ? `卡券号 ${payload.ticketCodeMasked}` : '',
+ payload.vnId ? `虚拟号 #${payload.vnId}` : '',
+ payload.oldVnId ? `旧虚拟号 #${payload.oldVnId}` : '',
+ payload.vnPhoneMasked ? `号码 ${payload.vnPhoneMasked}` : '',
+ payload.oldVnPhoneMasked ? `旧号码 ${payload.oldVnPhoneMasked}` : '',
+ payload.oldRoleName ? `旧角色 ${payload.oldRoleName}` : '',
+ payload.oldRoleId ? `旧角色ID ${payload.oldRoleId}` : '',
+ payload.attempt ? `第 ${payload.attempt} 次` : '',
+ payload.bindUrl ? `绑定链接 ${payload.bindUrl}` : '',
+ payload.sendType ? `发送类型 ${payload.sendType}` : '',
+ payload.note ? `备注 ${payload.note}` : '',
+ payload.previousOutcome
+ ? `切换原因 ${formatRedeemOutcomeLabel(String(payload.previousOutcome || ''))}`
+ : '',
+ payload.deliveryReference ? `单号 ${payload.deliveryReference}` : '',
+ payload.platformOrderId ? `平台单 ${payload.platformOrderId}` : '',
+ payload.trigger ? `触发 ${payload.trigger}` : '',
+ payload.reason ? `原因 ${payload.reason}` : '',
+ payload.purchaseTriggered === true ? '已触发购买' : '',
+ payload.usedKnapsack === true ? '使用背包库存' : '',
+ payload.responseStatus ? `HTTP ${payload.responseStatus}` : '',
+ payload.requestId ? `请求 ${payload.requestId}` : '',
+ payload.errorMessage ? `错误 ${payload.errorMessage}` : '',
+ ].filter(Boolean)
+
+ if (parts.length > 0) {
+ return parts.join(' · ')
+ }
+
+ return JSON.stringify(payload || {})
+}
diff --git a/apps/frontend-react/src/utils/admin-options.ts b/apps/frontend-react/src/utils/admin-options.ts
new file mode 100644
index 00000000..5abb1c10
--- /dev/null
+++ b/apps/frontend-react/src/utils/admin-options.ts
@@ -0,0 +1,52 @@
+export const adminPayStatusOptions = [
+ { label: '全部', value: '' },
+ { label: '未支付', value: 'unpaid' },
+ { label: '已支付', value: 'paid' },
+ { label: '失败', value: 'failed' },
+ { label: '已退款', value: 'refunded' },
+]
+
+export const adminTaskStatusOptions = [
+ { label: '全部', value: '' },
+ { label: '待支付', value: 'pending_payment' },
+ { label: '已支付', value: 'paid' },
+ { label: '已生成链接', value: 'link_generated' },
+ { label: '已创建会话', value: 'claimed' },
+ { label: '已确认角色', value: 'role_confirmed' },
+ { label: '兑换中', value: 'redeeming' },
+ { label: '已兑换', value: 'redeemed' },
+ { label: '待重试', value: 'retry_pending' },
+ { label: '人工处理', value: 'manual_review' },
+ { label: '已过期', value: 'expired' },
+ { label: '已关闭', value: 'closed' },
+]
+
+export const adminUserRoleOptions = [
+ { label: '全部', value: '' },
+ { label: '管理员', value: 'admin' },
+ { label: '普通运营', value: 'operator' },
+ { label: '客服', value: 'support' },
+]
+
+export const adminUserStatusOptions = [
+ { label: '全部', value: '' },
+ { label: '生效中', value: 'active' },
+ { label: '已停用', value: 'disabled' },
+]
+
+export const adminAuditActionOptions = [
+ { label: '全部', value: '' },
+ { label: '创建后台用户', value: 'admin_user_created' },
+ { label: '修改用户角色', value: 'admin_user_role_updated' },
+ { label: '修改用户状态', value: 'admin_user_status_updated' },
+ { label: '重置用户密码', value: 'admin_user_password_reset' },
+ { label: '换绑角色', value: 'task_kuaishou_cloud_rebind_role' },
+ { label: '关闭任务', value: 'task_closed' },
+ { label: '转人工处理', value: 'task_mark_manual_review' },
+]
+
+export const adminAuditTargetTypeOptions = [
+ { label: '全部', value: '' },
+ { label: '后台用户', value: 'admin_user' },
+ { label: '交付任务', value: 'task' },
+]
diff --git a/apps/frontend-react/src/utils/admin-time.ts b/apps/frontend-react/src/utils/admin-time.ts
new file mode 100644
index 00000000..b3696153
--- /dev/null
+++ b/apps/frontend-react/src/utils/admin-time.ts
@@ -0,0 +1 @@
+export { formatDateTime as formatAdminDateTime } from '@/utils/date-time'
diff --git a/apps/frontend-react/src/utils/date-time.ts b/apps/frontend-react/src/utils/date-time.ts
new file mode 100644
index 00000000..1567b29f
--- /dev/null
+++ b/apps/frontend-react/src/utils/date-time.ts
@@ -0,0 +1,54 @@
+const ISO_DATE_TIME_PATTERN =
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/
+
+export function isIsoDateTimeString(value: unknown): value is string {
+ return typeof value === 'string' && ISO_DATE_TIME_PATTERN.test(value.trim())
+}
+
+export function formatDateTime(value: string | null | undefined, fallback = '-') {
+ const normalized = String(value || '').trim()
+
+ if (!normalized) {
+ return fallback
+ }
+
+ const date = new Date(normalized)
+
+ if (Number.isNaN(date.getTime())) {
+ return normalized.replace('T', ' ').replace('Z', '')
+ }
+
+ const year = date.getFullYear()
+ const month = String(date.getMonth() + 1).padStart(2, '0')
+ const day = String(date.getDate()).padStart(2, '0')
+ const hours = String(date.getHours()).padStart(2, '0')
+ const minutes = String(date.getMinutes()).padStart(2, '0')
+ const seconds = String(date.getSeconds()).padStart(2, '0')
+
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+}
+
+export function normalizeDateTimeValue(value: unknown): unknown {
+ if (Array.isArray(value)) {
+ return value.map((item) => normalizeDateTimeValue(item))
+ }
+
+ if (value && typeof value === 'object') {
+ return Object.fromEntries(
+ Object.entries(value).map(([key, currentValue]) => [
+ key,
+ normalizeDateTimeValue(currentValue),
+ ]),
+ )
+ }
+
+ if (isIsoDateTimeString(value)) {
+ return formatDateTime(value, '')
+ }
+
+ return value
+}
+
+export function stringifyDisplayJson(value: unknown, space = 2) {
+ return JSON.stringify(normalizeDateTimeValue(value), null, space)
+}
diff --git a/apps/frontend-react/src/vite-env.d.ts b/apps/frontend-react/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/apps/frontend-react/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/apps/frontend-react/tsconfig.app.json b/apps/frontend-react/tsconfig.app.json
new file mode 100644
index 00000000..8d75ae3c
--- /dev/null
+++ b/apps/frontend-react/tsconfig.app.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "ignoreDeprecations": "6.0",
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ }
+ },
+ "include": ["src"]
+}
diff --git a/apps/frontend-react/tsconfig.json b/apps/frontend-react/tsconfig.json
new file mode 100644
index 00000000..1ffef600
--- /dev/null
+++ b/apps/frontend-react/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/apps/frontend-react/tsconfig.node.json b/apps/frontend-react/tsconfig.node.json
new file mode 100644
index 00000000..672c8363
--- /dev/null
+++ b/apps/frontend-react/tsconfig.node.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "noEmit": true,
+ "types": ["node"]
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/apps/frontend-react/vite.config.ts b/apps/frontend-react/vite.config.ts
new file mode 100644
index 00000000..eae32342
--- /dev/null
+++ b/apps/frontend-react/vite.config.ts
@@ -0,0 +1,31 @@
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+import react from '@vitejs/plugin-react'
+import { defineConfig } from 'vite'
+
+const currentDir = path.dirname(fileURLToPath(import.meta.url))
+const apiTarget = process.env.VITE_API_TARGET || 'http://127.0.0.1'
+
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ '@': path.resolve(currentDir, 'src'),
+ },
+ },
+ server: {
+ host: '0.0.0.0',
+ port: 5174,
+ proxy: {
+ '/api': {
+ target: apiTarget,
+ changeOrigin: true,
+ },
+ '/health': {
+ target: apiTarget,
+ changeOrigin: true,
+ },
+ },
+ },
+})