diff --git a/bun.lock b/bun.lock index bd690c2..5e1ccdb 100644 --- a/bun.lock +++ b/bun.lock @@ -1,16 +1,16 @@ { "lockfileVersion": 1, - "configVersion": 0, + "configVersion": 1, "workspaces": { "": { "name": "pushy-admin", "dependencies": { "@ant-design/charts": "^2.6.7", "@ant-design/icons": "^6.1.1", - "@rsbuild/core": "^1.7.5", - "@rsbuild/plugin-react": "^1.4.6", - "@rsbuild/plugin-svgr": "^1.3.1", - "@tanstack/react-query": "^5.99.2", + "@rsbuild/core": "^2.0.1", + "@rsbuild/plugin-react": "^2.0.0", + "@rsbuild/plugin-svgr": "^2.0.1", + "@tanstack/react-query": "^5.100.5", "antd": "^6.3.6", "dayjs": "^1.11.20", "git-url-parse": "^16.1.0", @@ -19,32 +19,29 @@ "json-diff-kit": "^1.0.35", "react": "^19.2.5", "react-dom": "^19.2.5", - "react-router-dom": "^7.14.1", + "react-router-dom": "^7.14.2", "ua-parser-js": "^2.0.9", "vanilla-jsoneditor": "^3.12.0", "xlsx": "^0.18.5", }, "devDependencies": { - "@biomejs/biome": "2.4.12", - "@tailwindcss/postcss": "^4.2.3", + "@biomejs/biome": "2.4.13", + "@tailwindcss/postcss": "^4.2.4", "@testing-library/react": "^16.3.2", - "@types/bun": "^1.3.12", + "@types/bun": "^1.3.13", "@types/git-url-parse": "^16.0.2", "@types/node": "^25.6.0", "@types/react": "^19", "@types/react-dom": "^19", "@types/react-router-dom": "^5.3.3", - "@typescript/native-preview": "^7.0.0-dev.20260420.1", + "@typescript/native-preview": "^7.0.0-dev.20260426.1", "happy-dom": "^20.9.0", "mitata": "^1.0.34", - "tailwindcss": "^4.2.3", + "tailwindcss": "^4.2.4", "typescript": "^6.0.3", }, }, }, - "trustedDependencies": [ - "core-js", - ], "packages": { "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -148,23 +145,23 @@ "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], - "@biomejs/biome": ["@biomejs/biome@2.4.12", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.12", "@biomejs/cli-darwin-x64": "2.4.12", "@biomejs/cli-linux-arm64": "2.4.12", "@biomejs/cli-linux-arm64-musl": "2.4.12", "@biomejs/cli-linux-x64": "2.4.12", "@biomejs/cli-linux-x64-musl": "2.4.12", "@biomejs/cli-win32-arm64": "2.4.12", "@biomejs/cli-win32-x64": "2.4.12" }, "bin": { "biome": "bin/biome" } }, "sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA=="], + "@biomejs/biome": ["@biomejs/biome@2.4.13", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.13", "@biomejs/cli-darwin-x64": "2.4.13", "@biomejs/cli-linux-arm64": "2.4.13", "@biomejs/cli-linux-arm64-musl": "2.4.13", "@biomejs/cli-linux-x64": "2.4.13", "@biomejs/cli-linux-x64-musl": "2.4.13", "@biomejs/cli-win32-arm64": "2.4.13", "@biomejs/cli-win32-x64": "2.4.13" }, "bin": { "biome": "bin/biome" } }, "sha512-gLXOwkOBBg0tr7bDsqlkIh4uFeKuMjxvqsrb1Tukww1iDmHcfr4Uu8MoQxp0Rcte+69+osRNWXwHsu/zxT6XqA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2KImO1jhNFBa2oWConyr0x6flxbQpGKv6902uGXpYM62Xyem8U80j441SyUJ8KyngsmKbQjeIv1q2CQfDkNnYg=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-BKrJklbaFN4p1Ts4kPBczo+PkbsHQg57kmJ+vON9u2t6uN5okYHaSr7h/MutPCWQgg2lglaWoSmm+zhYW+oOkg=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-NzkUDSqfvMBrPplKgVr3aXLHZ2NEELvvF4vZxXulEylKWIGqlvNEcwUcj9OLrn75TD3lJ/GIqCVlBwd1MZCuYQ=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-U5MsuBQW25dXaYtqWWSPM3P96H6Y+fHuja3TQpMNnylocHW0tEbtFTDlUj6oM+YJLntvEkQy4grBvQNUD4+RCg=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.12", "", { "os": "linux", "cpu": "x64" }, "sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.13", "", { "os": "linux", "cpu": "x64" }, "sha512-Az3ZZedYRBo9EQzNnD9SxFcR1G5QsGo6VEc2hIyVPZ1rdKwee/7E9oeBBZFpE8Z44ekxsDQBqbiWGW5ShOhUSQ=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.12", "", { "os": "linux", "cpu": "x64" }, "sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.13", "", { "os": "linux", "cpu": "x64" }, "sha512-Z601MienRgTBDza/+u2CH3RSrWoXo9rtr8NK6A4KJzqGgfxx+H3VlyLgTJ4sRo40T3pIsqpTmiOQEvYzQvBRvQ=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-Px9PS2B5/Q183bUwy/5VHqp3J2lzdOCeVGzMpphYfl8oSa7VDCqenBdqWpy6DCy/en4Rbf/Y1RieZF6dJPcc9A=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.12", "", { "os": "win32", "cpu": "x64" }, "sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.13", "", { "os": "win32", "cpu": "x64" }, "sha512-tTcMkXyBrmHi9BfrD2VNHs/5rYIUKETqsBlYOvSAABwBkJhSDVb5e7wPukftsQbO3WzQkXe6kaztC6WtUOXSoQ=="], "@codemirror/autocomplete": ["@codemirror/autocomplete@6.20.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A=="], @@ -176,19 +173,19 @@ "@codemirror/lint": ["@codemirror/lint@6.9.5", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.35.0", "crelt": "^1.0.5" } }, "sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA=="], - "@codemirror/search": ["@codemirror/search@6.6.0", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.37.0", "crelt": "^1.0.5" } }, "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw=="], + "@codemirror/search": ["@codemirror/search@6.7.0", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.37.0", "crelt": "^1.0.5" } }, "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg=="], "@codemirror/state": ["@codemirror/state@6.6.0", "", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ=="], - "@codemirror/view": ["@codemirror/view@6.41.0", "", { "dependencies": { "@codemirror/state": "^6.6.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-6H/qadXsVuDY219Yljhohglve8xf4B8xJkVOEWfA5uiYKiTFppjqsvsfR5iPA0RbvRBoOyTZpbLIxe9+0UR8xA=="], + "@codemirror/view": ["@codemirror/view@6.41.1", "", { "dependencies": { "@codemirror/state": "^6.6.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-ToDnWKbBnke+ZLrP6vgTTDScGi5H37YYuZGniQaBzxMVdtCxMrslsmtnOvbPZk4RX9bvkQqnWR/WS/35tJA0qg=="], "@cronvel/get-pixels": ["@cronvel/get-pixels@3.4.1", "", { "dependencies": { "jpeg-js": "^0.4.4", "ndarray": "^1.0.19", "ndarray-pack": "^1.1.1", "node-bitmap": "0.0.1", "omggif": "^1.0.10", "pngjs": "^6.0.0" } }, "sha512-gB5C5nDIacLUdsMuW8YsM9SzK3vaFANe4J11CVXpovpy7bZUGrcJKmc6m/0gWG789pKr6XSZY2aEetjFvSRw5g=="], - "@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], - "@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="], + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], "@emotion/hash": ["@emotion/hash@0.8.0", "", {}, "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="], @@ -220,29 +217,17 @@ "@jsonquerylang/jsonquery": ["@jsonquerylang/jsonquery@5.1.1", "", { "bin": { "jsonquery": "bin/cli.js" } }, "sha512-Fj4SoA6Ku09EF+t7OEI8QLipA2A+fJCdEOwnDWG84o5jXMRjkcN5NCMH7kFZb5fP62xz914XV5LBOiDdiUXObg=="], - "@lezer/common": ["@lezer/common@1.5.1", "", {}, "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw=="], + "@lezer/common": ["@lezer/common@1.5.2", "", {}, "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ=="], "@lezer/highlight": ["@lezer/highlight@1.2.3", "", { "dependencies": { "@lezer/common": "^1.3.0" } }, "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g=="], "@lezer/json": ["@lezer/json@1.0.3", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ=="], - "@lezer/lr": ["@lezer/lr@1.4.8", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA=="], + "@lezer/lr": ["@lezer/lr@1.4.10", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A=="], "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="], - "@module-federation/error-codes": ["@module-federation/error-codes@0.22.0", "", {}, "sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug=="], - - "@module-federation/runtime": ["@module-federation/runtime@0.22.0", "", { "dependencies": { "@module-federation/error-codes": "0.22.0", "@module-federation/runtime-core": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA=="], - - "@module-federation/runtime-core": ["@module-federation/runtime-core@0.22.0", "", { "dependencies": { "@module-federation/error-codes": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA=="], - - "@module-federation/runtime-tools": ["@module-federation/runtime-tools@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/webpack-bundler-runtime": "0.22.0" } }, "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA=="], - - "@module-federation/sdk": ["@module-federation/sdk@0.22.0", "", {}, "sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g=="], - - "@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA=="], - - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], "@rc-component/async-validator": ["@rc-component/async-validator@5.1.0", "", { "dependencies": { "@babel/runtime": "^7.24.4" } }, "sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA=="], @@ -262,7 +247,7 @@ "@rc-component/dropdown": ["@rc-component/dropdown@1.0.2", "", { "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" } }, "sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg=="], - "@rc-component/form": ["@rc-component/form@1.8.0", "", { "dependencies": { "@rc-component/async-validator": "^5.1.0", "@rc-component/util": "^1.6.2", "clsx": "^2.1.1" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-eUD5KKYnIZWmJwRA0vnyO/ovYUfHGU1svydY1OrqU5fw8Oz9Tdqvxvrlh0wl6xI/EW69dT7II49xpgOWzK3T5A=="], + "@rc-component/form": ["@rc-component/form@1.8.1", "", { "dependencies": { "@rc-component/async-validator": "^5.1.0", "@rc-component/util": "^1.6.2", "clsx": "^2.1.1" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-8O7TB55Fi2mWIGvSnwZjk8jFqVNYyKDAswglwGShcbndxqzKz4cHwNtNaLjZlAeRge9wcB0LL8IWsC/Bl18raQ=="], "@rc-component/image": ["@rc-component/image@1.9.0", "", { "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" } }, "sha512-khF7w7xkBH5B1bsBcI1FSUZdkyd1aqpl2eYyILCqCzzQH3XdfehGUaZTnptyaJJfs09/R5hv9jXWyazOMFIClQ=="], @@ -282,7 +267,7 @@ "@rc-component/notification": ["@rc-component/notification@1.2.0", "", { "dependencies": { "@rc-component/motion": "^1.1.4", "@rc-component/util": "^1.2.1", "clsx": "^2.1.1" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA=="], - "@rc-component/overflow": ["@rc-component/overflow@1.0.0", "", { "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" } }, "sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw=="], + "@rc-component/overflow": ["@rc-component/overflow@1.0.1", "", { "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" } }, "sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA=="], "@rc-component/pagination": ["@rc-component/pagination@1.2.0", "", { "dependencies": { "@rc-component/util": "^1.3.0", "clsx": "^2.1.1" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw=="], @@ -326,45 +311,43 @@ "@rc-component/upload": ["@rc-component/upload@1.1.0", "", { "dependencies": { "@rc-component/util": "^1.3.0", "clsx": "^2.1.1" }, "peerDependencies": { "react": ">=16.9.0", "react-dom": ">=16.9.0" } }, "sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw=="], - "@rc-component/util": ["@rc-component/util@1.10.0", "", { "dependencies": { "is-mobile": "^5.0.0", "react-is": "^18.2.0" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-aY9GLBuiUdpyfIUpAWSYer4Tu3mVaZCo5A0q9NtXcazT3MRiI3/WNHCR+DUn5VAtR6iRRf0ynCqQUcHli5UdYw=="], + "@rc-component/util": ["@rc-component/util@1.10.1", "", { "dependencies": { "is-mobile": "^5.0.0", "react-is": "^18.2.0" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng=="], "@rc-component/virtual-list": ["@rc-component/virtual-list@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.20.0", "@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" } }, "sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ=="], "@replit/codemirror-indentation-markers": ["@replit/codemirror-indentation-markers@6.5.3", "", { "peerDependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-hL5Sfvw3C1vgg7GolLe/uxX5T3tmgOA3ZzqlMv47zjU1ON51pzNWiVbS22oh6crYhtVhv8b3gdXwoYp++2ilHw=="], - "@rsbuild/core": ["@rsbuild/core@1.7.5", "", { "dependencies": { "@rspack/core": "~1.7.10", "@rspack/lite-tapable": "~1.1.0", "@swc/helpers": "^0.5.20", "core-js": "~3.47.0", "jiti": "^2.6.1" }, "bin": { "rsbuild": "bin/rsbuild.js" } }, "sha512-i37urpoV4y9NSsGiUOuLdoI42KJ5h4gAZ8EG8Ilmsond3bxoAoOCu7YvC+1pJ7p+r16suVPW8cki891ZKHOoXQ=="], - - "@rsbuild/plugin-react": ["@rsbuild/plugin-react@1.4.6", "", { "dependencies": { "@rspack/plugin-react-refresh": "^1.6.1", "react-refresh": "^0.18.0" }, "peerDependencies": { "@rsbuild/core": "^1.0.0 || ^2.0.0-0" }, "optionalPeers": ["@rsbuild/core"] }, "sha512-LAT6xHlEyZKA0VjF/ph5d50iyG+WSmBx+7g98HNZUwb94VeeTMZFB8qVptTkbIRMss3BNKOXmHOu71Lhsh9oEw=="], + "@rsbuild/core": ["@rsbuild/core@2.0.1", "", { "dependencies": { "@rspack/core": "^2.0.0", "@swc/helpers": "^0.5.21" }, "peerDependencies": { "core-js": ">= 3.0.0" }, "optionalPeers": ["core-js"], "bin": { "rsbuild": "bin/rsbuild.js" } }, "sha512-5TwUpb10Y+VYaYH8oLL/rfJGrhxrk16BiGzv101kzaMPT60MtOXgjEUTxztbjRuq0ifbtRJ/w7rsIZQ4VziWYg=="], - "@rsbuild/plugin-svgr": ["@rsbuild/plugin-svgr@1.3.1", "", { "dependencies": { "@rsbuild/plugin-react": "^1.4.6", "@svgr/core": "8.1.0", "@svgr/plugin-jsx": "8.1.0", "@svgr/plugin-svgo": "8.1.0", "deepmerge": "^4.3.1", "loader-utils": "^3.3.1" }, "peerDependencies": { "@rsbuild/core": "^1.0.0 || ^2.0.0-0" }, "optionalPeers": ["@rsbuild/core"] }, "sha512-HQupp4ubDgoPg0VHva68BZKNEb2GM/bZfQP/Pit2tV7vu/SnOO8MZFdiFbjyK/VXCdQktIMbkNREG7xDgpo/ww=="], + "@rsbuild/plugin-react": ["@rsbuild/plugin-react@2.0.0", "", { "dependencies": { "@rspack/plugin-react-refresh": "2.0.0", "react-refresh": "^0.18.0" }, "peerDependencies": { "@rsbuild/core": "^2.0.0-0" }, "optionalPeers": ["@rsbuild/core"] }, "sha512-/1gzt39EGUSFEqB83g46QoOwsgv172HI18i6au1b6lgIaX4sv9stuX4ijdHbHCp8PqYEq+MyQ99jIQMO6I+etg=="], - "@rspack/binding": ["@rspack/binding@1.7.11", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.7.11", "@rspack/binding-darwin-x64": "1.7.11", "@rspack/binding-linux-arm64-gnu": "1.7.11", "@rspack/binding-linux-arm64-musl": "1.7.11", "@rspack/binding-linux-x64-gnu": "1.7.11", "@rspack/binding-linux-x64-musl": "1.7.11", "@rspack/binding-wasm32-wasi": "1.7.11", "@rspack/binding-win32-arm64-msvc": "1.7.11", "@rspack/binding-win32-ia32-msvc": "1.7.11", "@rspack/binding-win32-x64-msvc": "1.7.11" } }, "sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q=="], + "@rsbuild/plugin-svgr": ["@rsbuild/plugin-svgr@2.0.1", "", { "dependencies": { "@rsbuild/plugin-react": "^2.0.0", "@svgr/core": "8.1.0", "@svgr/plugin-jsx": "8.1.0", "@svgr/plugin-svgo": "8.1.0", "deepmerge": "^4.3.1", "loader-utils": "^3.3.1" }, "peerDependencies": { "@rsbuild/core": "^2.0.0-0" }, "optionalPeers": ["@rsbuild/core"] }, "sha512-cnBGmwtuhj1ltPBScsQVAvsF29m9xmtKXM3sjWWpDfPISkSTxR/jIRvFOf5qYVY37bJIs41Rxfd+PgrGazIgag=="], - "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.7.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oduECiZVqbO5zlVw+q7Vy65sJFth99fWPTyucwvLJJtJkPL5n17Uiql2cYP6Ijn0pkqtf1SXgK8WjiKLG5bIig=="], + "@rspack/binding": ["@rspack/binding@2.0.0", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "2.0.0", "@rspack/binding-darwin-x64": "2.0.0", "@rspack/binding-linux-arm64-gnu": "2.0.0", "@rspack/binding-linux-arm64-musl": "2.0.0", "@rspack/binding-linux-x64-gnu": "2.0.0", "@rspack/binding-linux-x64-musl": "2.0.0", "@rspack/binding-wasm32-wasi": "2.0.0", "@rspack/binding-win32-arm64-msvc": "2.0.0", "@rspack/binding-win32-ia32-msvc": "2.0.0", "@rspack/binding-win32-x64-msvc": "2.0.0" } }, "sha512-WA2f9eQpejkvf5Vrnf6wNCn1m8RT1p08NjgOZpKhsCzr0uBjWeRvGduawlrFFHZh/jPnWZTVaVdQ08FEAWbwGw=="], - "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.7.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-a1+TtTE9ap6RalgFi7FGIgkJP6O4Vy6ctv+9WGJy53E4kuqHR0RygzaiVxCI/GMc/vBT9vY23hyrpWb3d1vtXA=="], + "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@2.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ICBHDKYyndFqljLhjxvKfWWZu39RJSH2jkSmbceXl0kmptLSE0cLWpvk+eGSzLqtxKN0jVchwCw+5P5mWCzwAw=="], - "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.7.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-P0QrGRPbTWu6RKWfN0bDtbnEps3rXH0MWIMreZABoUrVmNQKtXR6e73J3ub6a+di5s2+K0M2LJ9Bh2/H4UsDUA=="], + "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@2.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-YQ96LMmzIzhZt9cZWUDWXSxS9UWWHWoLxJyZ5f42DSaVPVelBg5ThbVORDwOP5QDA2xFXj60rVnmmcZLzg/aDA=="], - "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.7.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-6ky7R43VMjWwmx3Yx7Jl7faLBBMAgMDt+/bN35RgwjiPgsIByz65EwytUVuW9rikB43BGHvA/eqlnjLrUzNBqw=="], + "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ufn33gzkIV7JY69k6vJQEdOzRvBqThIgH46pwXksHSMwRZp8IbJhXfyYIAVsRWCk8fXpr9t1nAvCDvJXT2EeyA=="], - "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.7.11", "", { "os": "linux", "cpu": "x64" }, "sha512-cuOJMfCOvb2Wgsry5enXJ3iT1FGUjdPqtGUBVupQlEG4ntSYsQ2PtF4wIDVasR3wdxC5nQbipOrDiN/u6fYsdQ=="], + "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-CZbvFKlNY9UC0C+Czz6i8JFCzGpuL9oX8gEqcJA1+84Y6eEEBH50UiTzeCewxKW3dOofkZdvT5vgNMXz6aMUmg=="], - "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.7.11", "", { "os": "linux", "cpu": "x64" }, "sha512-CoK37hva4AmHGh3VCsQXmGr40L36m1/AdnN5LEjUX6kx5rEH7/1nEBN6Ii72pejqDVvk9anEROmPDiPw10tpFg=="], + "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-dPjFGpoCvZfFpJBsWAUR+PR7mWYxpou6L026qIOpAVkz7WiTzErwKD3P1jVrpP4dM9yLb3fVE+PHHjTglhTJ4g=="], - "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@1.7.11", "", { "dependencies": { "@napi-rs/wasm-runtime": "1.0.7" }, "cpu": "none" }, "sha512-OtrmnPUVJMxjNa3eDMfHyPdtlLRmmp/aIm0fQHlAOATbZvlGm12q7rhPW5BXTu1yh+1rQ1/uqvz+SzKEZXuJaQ=="], + "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4fgDTMWt0mJDiugdia2mdOjTbnm7yM1Drzl1JpPqlUlOr113byOhc+qgN57LURSGypz2yz/h/Zad7/UnVAxYJw=="], - "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.7.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-lObFW6e5lCWNgTBNwT//yiEDbsxm9QG4BYUojqeXxothuzJ/L6ibXz6+gLMvbOvLGV3nKgkXmx8GvT9WDKR0mA=="], + "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@2.0.0", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "1.1.4" }, "cpu": "none" }, "sha512-ANk73ZKtPrZf9gdtyRK2nQUfhi1uXoC5P2KF89pyVAE8+zcoLBnYtZGYpWa/cmNi5BcO5g4Z+v2l1UA3bUPLQQ=="], - "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.7.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-0pYGnZd8PPqNR68zQ8skamqNAXEA1sUfXuAdYcknIIRq2wsbiwFzIc0Pov1cIfHYab37G7sSIPBiOUdOWF5Ivw=="], + "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@2.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-IHZFRtJ85ONbM+BCtF4TeYXS2Fu9X0IJS2phX1rPibYq9iEtHGfBt4cNlnsJPhbPAXVvi4Oli/yiLRJ1zxtCIg=="], - "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.7.11", "", { "os": "win32", "cpu": "x64" }, "sha512-EeQXayoQk/uBkI3pdoXfQBXNIUrADq56L3s/DFyM2pJeUDrWmhfIw2UFIGkYPTMSCo8F2JcdcGM32FGJrSnU0Q=="], + "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@2.0.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-n4tbIqacq/FhNJflMlgZV50AeQFTLh5hnDS3v4W+rJWa3IW1VfgB0+XppdeW+Dqhw7QcMIsCmro01kwNdlXZDQ=="], - "@rspack/core": ["@rspack/core@1.7.11", "", { "dependencies": { "@module-federation/runtime-tools": "0.22.0", "@rspack/binding": "1.7.11", "@rspack/lite-tapable": "1.1.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-rsD9b+Khmot5DwCMiB3cqTQo53ioPG3M/A7BySu8+0+RS7GCxKm+Z+mtsjtG/vsu4Tn2tcqCdZtA3pgLoJB+ew=="], + "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@2.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-cJOgikIW2t3S+42TQZsv+DJriJt2m6lnUk+pUFu/fO93rrMvNrx8gfMxR8W5zDTreBX0cfMx2pw6EVmyi/YzsQ=="], - "@rspack/lite-tapable": ["@rspack/lite-tapable@1.1.0", "", {}, "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw=="], + "@rspack/core": ["@rspack/core@2.0.0", "", { "dependencies": { "@rspack/binding": "2.0.0" }, "peerDependencies": { "@module-federation/runtime-tools": "^0.24.1 || ^2.0.0", "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@module-federation/runtime-tools", "@swc/helpers"] }, "sha512-WD1mJM9LbZ7Z399Rbv9dE3BNEV0+3sE5OzDdzV8hOxUb3mX++ynK5n9kil8w60B6nGdcKeV9ly5aN4PgqiwWUg=="], - "@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@1.6.1", "", { "dependencies": { "error-stack-parser": "^2.1.4", "html-entities": "^2.6.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0", "webpack-hot-middleware": "2.x" }, "optionalPeers": ["webpack-hot-middleware"] }, "sha512-eqqW5645VG3CzGzFgNg5HqNdHVXY+567PGjtDhhrM8t67caxmsSzRmT5qfoEIfBcGgFkH9vEg7kzXwmCYQdQDw=="], + "@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@2.0.0", "", { "peerDependencies": { "@rspack/core": "^2.0.0-0", "react-refresh": ">=0.10.0 <1.0.0" }, "optionalPeers": ["@rspack/core"] }, "sha512-Cf6CxBStNDJbiXMc/GmsvG1G8PRlUpa0MSfWsMTI+e8npzuTN/p8nwLs3shriBZOLciqgkSZpBtPTd10BLpj1g=="], "@sphinxxxx/color-conversion": ["@sphinxxxx/color-conversion@2.2.2", "", {}, "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw=="], @@ -396,41 +379,41 @@ "@svgr/plugin-svgo": ["@svgr/plugin-svgo@8.1.0", "", { "dependencies": { "cosmiconfig": "^8.1.3", "deepmerge": "^4.3.1", "svgo": "^3.0.2" }, "peerDependencies": { "@svgr/core": "*" } }, "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA=="], - "@swc/helpers": ["@swc/helpers@0.5.20", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw=="], + "@swc/helpers": ["@swc/helpers@0.5.21", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg=="], - "@tailwindcss/node": ["@tailwindcss/node@4.2.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.3" } }, "sha512-dhXFXkW2dGvX4r/fi24gyXM0t1mFMrpykQjqrdA4SuavaMagm4SY1u5G2SCJwu1/0x/5RlZJ2VPjP3mKYQfCkA=="], + "@tailwindcss/node": ["@tailwindcss/node@4.2.4", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.4" } }, "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.3", "@tailwindcss/oxide-darwin-arm64": "4.2.3", "@tailwindcss/oxide-darwin-x64": "4.2.3", "@tailwindcss/oxide-freebsd-x64": "4.2.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.3", "@tailwindcss/oxide-linux-arm64-musl": "4.2.3", "@tailwindcss/oxide-linux-x64-gnu": "4.2.3", "@tailwindcss/oxide-linux-x64-musl": "4.2.3", "@tailwindcss/oxide-wasm32-wasi": "4.2.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.3", "@tailwindcss/oxide-win32-x64-msvc": "4.2.3" } }, "sha512-YyhwSBcxHLS3CU2Mk3dXDuVm8/Ia0+XvfpT8s9YQoICppkUeoobB3hgyGMYbyQ4vn6VgWH9bdv5UnzhTz2NPTQ=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.4", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.4", "@tailwindcss/oxide-darwin-arm64": "4.2.4", "@tailwindcss/oxide-darwin-x64": "4.2.4", "@tailwindcss/oxide-freebsd-x64": "4.2.4", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", "@tailwindcss/oxide-linux-x64-musl": "4.2.4", "@tailwindcss/oxide-wasm32-wasi": "4.2.4", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" } }, "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.3", "", { "os": "android", "cpu": "arm64" }, "sha512-0Jmt1U/zPqeKp1+fvgI3qMqrV5b/EcFIbE5Dl5KdPl5Ri6e+95nlYNjfB3w8hJBeASI4IQSnIMz0tdVP1AVO4g=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.4", "", { "os": "android", "cpu": "arm64" }, "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c+/Etn/nghKBhd9fh2diG+3SEV1VTTPLlqH209yleofi28H87Cy6g1vsd3W3kf6r/dR5g4G4TEwHxo2Ydn6yFw=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-1DrKKsdJTLuLWVdpaLZ0j/g9YbCZyP9xnwSqEvl3gY4ZHdXmX7TwVAHkoWUljOq7JK5zvzIGhrYmfE/2DJ5qaA=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-HE6HHZYF8k7m80eVQ0RBvRGBdvvLvCpHiT38IRH9JSnBlt1T7gDzWoslWjmpXQFuqlRpzkCpbdKJa3NxWMfgVA=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.3", "", { "os": "linux", "cpu": "arm" }, "sha512-Li2wVd2kkKlKkTdpo7ujHSv6kxD1UYMvulAraikyvVf6AKNZ/VHbm8XoSNimZ+dF7SOFaDD2VAT64SK7WKcbjQ=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-otIiImZaHj9MiDK02ItoWxIVcMTZVAX2F1c32bg9y7ecV0AnN5JHDZqIO8LxWsTuig1d+Bjg0cBWn4A9sGJO9Q=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-MmIA32rNEOrjh6wnevlR3OjjlCuwgZ4JMJo7Vrhk4Fk56Vxi7EeF7cekSKwvlrnfcn/ERC1LdcG3sFneU8WdoA=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-BiCy1YV0IKO+xbD7gyZnENU4jdwDygeGQjncJoeIE5Kp4UqWHFsKUSJ3pp7vYURrqVzwJX2xD5gQeGnoXp4xPQ=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-venvyAu0AMKdr0c1Oz23IJJdZ72zSwKyHrLvqQV1cn49vPAJk3AuVtDkJ1ayk1sYI4M4j8Jv6ZGflpaP0QVSXQ=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.3", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-e3kColrZZCdtbwIOc07cNQ2zNf1sTPXTYLjjPlsgsaf+ttzAg/hOlDyEgHoOlBGxM88nPxeVaOGe9ThqVzPncg=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.4", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-qpwoUPzfu71cppxOtcz4LXMR1brljS13yOcAAnVHKIL++NJvSQKZBKlP39pVowd+G6Mq34YAbf4CUUYdLWL9gQ=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.3", "", { "os": "win32", "cpu": "x64" }, "sha512-dTRIlLRC5lCRHqO5DLb+A18HCvS394axmzqfnRNLptKVw7WuckpUwo1Z87Yw74mesbeIhnQTA2SZbRcIfVlwxg=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw=="], - "@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.3", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.3", "@tailwindcss/oxide": "4.2.3", "postcss": "^8.5.6", "tailwindcss": "4.2.3" } }, "sha512-MehdHOQRVFf300r8F430s4cf2QL+nSjFUNIndX5ZMqDLyMwTnyL4RDZsoDsDU+ThzT5eCj1+erSDKBWdn462Nw=="], + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.4", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.4", "@tailwindcss/oxide": "4.2.4", "postcss": "^8.5.6", "tailwindcss": "4.2.4" } }, "sha512-wgAVj6nUWAolAu8YFvzT2cTBIElWHkjZwFYovF+xsqKsW2ADxM/X2opxj5NsF/qVccAOjRNe8X2IdPzMsWyHTg=="], - "@tanstack/query-core": ["@tanstack/query-core@5.99.2", "", {}, "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA=="], + "@tanstack/query-core": ["@tanstack/query-core@5.100.5", "", {}, "sha512-t20KrhKkf0HXzqQkPbJ5erhFesup68BAbwFgYmTrS7bxMF7O5MdmL8jUkik4thsG7Hg00fblz30h6yF1d5TxGg=="], - "@tanstack/react-query": ["@tanstack/react-query@5.99.2", "", { "dependencies": { "@tanstack/query-core": "5.99.2" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA=="], + "@tanstack/react-query": ["@tanstack/react-query@5.100.5", "", { "dependencies": { "@tanstack/query-core": "5.100.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-aNwj1mi2v2bQ9IxkyR1grLOUkv3BYWoykHy9KDyLNbjC3tsahbOHJibK+Wjtr1wRhG59/AvJhiJG5OlthaCgJA=="], "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], @@ -440,7 +423,7 @@ "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], - "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], @@ -500,37 +483,33 @@ "@types/react-router-dom": ["@types/react-router-dom@5.3.3", "", { "dependencies": { "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router": "*" } }, "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw=="], - "@types/stylis": ["@types/stylis@4.2.7", "", {}, "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA=="], - "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@typescript-eslint/types": ["@typescript-eslint/types@8.58.0", "", {}, "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww=="], - - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260420.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260420.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260420.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260420.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260420.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260420.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260420.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260420.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-XYMYZ1OAvO1OWJMntCGW5yoJvJjdrsgmNJxXEPO3Za2kFmQ69TdHOAzg9adurMgAXNpaDWtf28bfEveMRnvpZA=="], + "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260426.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260426.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260426.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260426.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260426.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260426.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260426.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260426.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-zE7B6TIG4XDYr4Your5E2Bxm1vD2YiPyD8OFG4nD5Odt/uN6gO0Y+T4TIbtGUBmOftMRqEV2Jw1ZC4ka0my1yw=="], - "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260420.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-WMVphWuSmPuDzdutP2OM7ZhvlgdrBQ6ufQia+cJz6jqLd2MjPaYlS+/ACEcTgf6+PsTXygUOUvcqv9j5s7QC8w=="], + "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260426.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HzGvERpIFO7p6pMljPN1fIOHqAv2oMeVIqYLSt27TKILkTRpe7fANW3R2OAM+/A+pLtYNNXGDbKl/wR+DHz9KA=="], - "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260420.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-ChRf5ZpdM5CmplmtfCHGFd/UjYDkld9Z27h5HeC41NGxkbe7NeNQYjwPcbZynca2swISZvFr8Wwlo4nNZG/51A=="], + "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260426.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-aE17wCPNQ09K4jV7TQYYRYF/Q/6nFS9jLpbyTYHtS+i+0yV1Rrs4VsqboisS1R/iSWsq3m1Yhh3uS4x3/9KUkg=="], - "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260420.1", "", { "os": "linux", "cpu": "arm" }, "sha512-8+Ey5WZ1jiJXFuN8oQxu3N6pTi6UvigNXTWmB/e+2q6cu6ytFNgiVic6xXtcxOqBpqYMKwXzTZkECjs3NtT6og=="], + "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260426.1", "", { "os": "linux", "cpu": "arm" }, "sha512-/XJRC8B6JeOOb2/iek/BrzW4r5Nut+fkucG7ntEOQn63IRTsfP+AfJdJodG1VIwXOleNlFgG4RtYTUsvcbDJhg=="], - "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260420.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-AmRuZ5R1TTrBNMInIACxd3AGvXS8X0PCAheftIMSa/1YOsNbfvPCbzZTleSaIx0ETabPeKQArur0uTPwnKjJbg=="], + "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260426.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-6OfhODChD1N6FX+ITzA1lny3WX6uew/Nw9kN7uWhymXlM3/vE0qtaAfsMpgdHdCbTPgcdpGaNFhbcMieju9Vdg=="], - "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260420.1", "", { "os": "linux", "cpu": "x64" }, "sha512-zk6y0AX1+SjuYXZfIRTkpRttp+gfpnz6ELFDWLuhtTJtnxgm8BRSKPup2rcm4xkfpRrVv9dhPGSjJYGNR9z9tg=="], + "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260426.1", "", { "os": "linux", "cpu": "x64" }, "sha512-KPDpjmLo/4xY8ugfMGFm7Ona/1igPzZveLt/C0rb6/jNPYuShumRfKYnItGDRXBlmecJY/04lrqkWqQjhtSSPg=="], - "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260420.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-TopcWdHwUGaocYuoj0qEZmZa1SLFm9hY0jKrzi5Xa9Xt/gvUnSLJYRBVblibM+/s8S9ZaV94Gc+CUn9EeSsJ9A=="], + "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260426.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-I7ThiopxuNKX/iAcwgMwsm6L32GOwmwLOyPwQmXjh5c3VD2acq3FYyZRDJVk0aUUy1w6bTbODlo5ZHoPnlZtvw=="], - "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260420.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9P6DDMOCncgXGbFfxRpZE0sWzjyymRON67T9J1u885NLp+AmIdns8e91nzD6ipB0EdZimyR4ZjdSZfqetAQSrQ=="], + "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260426.1", "", { "os": "win32", "cpu": "x64" }, "sha512-4624MJq72vN4H1msiWVBqAIyerJRi5Ni/U6eeE1A1Opqg4c4QoalYQQ+5h5RIuaZ6rY+9kvUn+SjsvbZwyLbjQ=="], "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "adler-32": ["adler-32@1.3.1", "", {}, "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="], - "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -546,7 +525,7 @@ "base64-arraybuffer": ["base64-arraybuffer@1.0.2", "", {}, "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.13", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.23", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g=="], "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], @@ -554,7 +533,7 @@ "bubblesets-js": ["bubblesets-js@2.3.4", "", {}, "sha512-DyMjHmpkS2+xcFNtyN00apJYL3ESdp9fTrkDr5+9Qg/GPqFmcWgGsK1akZnttE1XFxJ/VMy4DNNGMGYtmFp1Sg=="], - "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], @@ -562,7 +541,7 @@ "camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001784", "", {}, "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001791", "", {}, "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ=="], "cfb": ["cfb@1.2.2", "", { "dependencies": { "adler-32": "~1.3.0", "crc-32": "~1.2.0" } }, "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA=="], @@ -588,8 +567,6 @@ "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "core-js": ["core-js@3.47.0", "", {}, "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg=="], - "cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="], "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], @@ -678,7 +655,7 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "devalue": ["devalue@5.6.4", "", {}, "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA=="], + "devalue": ["devalue@5.7.1", "", {}, "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA=="], "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], @@ -694,21 +671,19 @@ "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], - "electron-to-chromium": ["electron-to-chromium@1.5.331", "", {}, "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q=="], + "electron-to-chromium": ["electron-to-chromium@1.5.344", "", {}, "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg=="], - "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], + "enhanced-resolve": ["enhanced-resolve@5.21.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA=="], "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], - "error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], - "esrap": ["esrap@2.2.4", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@typescript-eslint/types": "^8.2.0" } }, "sha512-suICpxAmZ9A8bzJjEl/+rLJiDKC0X4gYWUxT6URAWBLvlXmtbZd5ySMu/N2ZGEtMCAmflUDPSehrP9BQcsGcSg=="], + "esrap": ["esrap@2.2.5", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig=="], "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], @@ -742,8 +717,6 @@ "history": ["history@5.3.0", "", { "dependencies": { "@babel/runtime": "^7.7.6" } }, "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ=="], - "html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="], - "html2canvas": ["html2canvas@1.4.1", "", { "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" } }, "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA=="], "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -756,7 +729,7 @@ "iota-array": ["iota-array@1.0.0", "", {}, "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA=="], - "is-any-array": ["is-any-array@2.0.1", "", {}, "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ=="], + "is-any-array": ["is-any-array@3.0.0", "", {}, "sha512-o4h+tylWykC4BD1vaejp6gDxoM13bwW8FGuNs4yIKpj8xbBJcRxJx8vZpq0dCr7ZDEfeKjmsi/euolKhX6f/ww=="], "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], @@ -798,7 +771,7 @@ "jsonpath-plus": ["jsonpath-plus@10.4.0", "", { "dependencies": { "@jsep-plugin/assignment": "^1.3.0", "@jsep-plugin/regex": "^1.0.4", "jsep": "^1.4.0" }, "bin": { "jsonpath": "bin/jsonpath-cli.js", "jsonpath-plus": "bin/jsonpath-cli.js" } }, "sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA=="], - "jsonrepair": ["jsonrepair@3.13.3", "", { "bin": { "jsonrepair": "bin/cli.js" } }, "sha512-BTznj0owIt2CBAH/LTo7+1I5pMvl1e1033LRl/HUowlZmJOIhzC0zbX5bxMngLkfT4WnzPP26QnW5wMr2g9tsQ=="], + "jsonrepair": ["jsonrepair@3.14.0", "", { "bin": { "jsonrepair": "bin/cli.js" } }, "sha512-tWPGKMZf/8UPim+fcW2EfcQ/d/7aKUrP6IECz9G3Tu6Q5dX0orSleqJ9z6sSw7qrQkjF8/Edo4DvsWBZ8H+HNg=="], "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], @@ -852,13 +825,13 @@ "mitata": ["mitata@1.0.34", "", {}, "sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA=="], - "ml-array-max": ["ml-array-max@1.2.4", "", { "dependencies": { "is-any-array": "^2.0.0" } }, "sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ=="], + "ml-array-max": ["ml-array-max@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0" } }, "sha512-QQZ4kENwpWmyNb98UXRDFXrmtIXuXtt1+bSbda/2KA85+F+rrJP8hZk6QOkCQXM2Th9mUDYdq/PNByPdT9ID4A=="], - "ml-array-min": ["ml-array-min@1.2.3", "", { "dependencies": { "is-any-array": "^2.0.0" } }, "sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q=="], + "ml-array-min": ["ml-array-min@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0" } }, "sha512-GRj6Ky6sW9vGL6yIjgsHmXZ9YgrdmcQ8nCxPqEGeKc6dkfYg1XDYxGFxADUjNuZyoCd5PUscWAS4N+cFaX6hFg=="], - "ml-array-rescale": ["ml-array-rescale@1.3.7", "", { "dependencies": { "is-any-array": "^2.0.0", "ml-array-max": "^1.2.4", "ml-array-min": "^1.2.3" } }, "sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ=="], + "ml-array-rescale": ["ml-array-rescale@2.0.0", "", { "dependencies": { "is-any-array": "^3.0.0", "ml-array-max": "^2.0.0", "ml-array-min": "^2.0.0" } }, "sha512-2GGtKfSno94/kIloWGvpp/U5Q5vLvLrza+SAaGsLeo6Xj4mEbA6Gqx+oTfZFkxnd1grT2X007HfJNs3T5BsiVg=="], - "ml-matrix": ["ml-matrix@6.12.1", "", { "dependencies": { "is-any-array": "^2.0.1", "ml-array-rescale": "^1.3.7" } }, "sha512-TJ+8eOFdp+INvzR4zAuwBQJznDUfktMtOB6g/hUcGh3rcyjxbz4Te57Pgri8Q9bhSQ7Zys4IYOGhFdnlgeB6Lw=="], + "ml-matrix": ["ml-matrix@6.12.2", "", { "dependencies": { "is-any-array": "^3.0.0", "ml-array-rescale": "^2.0.0" } }, "sha512-GC+BnW+pBh8Auap8goAxY0senAmF0IEoc3HNVSfnfbvGw0buuDIYb9kAKMS1l+GiwJ1rfK2bzJ8IHhwjzATSFA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -876,7 +849,7 @@ "node-bitmap": ["node-bitmap@0.0.1", "", {}, "sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA=="], - "node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="], + "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], @@ -898,7 +871,7 @@ "pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="], - "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], + "postcss": ["postcss@8.5.12", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA=="], "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], @@ -916,9 +889,9 @@ "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], - "react-router": ["react-router@7.14.1", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg=="], + "react-router": ["react-router@7.14.2", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-yCqNne6I8IB6rVCH7XUvlBK7/QKyqypBFGv+8dj4QBFJiiRX+FG7/nkdAvGElyvVZ/HQP5N19wzteuTARXi5Gw=="], - "react-router-dom": ["react-router-dom@7.14.1", "", { "dependencies": { "react-router": "7.14.1" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA=="], + "react-router-dom": ["react-router-dom@7.14.2", "", { "dependencies": { "react-router": "7.14.2" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-YZcM5ES8jJSM+KrJ9BdvHHqlnGTg5tH3sC5ChFRj4inosKctdyzBDhOyyHdGk597q2OT6NTrCA1OvB/YDwfekQ=="], "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], @@ -942,8 +915,6 @@ "seventh": ["seventh@0.9.4", "", { "dependencies": { "setimmediate": "^1.0.5" } }, "sha512-O85mosi4sOfxG+slvqy0j7zLuFD4ylUgEMt7Pvt9Q/wnwNwG/6MNnHKzV9JkAoPoPM26t/DLFn17p7o7u5kIBA=="], - "shallowequal": ["shallowequal@1.1.0", "", {}, "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="], - "simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], @@ -954,19 +925,17 @@ "ssf": ["ssf@0.11.2", "", { "dependencies": { "frac": "~1.1.2" } }, "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g=="], - "stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="], - "string-convert": ["string-convert@0.2.1", "", {}, "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="], "string-kit": ["string-kit@0.19.3", "", {}, "sha512-94p913R6+Ea6656A39bqJeHgt64HM9RSq36oc0F8N8Mz76g5+cLaqEwkouqguEDrJ4rhVQpS+tKz214FdtMoqA=="], "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], - "styled-components": ["styled-components@6.3.12", "", { "dependencies": { "@emotion/is-prop-valid": "1.4.0", "@emotion/unitless": "0.10.0", "@types/stylis": "4.2.7", "css-to-react-native": "3.2.0", "csstype": "3.2.3", "postcss": "8.4.49", "shallowequal": "1.1.0", "stylis": "4.3.6", "tslib": "2.8.1" }, "peerDependencies": { "react": ">= 16.8.0", "react-dom": ">= 16.8.0" }, "optionalPeers": ["react-dom"] }, "sha512-hFR6xsVkVYbsdcUlzPYFvFfoc6o2KlV0VvgRIQwSYMtdThM7SCxnjX9efh/cWce2kTq16I/Kl3xM98xiLptsXA=="], + "styled-components": ["styled-components@6.4.1", "", { "dependencies": { "@emotion/is-prop-valid": "1.4.0", "css-to-react-native": "3.2.0", "csstype": "3.2.3", "stylis": "4.3.6" }, "peerDependencies": { "react": ">= 16.8.0", "react-dom": ">= 16.8.0", "react-native": ">= 0.68.0" }, "optionalPeers": ["react-dom", "react-native"] }, "sha512-ADu2dF53esUzzM4I0ewxhxFtsDd6v4V6dNkg3vG0iFKhnt06sJneTZnRvujAosZwW0XD58IKgGMQoqri4wHRqg=="], - "stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="], + "stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="], - "svelte": ["svelte@5.55.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-QjvU7EFemf6mRzdMGlAFttMWtAAVXrax61SZYHdkD6yoVGQ89VeyKfZD4H1JrV1WLmJBxWhFch9H6ig/87VGjw=="], + "svelte": ["svelte@5.55.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.4", "esm-env": "^1.2.1", "esrap": "^2.2.4", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw=="], "svg-parser": ["svg-parser@2.0.4", "", {}, "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="], @@ -974,9 +943,9 @@ "svgo": ["svgo@3.3.3", "", { "dependencies": { "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0", "sax": "^1.5.0" }, "bin": "./bin/svgo" }, "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng=="], - "tailwindcss": ["tailwindcss@4.2.3", "", {}, "sha512-fA/NX5gMf0ooCLISgB0wScaWgaj6rjTN2SVAwleURjiya7ITNkV+VMmoHtKkldP6CIZoYCZyxb8zP/e2TWoEtQ=="], + "tailwindcss": ["tailwindcss@4.2.4", "", {}, "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA=="], - "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], "terminal-kit": ["terminal-kit@3.1.2", "", { "dependencies": { "@cronvel/get-pixels": "^3.4.1", "chroma-js": "^2.4.2", "lazyness": "^1.2.0", "ndarray": "^1.0.19", "nextgen-events": "^1.5.3", "seventh": "^0.9.2", "string-kit": "^0.19.0", "tree-kit": "^0.8.7" } }, "sha512-ro2FyU4A+NwA74DLTYTnoCFYuFpgV1aM07IS6MPrJeajoI2hwF44EdUqjoTmKEl6srYDWtbVkc/b1C16iUnxFQ=="], @@ -984,7 +953,7 @@ "throttle-debounce": ["throttle-debounce@5.0.2", "", {}, "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A=="], - "tree-kit": ["tree-kit@0.8.8", "", {}, "sha512-L7zwpXp0/Nha6mljVcVOnhhxuCkFRWmt26wza3TKnyMBewid4F2vyiVdcSsw41ZoG1Wj+3lM48Er9lhttbxfLA=="], + "tree-kit": ["tree-kit@0.8.10", "", {}, "sha512-mwuV3lHL+utI9z7vxah/27wrMJprx925xhkw1N4KuGa1dqIi0DLHWfXJpHEyR+ZI0Ij80zN58ztiGp3KlH0wtw=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -1032,24 +1001,20 @@ "@antv/coord/@antv/util": ["@antv/util@2.0.17", "", { "dependencies": { "csstype": "^3.0.8", "tslib": "^2.0.3" } }, "sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q=="], - "@rc-component/image/@rc-component/util": ["@rc-component/util@1.10.1", "", { "dependencies": { "is-mobile": "^5.0.0", "react-is": "^18.2.0" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng=="], - "@svgr/hast-util-to-babel-ast/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "antd/@rc-component/util": ["@rc-component/util@1.10.1", "", { "dependencies": { "is-mobile": "^5.0.0", "react-is": "^18.2.0" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng=="], - "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -1062,9 +1027,7 @@ "simple-swizzle/is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="], - "styled-components/@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="], - - "styled-components/postcss": ["postcss@8.4.49", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="], + "styled-components/stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="], "svelte/aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], diff --git a/package.json b/package.json index 8642fa1..3ac7f6d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "pushy-admin", "private": true, "scripts": { - "dev": "PUBLIC_API=https://5.rnupdate.online/api rsbuild dev", + "dev": "PUBLIC_API=https://update.reactnative.cn/api rsbuild dev", "dev:local": "PUBLIC_API=http://localhost:9000 rsbuild dev", "build": "NODE_ENV=production rsbuild build", "preview": "rsbuild preview", @@ -12,10 +12,10 @@ "dependencies": { "@ant-design/charts": "^2.6.7", "@ant-design/icons": "^6.1.1", - "@rsbuild/core": "^1.7.5", - "@rsbuild/plugin-react": "^1.4.6", - "@rsbuild/plugin-svgr": "^1.3.1", - "@tanstack/react-query": "^5.99.2", + "@rsbuild/core": "^2.0.1", + "@rsbuild/plugin-react": "^2.0.0", + "@rsbuild/plugin-svgr": "^2.0.1", + "@tanstack/react-query": "^5.100.5", "antd": "^6.3.6", "dayjs": "^1.11.20", "git-url-parse": "^16.1.0", @@ -24,25 +24,25 @@ "json-diff-kit": "^1.0.35", "react": "^19.2.5", "react-dom": "^19.2.5", - "react-router-dom": "^7.14.1", + "react-router-dom": "^7.14.2", "ua-parser-js": "^2.0.9", "vanilla-jsoneditor": "^3.12.0", "xlsx": "^0.18.5" }, "devDependencies": { - "@biomejs/biome": "2.4.12", - "@tailwindcss/postcss": "^4.2.3", + "@biomejs/biome": "2.4.13", + "@tailwindcss/postcss": "^4.2.4", "@testing-library/react": "^16.3.2", - "@types/bun": "^1.3.12", + "@types/bun": "^1.3.13", "@types/git-url-parse": "^16.0.2", "@types/node": "^25.6.0", "@types/react": "^19", "@types/react-dom": "^19", "@types/react-router-dom": "^5.3.3", - "@typescript/native-preview": "^7.0.0-dev.20260420.1", + "@typescript/native-preview": "^7.0.0-dev.20260426.1", "happy-dom": "^20.9.0", "mitata": "^1.0.34", - "tailwindcss": "^4.2.3", + "tailwindcss": "^4.2.4", "typescript": "^6.0.3" }, "trustedDependencies": [ diff --git a/src/components/create-app-modal.tsx b/src/components/create-app-modal.tsx new file mode 100644 index 0000000..ce8b552 --- /dev/null +++ b/src/components/create-app-modal.tsx @@ -0,0 +1,82 @@ +import { Form, Input, Modal, message, Select } from 'antd'; +import { api } from '@/services/api'; +import PlatformIcon from './platform-icon'; + +export const showCreateAppModal = ({ + onCreated, +}: { + onCreated?: (id: number) => void | Promise; +} = {}) => { + let name = ''; + let platform = 'android'; + + Modal.confirm({ + icon: null, + closable: true, + maskClosable: true, + content: ( +
+
+ + { + name = target.value; + }} + /> + + + (name = target.value)} - /> - - - } + placeholder="搜索应用名称、App Key 或平台" + value={query} + onChange={(event) => setQuery(event.target.value)} + /> + {recentApps.length > 0 && ( +
+ 最近访问 + {recentApps.map((app) => ( + + ))} +
+ )} + +
+
+ {hasSearch ? '搜索结果' : '全部应用'} +
+
+ {filteredApps.length > 0 ? ( + filteredApps.map((app) => ( + + )) + ) : ( + + )} +
+
+
+ +
+ + ); +} + +function AppRow({ + app, + isActive, + onSelect, +}: { + app: AppItem; + isActive: boolean; + onSelect: (appId: number) => void; +}) { + const appKeyLabel = formatAppKey(app.appKey); + + return ( + + ); +} + +function formatAppKey(appKey?: string) { + if (!appKey) { + return null; + } + if (appKey.length <= 14) { + return appKey; + } + return `${appKey.slice(0, 6)}...${appKey.slice(-4)}`; +} + +function getSelectedKeys(pathname: string) { + if (pathname === rootRouterPath.user) { + return ['user']; + } + if (pathname === rootRouterPath.apiTokens) { + return ['api-tokens']; + } + if (pathname === rootRouterPath.auditLogs) { + return ['audit-logs']; + } + if (pathname === rootRouterPath.realtimeMetrics) { + return ['realtime-metrics']; + } + if (pathname === rootRouterPath.adminConfig) { + return ['admin-config']; + } + if (pathname === rootRouterPath.adminUsers) { + return ['admin-users']; + } + if (pathname === rootRouterPath.adminApps) { + return ['admin-apps']; + } + if (pathname === rootRouterPath.adminMetrics) { + return ['admin-metrics']; + } + return []; +} + +function getCurrentAppId(pathname: string) { + const match = pathname.match(/^\/apps\/(\d+)/); + return match ? Number(match[1]) : null; +} + +interface ExtLinkProps { + children: ReactNode; + href: string; +} + +function ExtLink({ children, href }: ExtLinkProps) { + return ( + + {children} + + ); +} diff --git a/src/globals.d.ts b/src/globals.d.ts index 49b77c7..d6a174b 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -51,7 +51,6 @@ type Package = import('./types').Package; type Commit = import('./types').Commit; type Version = import('./types').Version; type AppDetail = import('./types').AppDetail; -type SiderMenuProps = import('./types').SiderMenuProps; type ContentProps = import('./types').ContentProps; type VersionConfig = import('./types').VersionConfig; type BindingType = import('./types').BindingType; diff --git a/src/index.css b/src/index.css index 5828df1..b49799e 100644 --- a/src/index.css +++ b/src/index.css @@ -5,6 +5,14 @@ body { height: 100%; } +a { + text-decoration: none; +} + +a:hover { + text-decoration: none; +} + .ant-layout-content { padding: 24px; } diff --git a/src/pages/admin-apps.tsx b/src/pages/admin-apps.tsx index 8d66b02..729bc35 100644 --- a/src/pages/admin-apps.tsx +++ b/src/pages/admin-apps.tsx @@ -1,4 +1,10 @@ -import { CopyOutlined, EditOutlined, SearchOutlined } from '@ant-design/icons'; +import { + CopyOutlined, + EditOutlined, + LineChartOutlined, + LinkOutlined, + SearchOutlined, +} from '@ant-design/icons'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Button, @@ -17,31 +23,71 @@ import { import type { ColumnsType } from 'antd/es/table'; import dayjs from 'dayjs'; import { useEffect, useState } from 'react'; +import { Link, useSearchParams } from 'react-router-dom'; +import { rootRouterPath } from '@/router'; import { adminApi } from '@/services/admin-api'; +import { patchSearchParams } from '@/utils/helper'; const { Title } = Typography; +const parsePositiveInt = (value: string | null, fallback: number) => { + const parsed = Number(value); + return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; +}; + export const Component = () => { const queryClient = useQueryClient(); const screens = Grid.useBreakpoint(); const isMobile = !screens.md; - const [searchKeyword, setSearchKeyword] = useState(''); - const [debouncedSearch, setDebouncedSearch] = useState(''); + const [searchParams, setSearchParams] = useSearchParams(); const [isModalOpen, setIsModalOpen] = useState(false); const [editingApp, setEditingApp] = useState(null); const [form] = Form.useForm(); - // Debounce search + const searchQuery = searchParams.get('search')?.trim() ?? ''; + const currentPage = parsePositiveInt(searchParams.get('page'), 1); + const pageSize = parsePositiveInt( + searchParams.get('pageSize'), + isMobile ? 10 : 20, + ); + const [searchKeyword, setSearchKeyword] = useState(searchQuery); + useEffect(() => { - const timer = setTimeout(() => setDebouncedSearch(searchKeyword), 300); - return () => clearTimeout(timer); - }, [searchKeyword]); + setSearchKeyword(searchQuery); + }, [searchQuery]); + + useEffect(() => { + const trimmedKeyword = searchKeyword.trim(); + if (trimmedKeyword === searchQuery) { + return; + } + + const timer = window.setTimeout(() => { + patchSearchParams(setSearchParams, { + search: trimmedKeyword || undefined, + page: '1', + }); + }, 300); + + return () => { + window.clearTimeout(timer); + }; + }, [searchKeyword, searchQuery, setSearchParams]); const { data, isLoading } = useQuery({ - queryKey: ['adminApps', debouncedSearch], - queryFn: () => adminApi.searchApps(debouncedSearch || undefined), + queryKey: ['adminApps', searchQuery], + queryFn: () => adminApi.searchApps(searchQuery || undefined), }); + const total = data?.data.length ?? 0; + const maxPage = Math.max(1, Math.ceil(total / pageSize)); + + useEffect(() => { + if (currentPage > maxPage) { + patchSearchParams(setSearchParams, { page: String(maxPage) }); + } + }, [currentPage, maxPage, setSearchParams]); + const updateMutation = useMutation({ mutationFn: ({ id, data }: { id: number; data: Partial }) => adminApi.updateApp(id, data), @@ -108,7 +154,7 @@ export const Component = () => { title: 'App Key', dataIndex: 'appKey', key: 'appKey', - width: 200, + width: 220, render: (key: string) => ( {key} @@ -164,9 +210,9 @@ export const Component = () => { key: 'ignoreBuildTime', responsive: ['lg'], width: 120, - render: (v: string | null) => ( - - {v === 'enabled' ? '是' : v === 'disabled' ? '否' : '-'} + render: (value: string | null) => ( + + {value === 'enabled' ? '是' : value === 'disabled' ? '否' : '-'} ), }, @@ -182,15 +228,31 @@ export const Component = () => { { title: '操作', key: 'action', - width: 80, - render: (_: unknown, record: AdminApp) => ( - + width: isMobile ? 136 : 220, + render: (_value, record) => ( + + + + + + + + + ), }, ]; @@ -198,15 +260,20 @@ export const Component = () => { return (
-
- - 应用管理 - +
+
+ + 应用管理 + +
+ 搜索条件会保留在 URL 中,刷新后仍然能回到同一视图。 +
+
} value={searchKeyword} - onChange={(e) => setSearchKeyword(e.target.value)} + onChange={(event) => setSearchKeyword(event.target.value)} allowClear className="w-full md:w-72" /> @@ -218,12 +285,22 @@ export const Component = () => { columns={columns} rowKey="id" size={isMobile ? 'small' : 'middle'} - pagination={ - isMobile - ? { pageSize: 10, simple: true } - : { pageSize: 20, showSizeChanger: true } - } - scroll={{ x: 760 }} + pagination={{ + current: currentPage, + pageSize, + total, + simple: isMobile, + showQuickJumper: !isMobile, + showSizeChanger: !isMobile, + showTotal: isMobile ? undefined : (count) => `共 ${count} 个应用`, + onChange: (page, nextPageSize) => { + patchSearchParams(setSearchParams, { + page: String(page), + pageSize: String(nextPageSize), + }); + }, + }} + scroll={{ x: 900 }} /> diff --git a/src/pages/admin-metrics.tsx b/src/pages/admin-metrics.tsx index ee9b492..6ab5736 100644 --- a/src/pages/admin-metrics.tsx +++ b/src/pages/admin-metrics.tsx @@ -11,8 +11,10 @@ import { } from 'antd'; import type { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; -import { useMemo, useRef, useState } from 'react'; +import { useMemo, useRef } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { api } from '@/services/api'; +import { patchSearchParams } from '@/utils/helper'; const { Title } = Typography; const { RangePicker } = DatePicker; @@ -32,6 +34,7 @@ interface MetricsResponse { } const TOTAL_SERIES_LABEL = 'total'; +const DEFAULT_RANGE_HOURS = 24; const modeLabels: Record = { pv: '请求数', uv: '用户数', @@ -41,7 +44,7 @@ const metricKeyOptions = [ { label: 'rn', value: 'rn' }, { label: 'os', value: 'os' }, { label: 'rnu', value: 'rnu' }, -]; +] satisfies Array<{ label: string; value: MetricKeyPrefix }>; const getCategoryPrefix = (category: string) => { const separatorIndex = category.indexOf(':'); @@ -51,6 +54,7 @@ const getCategoryPrefix = (category: string) => { const getMetricsTotal = (metrics?: MetricsResponse) => { if (!metrics?.data || !metrics.dict) return 0; + let total = 0; for (const bucket of metrics.data) { let bucketTotal = 0; @@ -64,6 +68,7 @@ const getMetricsTotal = (metrics?: MetricsResponse) => { } total += bucketTotal; } + return total; }; @@ -72,17 +77,40 @@ type ChartController = { on: (...args: unknown[]) => unknown; }; +const parseMode = (value: string | null): MetricMode => + value === 'uv' ? 'uv' : 'pv'; + +const parseKeyPrefix = (value: string | null): MetricKeyPrefix => + metricKeyOptions.some((option) => option.value === value) + ? (value as MetricKeyPrefix) + : 'rn'; + +const parseDateRange = (searchParams: URLSearchParams): [Dayjs, Dayjs] => { + const fallbackEnd = dayjs(); + const fallbackStart = fallbackEnd.subtract(DEFAULT_RANGE_HOURS, 'hour'); + const parsedStart = searchParams.get('start') + ? dayjs(searchParams.get('start')) + : fallbackStart; + const parsedEnd = searchParams.get('end') + ? dayjs(searchParams.get('end')) + : fallbackEnd; + + const start = parsedStart.isValid() ? parsedStart : fallbackStart; + const end = parsedEnd.isValid() ? parsedEnd : fallbackEnd; + if (start.isAfter(end)) { + return [end.subtract(DEFAULT_RANGE_HOURS, 'hour'), end]; + } + return [start, end]; +}; + export const Component = () => { - const [dateRange, setDateRange] = useState<[Dayjs, Dayjs]>([ - dayjs().subtract(24, 'hour'), - dayjs(), - ]); - const [mode, setMode] = useState('pv'); - const [selectedKeyPrefix, setSelectedKeyPrefix] = - useState('rn'); + const [searchParams, setSearchParams] = useSearchParams(); const legendValuesRef = useRef([]); - const startDate = dateRange[0].toISOString(); - const endDate = dateRange[1].toISOString(); + const mode = parseMode(searchParams.get('mode')); + const selectedKeyPrefix = parseKeyPrefix(searchParams.get('prefix')); + const [rangeStart, rangeEnd] = parseDateRange(searchParams); + const startDate = rangeStart.toISOString(); + const endDate = rangeEnd.toISOString(); const { data: pvMetrics, isLoading: isLoadingPv } = useQuery({ queryKey: ['globalMetrics', startDate, endDate, 'pv'], @@ -92,7 +120,6 @@ export const Component = () => { end: endDate, mode: 'pv', }), - enabled: !!dateRange[0] && !!dateRange[1], }); const { data: uvMetrics, isLoading: isLoadingUv } = useQuery({ @@ -103,28 +130,27 @@ export const Component = () => { end: endDate, mode: 'uv', }), - enabled: !!dateRange[0] && !!dateRange[1], }); const metricsData = mode === 'pv' ? pvMetrics : uvMetrics; const isChartLoading = mode === 'pv' ? isLoadingPv : isLoadingUv; - // Transform data for chart const chartData = useMemo(() => { if (!metricsData?.data || !metricsData?.dict) return []; + const points: ChartDataPoint[] = []; for (const bucket of metricsData.data) { for (const [dictIndex, count] of bucket.data) { const rawCategory = metricsData.dict[dictIndex] || ''; - // Skip _total entry if (rawCategory === '_total') { continue; } - // Treat empty values as 'unknown' (legacy data compatibility) + let category = rawCategory.replace('\u001f', ': '); if (rawCategory.endsWith('\u001f')) { category = rawCategory.replace('\u001f', ': unknown'); } + points.push({ time: bucket.time, value: count, @@ -132,6 +158,7 @@ export const Component = () => { }); } } + return points; }, [metricsData]); @@ -161,6 +188,7 @@ export const Component = () => { const totalSeriesData = useMemo(() => { if (!prefixFilteredChartData.length) return []; + const totalsByTime = new Map(); for (const point of prefixFilteredChartData) { totalsByTime.set( @@ -168,6 +196,7 @@ export const Component = () => { (totalsByTime.get(point.time) || 0) + point.value, ); } + return Array.from(totalsByTime.entries()) .map(([time, value]) => ({ time, @@ -203,13 +232,17 @@ export const Component = () => { if (!prefixFilteredChartData.length) { return { total: 0, categories: new Map() }; } + let total = 0; const categories = new Map(); for (const point of prefixFilteredChartData) { total += point.value; - const existing = categories.get(point.category) || 0; - categories.set(point.category, existing + point.value); + categories.set( + point.category, + (categories.get(point.category) || 0) + point.value, + ); } + return { total, categories }; }, [prefixFilteredChartData]); @@ -219,20 +252,13 @@ export const Component = () => { .slice(0, 10); }, [displayTotals.categories]); - const handleDateChange = (dates: [Dayjs | null, Dayjs | null] | null) => { - if (dates?.[0] && dates[1]) { - setDateRange([dates[0], dates[1]]); - } - }; - - // Line chart config const lineConfig = { interaction: { legendFilter: true, tooltip: { shared: true }, }, data: lineData, - xField: (d: ChartDataPoint) => new Date(d.time), + xField: (datum: ChartDataPoint) => new Date(datum.time), yField: 'value', colorField: 'category', shapeField: 'smooth', @@ -270,24 +296,39 @@ export const Component = () => { }); }); } catch (error) { - // eslint-disable-next-line no-console console.error(error); } }, height: 480, }; + const handleDateChange = (dates: [Dayjs | null, Dayjs | null] | null) => { + patchSearchParams(setSearchParams, { + start: dates?.[0] ? dates[0].toISOString() : undefined, + end: dates?.[1] ? dates[1].toISOString() : undefined, + }); + }; + return (
-
- - 全局数据统计 - +
+
+ + 全局数据统计 + +
+ 当前时间范围、指标模式和分类前缀都会写入 URL,方便回放同一视图。 +
+
setMode(e.target.value)} + onChange={(event) => { + patchSearchParams(setSearchParams, { + mode: event.target.value as MetricMode, + }); + }} className="w-full md:w-auto" > 请求数 @@ -295,17 +336,18 @@ export const Component = () => { } value={searchKeyword} - onChange={(e) => setSearchKeyword(e.target.value)} + onChange={(event) => setSearchKeyword(event.target.value)} allowClear className="w-full md:w-72" /> @@ -311,11 +354,21 @@ export const Component = () => { columns={columns} rowKey="id" size={isMobile ? 'small' : 'middle'} - pagination={ - isMobile - ? { pageSize: 10, simple: true } - : { pageSize: 20, showSizeChanger: true } - } + pagination={{ + current: currentPage, + pageSize, + total, + simple: isMobile, + showQuickJumper: !isMobile, + showSizeChanger: !isMobile, + showTotal: isMobile ? undefined : (count) => `共 ${count} 个用户`, + onChange: (page, nextPageSize) => { + patchSearchParams(setSearchParams, { + page: String(page), + pageSize: String(nextPageSize), + }); + }, + }} scroll={{ x: 760 }} /> diff --git a/src/pages/apps.tsx b/src/pages/apps.tsx new file mode 100644 index 0000000..3a40b60 --- /dev/null +++ b/src/pages/apps.tsx @@ -0,0 +1,208 @@ +import { + AppstoreOutlined, + PlusOutlined, + SearchOutlined, +} from '@ant-design/icons'; +import { Button, Card, Empty, Input, Spin, Tag, Typography } from 'antd'; +import { useMemo, useState } from 'react'; +import { Link } from 'react-router-dom'; +import { showCreateAppModal } from '@/components/create-app-modal'; +import PlatformIcon from '@/components/platform-icon'; +import { rootRouterPath, router } from '@/router'; +import { cn, rememberRecentApp } from '@/utils/helper'; +import { useAppList } from '@/utils/hooks'; + +const { Title } = Typography; + +type AppItem = NonNullable['apps']>[number]; + +const platformLabels: Record = { + android: 'Android', + ios: 'iOS', + harmony: 'HarmonyOS', +}; + +const formatAppKey = (appKey?: string) => { + if (!appKey) { + return '尚未生成 App Key'; + } + if (appKey.length <= 16) { + return appKey; + } + return `${appKey.slice(0, 8)}...${appKey.slice(-6)}`; +}; + +export const Component = () => { + const { apps: appList, isLoading } = useAppList(); + const [query, setQuery] = useState(''); + const apps = appList ?? []; + const normalizedQuery = query.trim().toLowerCase(); + + const filteredApps = useMemo(() => { + if (!normalizedQuery) { + return apps; + } + return apps.filter((app) => + [app.name, app.appKey, app.platform] + .filter(Boolean) + .some((value) => value?.toLowerCase().includes(normalizedQuery)), + ); + }, [apps, normalizedQuery]); + + const pausedCount = useMemo( + () => apps.filter((app) => app.status === 'paused').length, + [apps], + ); + const totalChecks = useMemo( + () => + apps.reduce((sum, app) => { + return sum + (app.checkCount ?? 0); + }, 0), + [apps], + ); + + const createApp = () => { + showCreateAppModal({ + onCreated: (id) => { + rememberRecentApp(id); + return router.navigate(rootRouterPath.versions(String(id))); + }, + }); + }; + + return ( +
+
+
+
+ + 应用工作台 +
+ + 应用列表 + +
+ 选择一个应用进入版本、原生包和发布配置管理。 +
+
+
+ } + placeholder="搜索应用名称、App Key 或平台" + value={query} + onChange={(event) => setQuery(event.target.value)} + /> + +
+
+ +
+ + + +
+ + + + {filteredApps.length > 0 ? ( +
+ {filteredApps.map((app) => ( + + ))} +
+ ) : ( + + {!query && ( + + )} + + )} +
+
+
+ ); +}; + +function MetricCard({ label, value }: { label: string; value: string }) { + return ( +
+
{label}
+
+ {value} +
+
+ ); +} + +function AppCard({ app }: { app: AppItem }) { + const appKeyLabel = formatAppKey(app.appKey); + + return ( + rememberRecentApp(app.id)} + > +
+
+
+ + + +
+
+ {app.name} +
+
+ {platformLabels[app.platform]} +
+
+
+ + {app.status === 'paused' ? '暂停' : '正常'} + +
+ +
+
+
App Key
+
+ {appKeyLabel} +
+
+
+ 检查次数 + + {(app.checkCount ?? 0).toLocaleString()} + +
+
+ 进入应用管理 +
+
+
+ + ); +} diff --git a/src/pages/audit-logs.tsx b/src/pages/audit-logs.tsx index cb54c52..e9488df 100644 --- a/src/pages/audit-logs.tsx +++ b/src/pages/audit-logs.tsx @@ -1,26 +1,57 @@ -import { DownloadOutlined, FileTextOutlined } from '@ant-design/icons'; -import { Button, DatePicker, Grid, message, Table, Typography } from 'antd'; +import { + DownloadOutlined, + EyeOutlined, + FileTextOutlined, + SearchOutlined, +} from '@ant-design/icons'; +import { + Button, + DatePicker, + Descriptions, + Drawer, + Grid, + Input, + message, + Select, + Space, + Table, + Tag, + Typography, +} from 'antd'; import type { ColumnType } from 'antd/lib/table'; import dayjs, { type Dayjs } from 'dayjs'; import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; import relativeTime from 'dayjs/plugin/relativeTime'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { UAParser } from 'ua-parser-js'; +import { patchSearchParams } from '@/utils/helper'; import { useAuditLogs } from '@/utils/hooks'; import 'dayjs/locale/zh-cn'; -import { UAParser } from 'ua-parser-js'; const { RangePicker } = DatePicker; +const { Paragraph, Text } = Typography; dayjs.extend(relativeTime); dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrBefore); dayjs.locale('zh-cn'); +type AuditStatusFilter = 'all' | 'success' | 'client-error' | 'server-error'; + +const statusFilterOptions = [ + { label: '全部状态', value: 'all' }, + { label: '2xx 成功', value: 'success' }, + { label: '4xx 客户端错误', value: 'client-error' }, + { label: '5xx 服务端错误', value: 'server-error' }, +] satisfies Array<{ label: string; value: AuditStatusFilter }>; + export const getUA = (userAgent: string) => { if (userAgent.startsWith('react-native-update-cli')) { return
cli {userAgent.split('/')[1]}
; } + const { browser, os } = UAParser(userAgent); return ( <> @@ -34,8 +65,6 @@ export const getUA = (userAgent: string) => { ); }; -const { Text } = Typography; - const getApiTokenLabel = (apiTokens?: AuditLog['apiTokens']) => { if (!apiTokens?.tokenSuffix) { return undefined; @@ -46,211 +75,191 @@ const getApiTokenLabel = (apiTokens?: AuditLog['apiTokens']) => { : `****${apiTokens.tokenSuffix}`; }; -// 将 path 中的数字替换为 {id},并移除末尾的斜杠 const normalizePath = (path: string): string => { return path.replace(/\/\d+/g, '/{id}').replace(/\/$/, ''); }; -// API 操作语义映射字典(只包含写操作) const actionMap: Record = { - // 用户相关 'POST /user/login': '登录', 'POST /user/register': '注册', 'POST /user/activate': '激活账户', 'POST /user/activate/sendmail': '发送激活邮件', 'POST /user/resetpwd/sendmail': '发送重置密码邮件', 'POST /user/resetpwd/reset': '重置密码', - // 应用相关 'POST /app/create': '创建应用', 'PUT /app/{id}': '修改应用设置', 'DELETE /app/{id}': '删除应用', - // 订单相关 'POST /orders': '创建订单', 'POST alipayCallback': '支付', - // 文件相关 'POST /upload': '上传文件', - // 原生包相关 'POST /app/{id}/package/create': '创建原生包', 'PUT /app/{id}/package/{id}': '修改原生包设置', 'DELETE /app/{id}/package/{id}': '删除原生包', - // 热更包相关 'POST /app/{id}/version/create': '创建热更包', 'PUT /app/{id}/version/{id}': '修改热更包设置', 'DELETE /app/{id}/version/{id}': '删除热更包', - // 绑定相关 'POST /app/{id}/binding': '创建/更新绑定', 'DELETE /app/{id}/binding/{id}': '删除绑定', - // api key 相关 'POST /api-token/create': '创建 API Key', 'DELETE /api-token/{id}': '删除 API Key', }; -// 获取操作语义描述 +const actionOptions = Object.values(actionMap) + .sort() + .map((value) => ({ + label: value, + value, + })); + const getActionLabel = (method: string, path: string): string => { const normalizedPath = normalizePath(path); const key = `${method.toUpperCase()} ${normalizedPath}`; - return actionMap[key] || `${method.toUpperCase()} ${path}`; }; -// 生成操作类型的 filter -const actionFilters = Object.values(actionMap) - .sort() - .map((value) => ({ - text: value, - value, - })); +const parsePositiveInt = (value: string | null, fallback: number) => { + const parsed = Number(value); + return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; +}; -const columns: ColumnType[] = [ - { - title: '时间', - dataIndex: 'createdAt', - width: 180, - sorter: (a, b) => - dayjs(a.createdAt).valueOf() - dayjs(b.createdAt).valueOf(), - render: (createdAt: string) => { - const date = dayjs(createdAt); - return ( -
-
{date.format('YYYY-MM-DD HH:mm:ss')}
- - {date.fromNow()} - -
- ); - }, - }, - { - title: '操作', - width: 120, - filters: actionFilters, - onFilter: (value, record) => { - const actionLabel = getActionLabel(record.method, record.path); - return actionLabel === value; - }, - render: (_, record) => { - const actionLabel = getActionLabel(record.method, record.path); - const isDelete = record.method.toUpperCase() === 'DELETE'; - const color = isDelete ? '#ff4d4f' : undefined; +const parseStatusFilter = (value: string | null): AuditStatusFilter => { + return statusFilterOptions.some((option) => option.value === value) + ? (value as AuditStatusFilter) + : 'all'; +}; - return ( - - {actionLabel} - - ); - }, - }, - { - title: '状态码', - dataIndex: 'statusCode', - width: 100, - render: (statusCode: string) => { - const code = Number(statusCode); - const isError = code >= 500; - const color = isError ? '#ff4d4f' : undefined; +const parseDateRange = ( + searchParams: URLSearchParams, +): [Dayjs | null, Dayjs | null] | null => { + const startValue = searchParams.get('start'); + const endValue = searchParams.get('end'); + const start = startValue ? dayjs(startValue) : null; + const end = endValue ? dayjs(endValue) : null; - return ( - - {statusCode} - - ); - }, - }, - { - title: '提交数据', - responsive: ['md'], - width: 300, - ellipsis: { - showTitle: false, - }, - render: (_, { path, data }: AuditLog) => { - const isUpload = path.startsWith('/upload'); - if (isUpload) { - if (data?.ext === '.ppk') { - return 热更包; - } else { - return 原生包; - } - } - if (!data) { - return -; - } - delete data.deps; - delete data.commit; - return ( - - {JSON.stringify(data)} - - ); - }, - }, - { - title: '设备信息', - dataIndex: 'userAgent', - responsive: ['lg'], - width: 200, - ellipsis: { - showTitle: false, - }, - render: (userAgent: string | undefined, record: AuditLog) => { - const apiToken = getApiTokenLabel(record.apiTokens); - const hasInfo = userAgent || record.ip || apiToken; - if (!hasInfo) { - return -; - } + if (!start && !end) { + return null; + } - const title = [userAgent, record.ip && `IP: ${record.ip}`, apiToken] - .filter(Boolean) - .join('\n'); + return [start?.isValid() ? start : null, end?.isValid() ? end : null]; +}; - return ( -
- {userAgent &&
{getUA(userAgent)}
} - {record.ip && ( -
- - IP: {record.ip} - -
- )} - {apiToken && ( -
- - API Key:{apiToken} - -
- )} -
- ); - }, - }, -]; +const getPreviewData = (data?: AuditLog['data']) => { + if (!data) { + return null; + } + + const { deps: _deps, commit: _commit, ...rest } = data; + return Object.keys(rest).length ? rest : null; +}; + +const matchesStatusFilter = ( + statusCode: string, + statusFilter: AuditStatusFilter, +) => { + if (statusFilter === 'all') { + return true; + } + + const code = Number(statusCode); + if (!Number.isFinite(code)) { + return false; + } + + if (statusFilter === 'success') { + return code >= 200 && code < 300; + } + if (statusFilter === 'client-error') { + return code >= 400 && code < 500; + } + return code >= 500; +}; + +const buildSearchText = (log: AuditLog) => { + return [ + log.id, + getActionLabel(log.method, log.path), + log.method, + log.path, + log.statusCode, + log.ip, + log.userAgent, + getApiTokenLabel(log.apiTokens), + JSON.stringify(getPreviewData(log.data) ?? {}), + ] + .filter(Boolean) + .join(' ') + .toLowerCase(); +}; export const AuditLogs = () => { const screens = Grid.useBreakpoint(); const isMobile = !screens.md; - const [offset, setOffset] = useState(0); - const [pageSize, setPageSize] = useState(20); - const [dateRange, setDateRange] = useState< - [Dayjs | null, Dayjs | null] | null - >(null); + const [searchParams, setSearchParams] = useSearchParams(); + const [searchInput, setSearchInput] = useState( + searchParams.get('query')?.trim() ?? '', + ); + + const currentPage = parsePositiveInt(searchParams.get('page'), 1); + const pageSize = parsePositiveInt( + searchParams.get('pageSize'), + isMobile ? 10 : 20, + ); + const query = searchParams.get('query')?.trim().toLowerCase() ?? ''; + const selectedAction = searchParams.get('action') ?? undefined; + const statusFilter = parseStatusFilter(searchParams.get('status')); + const dateRange = parseDateRange(searchParams); + const selectedLogId = searchParams.get('logId'); + + useEffect(() => { + setSearchInput(searchParams.get('query')?.trim() ?? ''); + }, [searchParams]); + + useEffect(() => { + const trimmedKeyword = searchInput.trim(); + const normalizedQuery = searchParams.get('query')?.trim() ?? ''; + if (trimmedKeyword === normalizedQuery) { + return; + } + + const timer = window.setTimeout(() => { + patchSearchParams(setSearchParams, { + query: trimmedKeyword || undefined, + page: '1', + }); + }, 300); - const { auditLogs: allAuditLogs, isLoading } = useAuditLogs({ + return () => { + window.clearTimeout(timer); + }; + }, [searchInput, searchParams, setSearchParams]); + + const { allAuditLogs = [], isLoading } = useAuditLogs({ offset: 0, limit: 1000, }); - // 根据日期范围筛选日志 const filteredAuditLogs = useMemo(() => { - if (!dateRange || (!dateRange[0] && !dateRange[1])) { - return allAuditLogs; - } - - const [startDate, endDate] = dateRange; return allAuditLogs.filter((log) => { + if ( + selectedAction && + getActionLabel(log.method, log.path) !== selectedAction + ) { + return false; + } + + if (!matchesStatusFilter(log.statusCode, statusFilter)) { + return false; + } + + if (query && !buildSearchText(log).includes(query)) { + return false; + } + + if (!dateRange || (!dateRange[0] && !dateRange[1])) { + return true; + } + + const [startDate, endDate] = dateRange; const logDate = dayjs(log.createdAt); if (startDate && endDate) { return ( @@ -266,48 +275,35 @@ export const AuditLogs = () => { } return true; }); - }, [allAuditLogs, dateRange]); + }, [allAuditLogs, dateRange, query, selectedAction, statusFilter]); - const handleDateRangeChange = ( - dates: [Dayjs | null, Dayjs | null] | null, - ) => { - // 验证日期范围不超过180天 - if (dates?.[0] && dates?.[1]) { - const startDate = dates[0]; - const endDate = dates[1]; - const diffInDays = endDate.diff(startDate, 'day'); + const maxPage = Math.max(1, Math.ceil(filteredAuditLogs.length / pageSize)); - if (diffInDays > 180) { - // 如果超过180天,自动调整为180天 - const adjustedEndDate = startDate.add(180, 'day'); - setDateRange([startDate, adjustedEndDate]); - } else { - setDateRange(dates); - } - } else { - setDateRange(dates); + useEffect(() => { + if (currentPage > maxPage) { + patchSearchParams(setSearchParams, { page: String(maxPage) }); + } + }, [currentPage, maxPage, setSearchParams]); + + const selectedLog = useMemo(() => { + if (!selectedLogId) { + return null; } - setOffset(0); // 重置到第一页 - }; - // 限制日期选择:不能超过180天,不能选择未来日期,不能选择超过180天前的日期 + return allAuditLogs.find((log) => String(log.id) === selectedLogId) ?? null; + }, [allAuditLogs, selectedLogId]); + const disabledDate = (current: Dayjs | null) => { if (!current) return false; const today = dayjs(); const oneHundredEightyDaysAgo = today.subtract(180, 'day'); - - // 不能选择未来日期 if (current.isAfter(today, 'day')) { return true; } - - // 不能选择超过180天前的日期(从今天开始往过去超过180天) if (current.isBefore(oneHundredEightyDaysAgo, 'day')) { return true; } - - // 如果已经选择了开始日期,限制结束日期不能超过开始日期180天 if (dateRange?.[0] && !dateRange[1]) { const startDate = dateRange[0]; const oneHundredEightyDaysLater = startDate.add(180, 'day'); @@ -316,8 +312,6 @@ export const AuditLogs = () => { current.isAfter(oneHundredEightyDaysLater, 'day') ); } - - // 如果已经选择了结束日期,限制开始日期不能早于结束日期180天 if (!dateRange?.[0] && dateRange?.[1]) { const endDate = dateRange[1]; const oneHundredEightyDaysEarlier = endDate.subtract(180, 'day'); @@ -326,11 +320,33 @@ export const AuditLogs = () => { current.isBefore(oneHundredEightyDaysEarlier, 'day') ); } - return false; }; - // 导出到 Excel + const handleDateRangeChange = ( + dates: [Dayjs | null, Dayjs | null] | null, + ) => { + if (dates?.[0] && dates[1]) { + const startDate = dates[0]; + const endDate = dates[1]; + const diffInDays = endDate.diff(startDate, 'day'); + const nextEndDate = + diffInDays > 180 ? startDate.add(180, 'day') : endDate; + patchSearchParams(setSearchParams, { + start: startDate.toISOString(), + end: nextEndDate.toISOString(), + page: '1', + }); + return; + } + + patchSearchParams(setSearchParams, { + start: dates?.[0] ? dates[0].toISOString() : undefined, + end: dates?.[1] ? dates[1].toISOString() : undefined, + page: '1', + }); + }; + const handleExportToExcel = async () => { if (filteredAuditLogs.length === 0) { return; @@ -339,20 +355,16 @@ export const AuditLogs = () => { try { const XLSX = await import('xlsx'); - // 格式化数据 const excelData = filteredAuditLogs.map((log) => { const date = dayjs(log.createdAt); - const actionLabel = getActionLabel(log.method, log.path); - - // 解析 UA 信息 + const previewData = getPreviewData(log.data); let browserInfo = '-'; let osInfo = '-'; + if (log.userAgent) { - // 处理特殊的 CLI useragent 格式 if (log.userAgent.startsWith('react-native-update-cli')) { const version = log.userAgent.split('/')[1] || ''; browserInfo = `cli ${version}`.trim(); - osInfo = '-'; } else { const { browser, os } = UAParser(log.userAgent); browserInfo = @@ -363,63 +375,223 @@ export const AuditLogs = () => { return { 时间: date.format('YYYY-MM-DD HH:mm:ss'), - 操作: actionLabel, + 操作: getActionLabel(log.method, log.path), + 方法: log.method.toUpperCase(), + 接口: log.path, 状态码: log.statusCode, - 提交数据: log.data ? JSON.stringify(log.data) : '-', + 提交数据: previewData ? JSON.stringify(previewData) : '-', 浏览器: browserInfo, 操作系统: osInfo, IP地址: log.ip || '-', - 'API Key': log.apiTokens - ? `${log.apiTokens.name}(${log.apiTokens.tokenSuffix})` - : '-', + 'API Key': getApiTokenLabel(log.apiTokens) || '-', }; }); - // 创建工作簿 - const wb = XLSX.utils.book_new(); - const ws = XLSX.utils.json_to_sheet(excelData); - - // 设置列宽 - const colWidths = [ - { wch: 20 }, // 时间 - { wch: 15 }, // 操作 - { wch: 10 }, // 状态码 - { wch: 40 }, // 提交数据 - { wch: 20 }, // 浏览器 - { wch: 20 }, // 操作系统 - { wch: 15 }, // IP地址 - { wch: 15 }, // 是否使用API + const workbook = XLSX.utils.book_new(); + const worksheet = XLSX.utils.json_to_sheet(excelData); + worksheet['!cols'] = [ + { wch: 20 }, + { wch: 20 }, + { wch: 10 }, + { wch: 36 }, + { wch: 10 }, + { wch: 40 }, + { wch: 20 }, + { wch: 20 }, + { wch: 18 }, + { wch: 18 }, ]; - ws['!cols'] = colWidths; - - // 添加工作表到工作簿 - XLSX.utils.book_append_sheet(wb, ws, '操作日志'); - - // 生成文件名 - const fileName = `操作日志_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`; - - // 导出文件 - XLSX.writeFile(wb, fileName); + XLSX.utils.book_append_sheet(workbook, worksheet, '操作日志'); + XLSX.writeFile( + workbook, + `操作日志_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`, + ); } catch (error) { message.error(`导出失败:${(error as Error).message}`); } }; + const columns: ColumnType[] = [ + { + title: '时间', + dataIndex: 'createdAt', + width: 180, + render: (createdAt: string) => { + const date = dayjs(createdAt); + return ( +
+
{date.format('YYYY-MM-DD HH:mm:ss')}
+ + {date.fromNow()} + +
+ ); + }, + }, + { + title: '操作', + width: 140, + render: (_value, record) => { + const actionLabel = getActionLabel(record.method, record.path); + const isDelete = record.method.toUpperCase() === 'DELETE'; + return ( + + {actionLabel} + + ); + }, + }, + { + title: '接口', + width: 260, + responsive: ['md'], + render: (_value, record) => ( +
+
+ {record.method.toUpperCase()} +
+
+ {record.path} +
+
+ ), + }, + { + title: '状态码', + dataIndex: 'statusCode', + width: 110, + render: (statusCode: string) => { + const code = Number(statusCode); + const color = + code >= 500 + ? 'red' + : code >= 400 + ? 'orange' + : code >= 200 + ? 'green' + : 'default'; + return {statusCode}; + }, + }, + { + title: '提交数据', + responsive: ['lg'], + width: 320, + render: (_value, record) => { + const previewData = getPreviewData(record.data); + if (!previewData) { + return -; + } + + const previewText = JSON.stringify(previewData); + return ( + + {JSON.stringify(previewData, null, 2)} + + ), + }} + > + {previewText} + + ); + }, + }, + { + title: '设备信息', + dataIndex: 'userAgent', + responsive: ['xl'], + width: 220, + render: (userAgent: string | undefined, record) => { + const apiToken = getApiTokenLabel(record.apiTokens); + const hasInfo = userAgent || record.ip || apiToken; + if (!hasInfo) { + return -; + } + + return ( +
+ {userAgent &&
{getUA(userAgent)}
} + {record.ip && ( +
IP: {record.ip}
+ )} + {apiToken && ( +
+ API Key:{apiToken} +
+ )} +
+ ); + }, + }, + { + title: '详情', + key: 'detail', + width: 90, + render: (_value, record) => ( + + ), + }, + ]; + return (
-
+
-

+

操作日志

-

+

日志功能从 2025 年 11 月 17 日开始测试,没有更早的数据。将仅保留 180 天内的数据。

-
+
+ } + placeholder="搜索操作、接口、IP、API Key" + onChange={(event) => setSearchInput(event.target.value)} + className="w-full md:w-64" + /> + { + patchSearchParams(setSearchParams, { + status: value === 'all' ? undefined : value, + page: '1', + }); + }} + className="w-full md:w-44" + /> {
+
+ 当前筛选结果 {filteredAuditLogs.length} 条,共载入{' '} + {allAuditLogs.length} 条最近日志。 +
+ `共 ${total} 条记录`, - onChange(page, size) { - if (size) { - setOffset((page - 1) * size); - setPageSize(size); - } + showTotal: isMobile ? undefined : (count) => `共 ${count} 条记录`, + onChange: (page, nextPageSize) => { + patchSearchParams(setSearchParams, { + page: String(page), + pageSize: String(nextPageSize), + }); }, }} size={isMobile ? 'small' : 'middle'} - scroll={{ x: isMobile ? 720 : 1200 }} + scroll={{ x: isMobile ? 860 : 1320 }} + onRow={(record) => ({ + className: 'cursor-pointer', + onClick: () => { + patchSearchParams(setSearchParams, { logId: String(record.id) }); + }, + })} /> + + patchSearchParams(setSearchParams, { logId: undefined })} + > + {selectedLog && ( + + + {selectedLog.path} + + ), + }, + { + key: 'status', + label: '状态码', + children: selectedLog.statusCode, + }, + { + key: 'ip', + label: 'IP', + children: selectedLog.ip || '-', + }, + { + key: 'apiToken', + label: 'API Key', + children: getApiTokenLabel(selectedLog.apiTokens) || '-', + }, + ]} + /> + +
+
提交数据
+
+                {JSON.stringify(
+                  getPreviewData(selectedLog.data) ?? {},
+                  null,
+                  2,
+                )}
+              
+
+ +
+
User-Agent
+
+                {selectedLog.userAgent || '-'}
+              
+
+
+ )} +
); }; diff --git a/src/pages/manage/index.tsx b/src/pages/manage/index.tsx index 315b970..6f8b3a2 100644 --- a/src/pages/manage/index.tsx +++ b/src/pages/manage/index.tsx @@ -1,4 +1,4 @@ -import { SettingFilled } from '@ant-design/icons'; +import { LineChartOutlined, SettingFilled } from '@ant-design/icons'; import { Breadcrumb, Button, @@ -14,11 +14,12 @@ import { Tag, } from 'antd'; -import { Link, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import './manage.css'; import { useEffect } from 'react'; import PlatformIcon from '@/components/platform-icon'; +import { rootRouterPath, router } from '@/router'; import { api } from '@/services/api'; import { useApp } from '@/utils/hooks'; import PackageList from './components/package-list'; @@ -98,6 +99,11 @@ export const Manage = () => { const params = useParams<{ id?: string }>(); const id = Number(params.id!); const { app } = useApp(id); + const realtimeMetricsPath = app?.appKey + ? `${rootRouterPath.realtimeMetrics}?${new URLSearchParams({ + appKey: app.appKey, + }).toString()}` + : undefined; const [form] = Form.useForm(); useEffect(() => { if (app) { @@ -112,7 +118,7 @@ export const Manage = () => { 应用列表, + title: '应用', }, { title: ( @@ -168,6 +174,18 @@ export const Manage = () => { > 应用设置 + diff --git a/src/pages/realtime-metrics.tsx b/src/pages/realtime-metrics.tsx index dd3ba19..72a4895 100644 --- a/src/pages/realtime-metrics.tsx +++ b/src/pages/realtime-metrics.tsx @@ -18,6 +18,7 @@ interface ChartDataPoint { category: string; attribute?: MetricAttribute; isTotal?: boolean; + sharePercent?: number; } type MetricAttribute = 'hash' | 'packageVersion_buildTime'; @@ -85,6 +86,14 @@ const attributeOptions = [ { label: '原生包', value: 'packageVersion_buildTime' }, ]; +const formatTooltipItem = (point: ChartDataPoint) => { + const countLabel = `${point.value.toLocaleString()} 次`; + if (point.isTotal || point.sharePercent === undefined) { + return countLabel; + } + return `${countLabel} (${point.sharePercent.toFixed(1)}%)`; +}; + export const Component = () => { const [searchParams, setSearchParams] = useSearchParams({ attribute: 'hash', @@ -189,9 +198,45 @@ export const Component = () => { }, [data]); const filteredChartData = useMemo(() => { - return chartData.filter( + const selectedPoints = chartData.filter( (point) => point.isTotal || point.attribute === selectedAttribute, ); + const denominators = new Map(); + const fallbackDenominators = new Map(); + + for (const point of selectedPoints) { + if (point.isTotal) { + denominators.set( + point.time, + (denominators.get(point.time) || 0) + point.value, + ); + } else { + fallbackDenominators.set( + point.time, + (fallbackDenominators.get(point.time) || 0) + point.value, + ); + } + } + + for (const [time, value] of fallbackDenominators) { + if (!denominators.has(time)) { + denominators.set(time, value); + } + } + + return selectedPoints.map((point) => { + if (point.isTotal) { + return point; + } + const denominator = denominators.get(point.time) || 0; + if (denominator <= 0) { + return point; + } + return { + ...point, + sharePercent: (point.value / denominator) * 100, + }; + }); }, [chartData, selectedAttribute]); const categoryTotals = useMemo(() => { @@ -290,7 +335,13 @@ export const Component = () => { y: {}, }, tooltip: { - channel: 'y', + title: (point: ChartDataPoint) => dayjs(point.time).format('MM/DD HH:mm'), + items: [ + (point: ChartDataPoint) => ({ + name: point.category, + value: formatTooltipItem(point), + }), + ], }, legend: { position: 'top', @@ -444,8 +495,6 @@ export const Component = () => { }} > {topCategories.map(([category, value], index) => { - const sharePercent = - totalRequests > 0 ? (value / totalRequests) * 100 : 0; const relativePercent = topCategoryMax > 0 ? (value / topCategoryMax) * 100 @@ -472,9 +521,6 @@ export const Component = () => { > TOP {index + 1} - - {sharePercent.toFixed(1)}% -
diff --git a/src/pages/user.tsx b/src/pages/user.tsx index 5f4ff05..8f7b8ac 100644 --- a/src/pages/user.tsx +++ b/src/pages/user.tsx @@ -1,4 +1,5 @@ -import { AlipayCircleOutlined } from '@ant-design/icons'; +import { AlipayCircleOutlined, LogoutOutlined } from '@ant-design/icons'; +import { useQueries } from '@tanstack/react-query'; import type { MenuProps } from 'antd'; import { Button, @@ -7,11 +8,15 @@ import { Grid, message, Popover, + Progress, Spin, + Tag, + Tooltip, } from 'antd'; import { type ReactNode, useState } from 'react'; import { api } from '@/services/api'; -import { useUserInfo } from '@/utils/hooks'; +import { logout } from '@/services/auth'; +import { useAppList, useUserInfo } from '@/utils/hooks'; import { PRICING_LINK } from '../constants/links'; import { quotas } from '../constants/quotas'; @@ -126,8 +131,25 @@ const UpgradeDropdown = ({ function UserPanel() { const { user, displayExpireDay, displayRemainingDays } = useUserInfo(); + const { apps } = useAppList(); const screens = Grid.useBreakpoint(); const isMobile = !screens.md; + const appList = apps ?? []; + const versionCountQueries = useQueries({ + queries: appList.map((app) => ({ + queryKey: ['accountQuotaVersions', app.id], + queryFn: () => api.getVersions({ appId: app.id, limit: 1 }), + staleTime: 60_000, + })), + }); + const packageCountQueries = useQueries({ + queries: appList.map((app) => ({ + queryKey: ['accountQuotaPackages', app.id], + queryFn: () => api.getPackages(app.id), + staleTime: 60_000, + })), + }); + if (!user) { return (
@@ -140,34 +162,88 @@ function UserPanel() { const currentQuota = quota || defaultQuota; const tierDisplay = currentQuota.title; - const quotaTableData = [ - { key: 'app', item: '应用数量', value: `${currentQuota.app} 个` }, + const appCount = appList.length; + const versionCounts = versionCountQueries.map((query) => query.data?.count); + const isVersionCountLoading = versionCountQueries.some( + (query) => query.isLoading, + ); + const packageCounts = packageCountQueries.map( + (query) => query.data?.count ?? query.data?.data?.length, + ); + const isPackageCountLoading = packageCountQueries.some( + (query) => query.isLoading, + ); + const maxVersionCount = Math.max( + 0, + ...versionCounts.map((count) => count ?? 0), + ); + const maxPackageCount = Math.max( + 0, + ...packageCounts.map((count) => count ?? 0), + ); + const remainingChecks = user.checkQuota; + const quotaUsageRows: QuotaUsageRow[] = [ { - key: 'package', - item: '每个应用原生包数量', - value: `${currentQuota.package} 个`, + key: 'app', + label: '应用数量', + limit: currentQuota.app, + note: '当前账户下应用总数', + percent: Math.min(100, (appCount / currentQuota.app) * 100), + status: appCount > currentQuota.app ? 'exception' : 'normal', + value: `${appCount.toLocaleString()} / ${currentQuota.app.toLocaleString()} 个`, }, { - key: 'packageSize', - item: '单个原生包大小', - value: currentQuota.packageSize, + key: 'bundle', + label: '热更包数量', + limit: currentQuota.bundle, + loading: isVersionCountLoading, + note: isVersionCountLoading + ? '正在统计各应用热更包数量' + : '最高单应用使用量', + percent: isVersionCountLoading + ? 0 + : Math.min(100, (maxVersionCount / currentQuota.bundle) * 100), + status: maxVersionCount > currentQuota.bundle ? 'exception' : 'normal', + value: isVersionCountLoading + ? '统计中' + : `${maxVersionCount.toLocaleString()} / ${currentQuota.bundle.toLocaleString()} 个`, }, { - key: 'bundle', - item: '每个应用热更包数量', - value: `${currentQuota.bundle} 个`, + key: 'package', + label: '原生包数量', + limit: currentQuota.package, + loading: isPackageCountLoading, + note: isPackageCountLoading + ? '正在统计各应用原生包数量' + : '最高单应用使用量', + percent: isPackageCountLoading + ? 0 + : Math.min(100, (maxPackageCount / currentQuota.package) * 100), + status: maxPackageCount > currentQuota.package ? 'exception' : 'normal', + value: isPackageCountLoading + ? '统计中' + : `${maxPackageCount.toLocaleString()} / ${currentQuota.package.toLocaleString()} 个`, }, + ]; + const quotaSizeLimits = [ { - key: 'bundleSize', - item: '单个热更包大小', + label: '单个原生包大小', + value: currentQuota.packageSize, + }, + { + label: '单个热更包大小', value: currentQuota.bundleSize, }, { - key: 'pv', - item: '每日总查询次数', - value: `${currentQuota.pv.toLocaleString()} 次`, + label: '检查额度上限', + value: `${currentQuota.pv.toLocaleString()} 次 / 日`, }, ]; + const handleLogout = () => { + message.info('您已退出登录'); + logout(); + }; + return (
-
- {quotaTableData.map(({ key, item, value }) => ( -
-
{item}
-
{value}
-
- ))} -
+
如有定制需求(限高级版以上),请联系 QQ 客服 34731408 @@ -263,11 +336,227 @@ function UserPanel() { > 使用网银转账 + +
+
+ ); +} + +type QuotaUsageRow = { + key: string; + label: string; + limit: number; + loading?: boolean; + note: string; + percent: number; + status: 'exception' | 'normal'; + value: string; +}; + +function QuotaDetailsPanel({ + dailyQuota, + last7dAvg, + last7dCounts, + remainingChecks, + rows, + sizeLimits, +}: { + dailyQuota: number; + last7dAvg?: number; + last7dCounts?: number[]; + remainingChecks?: number; + rows: QuotaUsageRow[]; + sizeLimits: Array<{ label: string; value: string }>; +}) { + const remainingPercent = + typeof remainingChecks === 'number' + ? Math.max(0, Math.min(100, (remainingChecks / dailyQuota) * 100)) + : 0; + const status = + remainingChecks !== undefined && remainingChecks <= 0 + ? 'exception' + : 'normal'; + + return ( +
+
+
+
+
+
每日检查额度
+
+ 客户端检查热更新时消耗,按账户全部应用汇总。 +
+
+ {status === 'exception' && 超额} +
+
+
+
今日剩余额度
+
+ {remainingChecks === undefined + ? dailyQuota.toLocaleString() + : Math.max(0, remainingChecks).toLocaleString()} +
+
+ 上限 {dailyQuota.toLocaleString()} 次 / 日 +
+
+
+
+ +
+
+ +
+ +
+ {rows.map((row) => ( +
+
+
+ {row.label} + {row.status === 'exception' && 超额} +
+
{row.note}
+
+
{row.value}
+ +
+ ))} +
+ +
+
规格限制
+
+ {sizeLimits.map((item) => ( +
+
{item.label}
+
{item.value}
+
+ ))} +
); } +function MiniQuotaBars({ + dailyQuota, + title, + tooltipSuffix, + values, +}: { + dailyQuota: number; + title: string; + tooltipSuffix: string; + values?: number[]; +}) { + const bars = (values ?? []) + .slice(0, 7) + .reverse() + .map((value, index, array) => { + const daysAgo = array.length - index - 1; + return { + daysAgo, + dateLabel: formatQuotaDateLabel(daysAgo), + value, + }; + }); + + return ( +
+
+ {title} +
+ {bars.length > 0 ? ( +
+ {bars.map((bar) => { + const percent = + dailyQuota > 0 + ? Math.max( + bar.value > 0 ? 4 : 0, + Math.min(100, (Math.max(0, bar.value) / dailyQuota) * 100), + ) + : 0; + return ( +
+
+ +
+ +
+ + {bar.dateLabel} + +
+ ); + })} +
+ ) : ( +
+ 暂无 7 天明细 +
+ )} +
+ ); +} + +function formatOptionalNumber(value?: number) { + return typeof value === 'number' ? Math.max(0, value).toLocaleString() : '-'; +} + +function formatQuotaDateLabel(daysAgo: number) { + const date = new Date(); + date.setDate(date.getDate() - daysAgo); + return formatShortQuotaDate(date); +} + +function formatQuotaDateRangeLabel() { + const end = new Date(); + const start = new Date(); + start.setDate(end.getDate() - 6); + return `${formatShortQuotaDate(start)} - ${formatShortQuotaDate(end)}`; +} + +function formatShortQuotaDate(date: Date) { + return `${date.getMonth() + 1}/${date.getDate()}`; +} + async function purchase(tier?: string) { const orderResponse = await api.createOrder({ tier }); if (orderResponse?.payUrl) { diff --git a/src/router.tsx b/src/router.tsx index 029df9b..50596af 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -1,13 +1,12 @@ -import { Alert } from 'antd'; -import { createHashRouter, Navigate, redirect } from 'react-router-dom'; +import { createHashRouter, redirect } from 'react-router-dom'; import { ErrorBoundary } from './components/error-boundary'; import MainLayout from './components/main-layout'; import { getToken } from './services/request'; // import './utils/notice'; export const rootRouterPath = { + apps: '/', user: '/user', - apps: '/apps', versions: (id: string) => `/apps/${id}`, resetPassword: (step: string) => `/reset-password/${step}`, activate: '/activate', @@ -42,13 +41,13 @@ export const needAuthLoader = ({ request }: { request: Request }) => { function resolveAuthenticatedRedirect(loginFrom?: string | null) { if (!loginFrom?.startsWith('/') || loginFrom.startsWith('//')) { - return rootRouterPath.user; + return rootRouterPath.apps; } if ( loginFrom === rootRouterPath.login || loginFrom.startsWith(`${rootRouterPath.login}?`) ) { - return rootRouterPath.user; + return rootRouterPath.apps; } return loginFrom; } @@ -70,8 +69,14 @@ export const router = createHashRouter([ errorElement: , children: [ { - path: '', - element: , + index: true, + loader: needAuthLoader, + lazy: () => import('./pages/apps'), + }, + { + path: 'apps', + loader: needAuthLoader, + lazy: () => import('./pages/apps'), }, { path: 'welcome', @@ -98,11 +103,6 @@ export const router = createHashRouter([ loader: publicOnlyLoader, lazy: () => import('./pages/login'), }, - { - path: 'apps', - loader: needAuthLoader, - element: , - }, { path: 'apps/:id', loader: needAuthLoader, diff --git a/src/services/api.ts b/src/services/api.ts index ab06bdd..39f753e 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -32,17 +32,24 @@ export const api = { getApp: (appId: number) => request('get', `/app/${appId}`), deleteApp: (appId: number) => request('delete', `/app/${appId}`).then(() => { - queryClient.setQueryData(['appList'], ({ data }: { data: App[] }) => ({ - data: data?.filter((i) => i.id !== appId), - })); + queryClient.setQueryData( + ['appList'], + (old?: { data?: App[] } | undefined) => ({ + data: old?.data?.filter((i) => i.id !== appId) ?? [], + }), + ); }), createApp: (params: { name: string; platform: string }) => request<{ id: number }>('post', '/app/create', params).then((response) => { if (!response) throw Error('Failed to create app'); const { id } = response; - queryClient.setQueryData(['appList'], ({ data }: { data: App[] }) => ({ - data: [...(data || []), { ...params, id }], - })); + queryClient.setQueryData( + ['appList'], + (old?: { data?: App[] } | undefined) => ({ + data: [...(old?.data || []), { ...params, id }], + }), + ); + return id; }), updateApp: ( appId: number, @@ -53,9 +60,14 @@ export const api = { ...old, ...params, })); - queryClient.setQueryData(['appList'], ({ data }: { data: App[] }) => ({ - data: data?.map((i) => (i.id === appId ? { ...i, ...params } : i)), - })); + queryClient.setQueryData( + ['appList'], + (old?: { data?: App[] } | undefined) => ({ + data: + old?.data?.map((i) => (i.id === appId ? { ...i, ...params } : i)) ?? + [], + }), + ); }), // package getPackages: (appId: number) => diff --git a/src/services/auth.ts b/src/services/auth.ts index afed641..08e3f1f 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -18,13 +18,13 @@ function getSearchParam(name: string) { function resolveLoginFrom(loginFrom?: string | null) { if (!loginFrom?.startsWith('/') || loginFrom.startsWith('//')) { - return rootRouterPath.user; + return rootRouterPath.apps; } if ( loginFrom === rootRouterPath.login || loginFrom.startsWith(`${rootRouterPath.login}?`) ) { - return rootRouterPath.user; + return rootRouterPath.apps; } return loginFrom; } diff --git a/src/types.ts b/src/types.ts index 5281d9e..c4bcdf1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,6 +16,7 @@ export interface User { tierExpiresAt?: string; checkQuota?: number; last7dAvg?: number; + last7dCounts?: number[]; quota?: Quota; admin?: boolean; } @@ -128,10 +129,6 @@ export interface AppDetail extends App { downloadUrl?: string; } -export interface SiderMenuProps { - selectedKeys?: string[]; -} - export interface ContentProps { app: App; } diff --git a/src/utils/helper.ts b/src/utils/helper.ts index c1cbf53..4d0987f 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -99,3 +99,37 @@ export const patchSearchParams = ( return next; }, navigateOptions); }; + +const RECENT_APP_STORAGE_KEY = 'pushy_recent_app_ids'; +const MAX_RECENT_APP_COUNT = 6; + +export const getRecentAppIds = () => { + if (typeof window === 'undefined') { + return [] as number[]; + } + + try { + const parsed = JSON.parse( + window.localStorage.getItem(RECENT_APP_STORAGE_KEY) ?? '[]', + ); + if (!Array.isArray(parsed)) { + return []; + } + return parsed.filter((value): value is number => Number.isInteger(value)); + } catch { + return []; + } +}; + +export const rememberRecentApp = (appId: number) => { + if (typeof window === 'undefined' || !Number.isInteger(appId)) { + return [] as number[]; + } + + const next = [appId, ...getRecentAppIds().filter((id) => id !== appId)].slice( + 0, + MAX_RECENT_APP_COUNT, + ); + window.localStorage.setItem(RECENT_APP_STORAGE_KEY, JSON.stringify(next)); + return next; +}; diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index 7d520e6..de4fbc6 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -205,11 +205,11 @@ export const useUserInfo = () => { }; export const useAppList = () => { - const { data } = useQuery({ + const { data, isLoading } = useQuery({ queryKey: ['appList'], queryFn: api.appList, }); - return { apps: data?.data }; + return { apps: data?.data, isLoading }; }; export const useApp = (appId: number) => {