From 5f93573e57ca1ba0ba3e7a586cf39a98990a0029 Mon Sep 17 00:00:00 2001 From: "guangfei.zhao" Date: Thu, 9 Oct 2025 17:23:15 +0800 Subject: [PATCH] feat: implement RAG dashboard with MUI and react-router Add new RAG dashboard feature including: - Main application layout with header and sidebar - Multiple pages (Home, KnowledgeBase, PipelineConfig, Dashboard, MCP) - Theme configuration and styling - Routing setup with protected routes - Login page and authentication flow - Various UI components and mock data for dashboard views --- package.json | 8 +- pnpm-lock.yaml | 602 +++++++++++++++++++++++++++ src/App.tsx | 42 +- src/components/Layout/Header.tsx | 73 ++++ src/components/Layout/MainLayout.tsx | 39 ++ src/components/Layout/Sidebar.tsx | 73 ++++ src/pages/Dashboard.tsx | 369 ++++++++++++++++ src/pages/Home.tsx | 218 ++++++++++ src/pages/KnowledgeBaseList.tsx | 265 ++++++++++++ src/pages/Login.tsx | 262 ++++++++++++ src/pages/MCP.tsx | 516 +++++++++++++++++++++++ src/pages/ModelsResources.tsx | 540 ++++++++++++++++++++++++ src/pages/PipelineConfig.tsx | 349 ++++++++++++++++ src/routes/index.tsx | 32 ++ src/theme/index.ts | 164 ++++++++ 15 files changed, 3521 insertions(+), 31 deletions(-) create mode 100644 src/components/Layout/Header.tsx create mode 100644 src/components/Layout/MainLayout.tsx create mode 100644 src/components/Layout/Sidebar.tsx create mode 100644 src/pages/Dashboard.tsx create mode 100644 src/pages/Home.tsx create mode 100644 src/pages/KnowledgeBaseList.tsx create mode 100644 src/pages/Login.tsx create mode 100644 src/pages/MCP.tsx create mode 100644 src/pages/ModelsResources.tsx create mode 100644 src/pages/PipelineConfig.tsx create mode 100644 src/routes/index.tsx create mode 100644 src/theme/index.ts diff --git a/package.json b/package.json index 92980b6..4ce7a09 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,14 @@ "preview": "vite preview" }, "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.4", + "@mui/material": "^7.3.4", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-is": "18.3.1", + "react-router-dom": "^7.9.4" }, "devDependencies": { "@eslint/js": "^9.36.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8af5be..7762c4c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,30 @@ importers: .: dependencies: + '@emotion/react': + specifier: ^11.14.0 + version: 11.14.0(@types/react@19.2.2)(react@18.3.1) + '@emotion/styled': + specifier: ^11.14.1 + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1) + '@mui/icons-material': + specifier: ^7.3.4 + version: 7.3.4(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.2.2)(react@18.3.1) + '@mui/material': + specifier: ^7.3.4 + version: 7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-is: + specifier: 18.3.1 + version: 18.3.1 + react-router-dom: + specifier: ^7.9.4 + version: 7.9.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@eslint/js': specifier: ^9.36.0 @@ -125,6 +143,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -137,6 +159,60 @@ packages: resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@esbuild/aix-ppc64@0.25.10': resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} engines: {node: '>=18'} @@ -363,6 +439,97 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mui/core-downloads-tracker@7.3.4': + resolution: {integrity: sha512-BIktMapG3r4iXwIhYNpvk97ZfYWTreBBQTWjQKbNbzI64+ULHfYavQEX2w99aSWHS58DvXESWIgbD9adKcUOBw==} + + '@mui/icons-material@7.3.4': + resolution: {integrity: sha512-9n6Xcq7molXWYb680N2Qx+FRW8oT6j/LXF5PZFH3ph9X/Rct0B/BlLAsFI7iL9ySI6LVLuQIVtrLiPT82R7OZw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^7.3.4 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@7.3.4': + resolution: {integrity: sha512-gEQL9pbJZZHT7lYJBKQCS723v1MGys2IFc94COXbUIyCTWa+qC77a7hUax4Yjd5ggEm35dk4AyYABpKKWC4MLw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^7.3.3 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@7.3.3': + resolution: {integrity: sha512-OJM+9nj5JIyPUvsZ5ZjaeC9PfktmK+W5YaVLToLR8L0lB/DGmv1gcKE43ssNLSvpoW71Hct0necfade6+kW3zQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@7.3.3': + resolution: {integrity: sha512-CmFxvRJIBCEaWdilhXMw/5wFJ1+FT9f3xt+m2pPXhHPeVIbBg9MnMvNSJjdALvnQJMPw8jLhrUtXmN7QAZV2fw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@7.3.3': + resolution: {integrity: sha512-Lqq3emZr5IzRLKaHPuMaLBDVaGvxoh6z7HMWd1RPKawBM5uMRaQ4ImsmmgXWtwJdfZux5eugfDhXJUo2mliS8Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.4.7': + resolution: {integrity: sha512-8vVje9rdEr1rY8oIkYgP+Su5Kwl6ik7O3jQ0wl78JGSmiZhRHV+vkjooGdKD8pbtZbutXFVTWQYshu2b3sG9zw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@7.3.3': + resolution: {integrity: sha512-kwNAUh7bLZ7mRz9JZ+6qfRnnxbE4Zuc+RzXnhSpRSxjTlSTj7b4JxRLXpG+MVtPVtqks5k/XC8No1Vs3x4Z2gg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -375,6 +542,9 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@rolldown/pluginutils@1.0.0-beta.38': resolution: {integrity: sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==} @@ -509,11 +679,22 @@ packages: '@types/node@24.7.0': resolution: {integrity: sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + '@types/react-dom@19.2.1': resolution: {integrity: sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==} peerDependencies: '@types/react': ^19.2.0 + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + '@types/react@19.2.2': resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} @@ -602,6 +783,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -635,6 +820,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -645,9 +834,20 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -667,9 +867,15 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + electron-to-chromium@1.5.233: resolution: {integrity: sha512-iUdTQSf7EFXsDdQsp8MwJz5SVk4APEFqXU/S47OtQ0YLqacSwPXdZ5vRlMX3neb07Cy2vgioNuRnWUXFwuslkg==} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + esbuild@0.25.10: resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} @@ -769,6 +975,9 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -785,6 +994,9 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -812,6 +1024,13 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -828,6 +1047,13 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -858,6 +1084,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -876,6 +1105,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -919,6 +1151,10 @@ packages: node-releases@2.0.23: resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -935,6 +1171,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -943,6 +1183,13 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -962,6 +1209,9 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -974,10 +1224,42 @@ packages: peerDependencies: react: ^18.3.1 + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.0: + resolution: {integrity: sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==} + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} + react-router-dom@7.9.4: + resolution: {integrity: sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.9.4: + resolution: {integrity: sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -986,6 +1268,11 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1010,6 +1297,9 @@ packages: engines: {node: '>=10'} hasBin: true + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1022,14 +1312,25 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -1124,6 +1425,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1219,6 +1524,8 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 + '@babel/runtime@7.28.4': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -1242,6 +1549,89 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.28.4 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.2 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@19.2.2)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.2 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.25.10': optional: true @@ -1396,6 +1786,93 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@mui/core-downloads-tracker@7.3.4': {} + + '@mui/icons-material@7.3.4(@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.2.2)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/material': 7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.2 + + '@mui/material@7.3.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/core-downloads-tracker': 7.3.4 + '@mui/system': 7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1) + '@mui/types': 7.4.7(@types/react@19.2.2) + '@mui/utils': 7.3.3(@types/react@19.2.2)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@19.2.2) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 19.2.0 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.2)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1) + '@types/react': 19.2.2 + + '@mui/private-theming@7.3.3(@types/react@19.2.2)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/utils': 7.3.3(@types/react@19.2.2)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.2 + + '@mui/styled-engine@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.2)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1) + + '@mui/system@7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/private-theming': 7.3.3(@types/react@19.2.2)(react@18.3.1) + '@mui/styled-engine': 7.3.3(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.4.7(@types/react@19.2.2) + '@mui/utils': 7.3.3(@types/react@19.2.2)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.2)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@18.3.1))(@types/react@19.2.2)(react@18.3.1) + '@types/react': 19.2.2 + + '@mui/types@7.4.7(@types/react@19.2.2)': + dependencies: + '@babel/runtime': 7.28.4 + optionalDependencies: + '@types/react': 19.2.2 + + '@mui/utils@7.3.3(@types/react@19.2.2)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/types': 7.4.7(@types/react@19.2.2) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.2.0 + optionalDependencies: + '@types/react': 19.2.2 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1408,6 +1885,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@popperjs/core@2.11.8': {} + '@rolldown/pluginutils@1.0.0-beta.38': {} '@rollup/rollup-android-arm-eabi@4.52.4': @@ -1505,10 +1984,18 @@ snapshots: dependencies: undici-types: 7.14.0 + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.15': {} + '@types/react-dom@19.2.1(@types/react@19.2.2)': dependencies: '@types/react': 19.2.2 + '@types/react-transition-group@4.4.12(@types/react@19.2.2)': + dependencies: + '@types/react': 19.2.2 + '@types/react@19.2.2': dependencies: csstype: 3.1.3 @@ -1637,6 +2124,12 @@ snapshots: argparse@2.0.1: {} + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.28.4 + cosmiconfig: 7.1.0 + resolve: 1.22.10 + balanced-match@1.0.2: {} baseline-browser-mapping@2.8.14: {} @@ -1671,6 +2164,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -1679,8 +2174,20 @@ snapshots: concat-map@0.0.1: {} + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} + cookie@1.0.2: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -1695,8 +2202,17 @@ snapshots: deep-is@0.1.4: {} + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.4 + csstype: 3.1.3 + electron-to-chromium@1.5.233: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + esbuild@0.25.10: optionalDependencies: '@esbuild/aix-ppc64': 0.25.10 @@ -1835,6 +2351,8 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-root@1.1.0: {} + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -1850,6 +2368,8 @@ snapshots: fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + gensync@1.0.0-beta.2: {} glob-parent@5.1.2: @@ -1868,6 +2388,14 @@ snapshots: has-flag@4.0.0: {} + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + ignore@5.3.2: {} ignore@7.0.5: {} @@ -1879,6 +2407,12 @@ snapshots: imurmurhash@0.1.4: {} + is-arrayish@0.2.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-extglob@2.1.1: {} is-glob@4.0.3: @@ -1899,6 +2433,8 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -1914,6 +2450,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -1951,6 +2489,8 @@ snapshots: node-releases@2.0.23: {} + object-assign@4.1.1: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -1972,10 +2512,21 @@ snapshots: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + path-exists@4.0.0: {} path-key@3.1.1: {} + path-parse@1.0.7: {} + + path-type@4.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -1990,6 +2541,12 @@ snapshots: prelude-ls@1.2.1: {} + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -2000,14 +2557,49 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-is@19.2.0: {} + react-refresh@0.17.0: {} + react-router-dom@7.9.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.9.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-router@7.9.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.0.2 + react: 18.3.1 + set-cookie-parser: 2.7.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 resolve-from@4.0.0: {} + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} rollup@4.52.4: @@ -2050,6 +2642,8 @@ snapshots: semver@7.7.3: {} + set-cookie-parser@2.7.1: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -2058,12 +2652,18 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.5.7: {} + strip-json-comments@3.1.1: {} + stylis@4.2.0: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -2126,4 +2726,6 @@ snapshots: yallist@3.1.1: {} + yaml@1.10.2: {} + yocto-queue@0.1.0: {} diff --git a/src/App.tsx b/src/App.tsx index 3d7ded3..50e65f3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,35 +1,17 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import { BrowserRouter } from 'react-router-dom'; +import { CssBaseline, ThemeProvider } from '@mui/material'; +import { theme } from './theme'; +import AppRoutes from './routes'; function App() { - const [count, setCount] = useState(0) - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) + + + + + + + ); } -export default App +export default App; diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx new file mode 100644 index 0000000..a704839 --- /dev/null +++ b/src/components/Layout/Header.tsx @@ -0,0 +1,73 @@ +import { Box, InputBase, styled } from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; + +const HeaderContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + padding: '0 20px', + height: '60px', + backgroundColor: '#FFFFFF', + color: '#333', + boxShadow: '0 1px 3px rgba(0,0,0,0.1)', + borderBottom: '1px solid #E5E5E5', +})); + +const BrandTitle = styled(Box)(({ theme }) => ({ + fontSize: '1.2rem', + fontWeight: 'bold', + color: '#333', +})); + +const SearchBox = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + backgroundColor: '#F8F9FA', + borderRadius: '6px', + padding: '6px 12px', + width: '320px', + border: '1px solid #E5E5E5', + marginLeft: 'auto', + marginRight: '20px', + '&:focus-within': { + borderColor: theme.palette.primary.main, + boxShadow: `0 0 0 2px rgba(226,0,116,0.1)`, + }, + '& .MuiInputBase-input': { + border: 'none', + outline: 'none', + backgroundColor: 'transparent', + fontSize: '14px', + color: '#333', + '&::placeholder': { + color: '#999', + }, + }, +})); + +const SearchInput = styled(InputBase)(({ theme }) => ({ + marginLeft: '8px', + flex: 1, + fontSize: '0.875rem', +})); + +const UserAvatar = styled(AccountCircleIcon)(({ theme }) => ({ + color: '#666', + cursor: 'pointer', + fontSize: '2rem', +})); + +const Header = () => { + return ( + + RAG Dashboard + + + + + + + ); +}; + +export default Header; \ No newline at end of file diff --git a/src/components/Layout/MainLayout.tsx b/src/components/Layout/MainLayout.tsx new file mode 100644 index 0000000..91f4027 --- /dev/null +++ b/src/components/Layout/MainLayout.tsx @@ -0,0 +1,39 @@ +import { Box, styled } from '@mui/material'; +import { Outlet } from 'react-router-dom'; +import Header from './Header'; +import Sidebar from './Sidebar'; + +const LayoutContainer = styled(Box)({ + display: 'flex', + height: '100vh', +}); + +const MainContent = styled(Box)({ + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', +}); + +const ContentArea = styled(Box)({ + flex: 1, + padding: '20px', + overflow: 'auto', + backgroundColor: '#f5f7fa', +}); + +const MainLayout = () => { + return ( + + + +
+ + + + + + ); +}; + +export default MainLayout; \ No newline at end of file diff --git a/src/components/Layout/Sidebar.tsx b/src/components/Layout/Sidebar.tsx new file mode 100644 index 0000000..271fe09 --- /dev/null +++ b/src/components/Layout/Sidebar.tsx @@ -0,0 +1,73 @@ +import { Box, List, ListItem, ListItemButton, ListItemText, Typography, styled } from '@mui/material'; +import { Link, useLocation } from 'react-router-dom'; + +const SidebarContainer = styled(Box)(({ theme }) => ({ + width: '240px', + backgroundColor: '#1E1E24', + color: '#DDD', + height: '100vh', + padding: '1rem 0', + display: 'flex', + flexDirection: 'column', +})); + +const Logo = styled(Typography)(({ theme }) => ({ + fontSize: '1.05rem', + fontWeight: 600, + padding: '0 1.25rem 1rem', + margin: '0 0 0.5rem', + color: '#FFF', + letterSpacing: '0.5px', +})); + +const NavItem = styled(ListItemButton)<{ active?: boolean }>(({ active, theme }) => ({ + color: active ? '#FFF' : '#B9B9C2', + backgroundColor: active ? 'rgba(226,0,116,0.12)' : 'transparent', + borderLeft: active ? `4px solid ${theme.palette.primary.main}` : '4px solid transparent', + fontWeight: active ? 600 : 'normal', + '&:hover': { + backgroundColor: 'rgba(255,255,255,0.05)', + color: '#FFF', + }, + '& .MuiListItemText-primary': { + fontSize: '0.9rem', + }, +})); + +const Footer = styled(Box)(({ theme }) => ({ + marginTop: 'auto', + padding: '20px', + fontSize: '0.75rem', + opacity: 0.7, +})); + +const navItems = [ + { text: 'Overview', path: '/' }, + { text: 'Knowledge Bases', path: '/kb-list' }, + { text: 'RAG Pipeline', path: '/pipeline-config' }, + { text: 'Operations', path: '/dashboard' }, + { text: 'Models & Resources', path: '/models-resources' }, + { text: 'MCP', path: '/mcp' }, +]; + +const Sidebar = () => { + const location = useLocation(); + + return ( + + RAGflow Prototype + + {navItems.map((item) => ( + + + + + + ))} + +
© 2025 RAG Demo
+
+ ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx new file mode 100644 index 0000000..f675e66 --- /dev/null +++ b/src/pages/Dashboard.tsx @@ -0,0 +1,369 @@ +import React, { useState } from 'react'; +import { + Box, + Typography, + Card, + CardContent, + Grid, + Button, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + Chip, + LinearProgress, + Select, + MenuItem, + FormControl, + InputLabel, +} from '@mui/material'; +import { + TrendingUp as TrendingUpIcon, + TrendingDown as TrendingDownIcon, + Assessment as AssessmentIcon, + Speed as SpeedIcon, + Error as ErrorIcon, + CheckCircle as CheckCircleIcon, +} from '@mui/icons-material'; +import { styled } from '@mui/material/styles'; + +const PageContainer = styled(Box)(({ theme }) => ({ + padding: '1.5rem', + backgroundColor: '#F8F9FA', + minHeight: 'calc(100vh - 60px)', +})); + +const PageHeader = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: '1.5rem', +})); + +const MetricCard = styled(Card)(({ theme }) => ({ + height: '100%', + border: '1px solid #E5E5E5', + transition: 'all 0.2s ease-in-out', + '&:hover': { + boxShadow: '0 4px 12px rgba(0,0,0,0.1)', + }, +})); + +const MetricValue = styled(Typography)(({ theme }) => ({ + fontSize: '2rem', + fontWeight: 700, + lineHeight: 1.2, +})); + +const TrendIndicator = styled(Box)<{ trend: 'up' | 'down' }>(({ trend, theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: '0.25rem', + color: trend === 'up' ? '#28A745' : '#DC3545', + fontSize: '0.875rem', + fontWeight: 500, +})); + +const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({ + fontSize: '0.75rem', + height: '24px', + backgroundColor: + status === 'success' ? '#E8F5E8' : + status === 'warning' ? '#FFF3CD' : + status === 'error' ? '#F8D7DA' : '#F8F9FA', + color: + status === 'success' ? '#155724' : + status === 'warning' ? '#856404' : + status === 'error' ? '#721C24' : '#666', +})); + +const mockMetrics = { + totalQueries: { value: 12847, trend: 'up', change: '+12.5%' }, + avgResponseTime: { value: '2.3s', trend: 'down', change: '-8.2%' }, + successRate: { value: '98.7%', trend: 'up', change: '+0.3%' }, + activeUsers: { value: 1256, trend: 'up', change: '+5.7%' }, +}; + +const mockRecentQueries = [ + { + id: 1, + query: '如何配置RAG Pipeline的参数?', + user: 'user@example.com', + status: 'success', + responseTime: '1.8s', + timestamp: '2024-01-15 14:30:25', + knowledgeBase: '产品文档库', + }, + { + id: 2, + query: '客服系统的常见问题有哪些?', + user: 'admin@company.com', + status: 'success', + responseTime: '2.1s', + timestamp: '2024-01-15 14:28:15', + knowledgeBase: '客服FAQ', + }, + { + id: 3, + query: '法律合规要求的详细说明', + user: 'legal@company.com', + status: 'warning', + responseTime: '4.2s', + timestamp: '2024-01-15 14:25:10', + knowledgeBase: '法律合规', + }, + { + id: 4, + query: '员工培训流程和要求', + user: 'hr@company.com', + status: 'error', + responseTime: 'N/A', + timestamp: '2024-01-15 14:22:45', + knowledgeBase: '培训资料', + }, +]; + +const Dashboard: React.FC = () => { + const [timeRange, setTimeRange] = useState('24h'); + + return ( + + + + 运营监控 + + + 时间范围 + + + + + {/* 关键指标卡片 */} + + + + + + + + 总查询数 + + + {mockMetrics.totalQueries.value.toLocaleString()} + + + {mockMetrics.totalQueries.trend === 'up' ? + : + + } + {mockMetrics.totalQueries.change} + + + + + + + + + + + + + + + 平均响应时间 + + + {mockMetrics.avgResponseTime.value} + + + {mockMetrics.avgResponseTime.trend === 'up' ? + : + + } + {mockMetrics.avgResponseTime.change} + + + + + + + + + + + + + + + 成功率 + + + {mockMetrics.successRate.value} + + + {mockMetrics.successRate.trend === 'up' ? + : + + } + {mockMetrics.successRate.change} + + + + + + + + + + + + + + + 活跃用户 + + + {mockMetrics.activeUsers.value.toLocaleString()} + + + {mockMetrics.activeUsers.trend === 'up' ? + : + + } + {mockMetrics.activeUsers.change} + + + + + + + + + + {/* 系统状态 */} + + + + + + 系统状态 + + + + CPU 使用率 + 45% + + + + + + 内存使用率 + 67% + + + + + + 存储使用率 + 23% + + + + + + + + + + + + 知识库状态 + + + + 产品文档库 + + + + 客服FAQ + + + + 法律合规 + + + + 培训资料 + + + + + + + + + {/* 最近查询记录 */} + + + + 最近查询记录 + + + + + + 查询内容 + 用户 + 知识库 + 状态 + 响应时间 + 时间 + + + + {mockRecentQueries.map((query) => ( + + + + {query.query} + + + {query.user} + {query.knowledgeBase} + + + + {query.responseTime} + + + {query.timestamp} + + + + ))} + +
+
+
+
+
+ ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..27431bf --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,218 @@ +import { + Box, + Button, + Card, + CardContent, + Grid, + LinearProgress, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, + styled +} from '@mui/material'; + +const StyledCard = styled(Card)({ + height: '100%', + display: 'flex', + flexDirection: 'column', + boxShadow: '0 2px 8px rgba(0,0,0,0.1)', + borderRadius: '8px', +}); + +const CardTitle = styled(Typography)({ + fontSize: '1rem', + fontWeight: 'bold', + marginBottom: '16px', +}); + +const MetricsContainer = styled(Box)({ + display: 'flex', + justifyContent: 'space-between', + marginBottom: '16px', +}); + +const Metric = styled(Box)({ + display: 'flex', + flexDirection: 'column', +}); + +const MetricValue = styled(Typography)({ + fontSize: '1.5rem', + fontWeight: 'bold', +}); + +const MetricLabel = styled(Typography)({ + fontSize: '0.75rem', + color: '#666', +}); + +const ProgressContainer = styled(Box)({ + marginBottom: '16px', +}); + +const ProgressLabel = styled(Box)({ + display: 'flex', + justifyContent: 'space-between', + fontSize: '0.65rem', + marginBottom: '4px', +}); + +const StyledLinearProgress = styled(LinearProgress)({ + height: '8px', + borderRadius: '4px', +}); + +const StatusPill = styled(Box)<{ status?: string }>(({ status }) => ({ + display: 'inline-block', + padding: '4px 8px', + borderRadius: '12px', + fontSize: '0.65rem', + fontWeight: 'bold', + backgroundColor: status === 'OK' ? '#e6f7ed' : '#ffebee', + color: status === 'OK' ? '#00a389' : '#d32f2f', +})); + +const InlineNote = styled('span')({ + fontSize: '0.75rem', + color: '#666', + fontWeight: 'normal', + marginLeft: '4px', +}); + +// 模拟数据 +const recentQueries = [ + { query: 'How to reset device firmware?', latency: 732, source: 'manual.pdf', time: '09:21', status: 'OK' }, + { query: 'List authentication failure codes', latency: 801, source: 'auth_guide.html', time: '09:18', status: 'OK' }, + { query: 'Can we purge stale vectors?', latency: 915, source: 'system_kb', time: '09:10', status: 'OK' }, + { query: 'Explain retrieval scoring logic', latency: 845, source: 'design_notes', time: '08:57', status: 'OK' }, + { query: 'Pipeline concurrency limits?', latency: 1042, source: 'ops_doc', time: '08:43', status: 'OK' }, +]; + +const Home = () => { + return ( + + + {/* Knowledge Base Status Card */} + + + + Knowledge Base Status + + + Documents + 4,218 + + + Sources + 17 + + + Vectors + 1.2M + + + + + + Sync Progress + 62% + + + + + + + + + + {/* Recent Activity Card */} + + + + + Recent Activity (latest 24h) + + + 152 user queries processed + 87 new documents ingested + 4 pipeline adjustments + + + Latency stable at p95 820ms + + + + + + {/* Model Overview Card */} + + + + Model Overview + + Embedding Model: text-embedding-3-large + Generator: gpt-4o-mini + Reranker: cross-encoder-v2 + Chunking: 512 tokens + Retriever Top-K: 8 + + Healthy + + + + + {/* Recent RAG Queries Table */} + + + + + Recent RAG Queries (latest 5) + + + + + + Query + Latency (ms) + Source + Time + Status + + + + {recentQueries.map((row, index) => ( + + {row.query} + {row.latency} + {row.source} + {row.time} + + {row.status} + + + ))} + +
+
+
+
+
+
+
+ ); +}; + +export default Home; \ No newline at end of file diff --git a/src/pages/KnowledgeBaseList.tsx b/src/pages/KnowledgeBaseList.tsx new file mode 100644 index 0000000..2cb1364 --- /dev/null +++ b/src/pages/KnowledgeBaseList.tsx @@ -0,0 +1,265 @@ +import React, { useState } from 'react'; +import { + Box, + Typography, + Button, + Card, + CardContent, + Grid, + Chip, + IconButton, + Menu, + MenuItem, + TextField, + InputAdornment, + Fab, +} from '@mui/material'; +import { + Search as SearchIcon, + Add as AddIcon, + MoreVert as MoreVertIcon, + Folder as FolderIcon, + Description as DocumentIcon, +} from '@mui/icons-material'; +import { styled } from '@mui/material/styles'; + +const PageContainer = styled(Box)(({ theme }) => ({ + padding: '1.5rem', + backgroundColor: '#F8F9FA', + minHeight: 'calc(100vh - 60px)', +})); + +const PageHeader = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: '1.5rem', +})); + +const SearchContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + gap: '1rem', + marginBottom: '1.5rem', +})); + +const KBCard = styled(Card)(({ theme }) => ({ + height: '100%', + transition: 'all 0.2s ease-in-out', + border: '1px solid #E5E5E5', + '&:hover': { + boxShadow: '0 4px 12px rgba(0,0,0,0.1)', + transform: 'translateY(-2px)', + }, +})); + +const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({ + fontSize: '0.75rem', + height: '24px', + backgroundColor: + status === 'active' ? '#E8F5E8' : + status === 'processing' ? '#FFF3CD' : '#F8D7DA', + color: + status === 'active' ? '#155724' : + status === 'processing' ? '#856404' : '#721C24', +})); + +const StatsBox = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + marginTop: '1rem', + padding: '0.75rem', + backgroundColor: '#F8F9FA', + borderRadius: '6px', +})); + +const StatItem = styled(Box)(({ theme }) => ({ + textAlign: 'center', + '& .number': { + fontSize: '1.25rem', + fontWeight: 600, + color: theme.palette.primary.main, + }, + '& .label': { + fontSize: '0.75rem', + color: '#666', + marginTop: '0.25rem', + }, +})); + +const mockKnowledgeBases = [ + { + id: 1, + name: '产品文档库', + description: '包含所有产品相关的技术文档和用户手册', + status: 'active', + documents: 156, + size: '2.3 GB', + lastUpdated: '2024-01-15', + }, + { + id: 2, + name: '客服FAQ', + description: '常见问题解答和客服对话记录', + status: 'processing', + documents: 89, + size: '1.1 GB', + lastUpdated: '2024-01-14', + }, + { + id: 3, + name: '法律合规', + description: '法律条文、合规要求和相关政策文档', + status: 'active', + documents: 234, + size: '3.7 GB', + lastUpdated: '2024-01-13', + }, + { + id: 4, + name: '培训资料', + description: '员工培训材料和学习资源', + status: 'inactive', + documents: 67, + size: '890 MB', + lastUpdated: '2024-01-10', + }, +]; + +const KnowledgeBaseList: React.FC = () => { + const [searchTerm, setSearchTerm] = useState(''); + const [anchorEl, setAnchorEl] = useState(null); + const [selectedKB, setSelectedKB] = useState(null); + + const handleMenuClick = (event: React.MouseEvent, kbId: number) => { + setAnchorEl(event.currentTarget); + setSelectedKB(kbId); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + setSelectedKB(null); + }; + + const filteredKBs = mockKnowledgeBases.filter(kb => + kb.name.toLowerCase().includes(searchTerm.toLowerCase()) || + kb.description.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + return ( + + + + 知识库管理 + + + + + + setSearchTerm(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ width: '400px' }} + /> + + + + {filteredKBs.map((kb) => ( + + + + + + + + {kb.name} + + + + + handleMenuClick(e, kb.id)} + > + + + + + + + {kb.description} + + + + +
{kb.documents}
+
文档数量
+
+ +
{kb.size}
+
存储大小
+
+
+ + + 最后更新: {kb.lastUpdated} + +
+
+
+ ))} +
+ + + 编辑 + 查看详情 + 导出 + + 删除 + + + + + + +
+ ); +}; + +export default KnowledgeBaseList; \ No newline at end of file diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx new file mode 100644 index 0000000..f9bdb85 --- /dev/null +++ b/src/pages/Login.tsx @@ -0,0 +1,262 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Box, + Button, + Checkbox, + Container, + FormControlLabel, + TextField, + Typography, + Link, + styled +} from '@mui/material'; + +const LoginContainer = styled(Box)({ + height: '100vh', + display: 'flex', + flexDirection: 'column', + backgroundColor: '#f5f7fa', +}); + +const TopBar = styled(Box)({ + height: '60px', + backgroundColor: '#1a1a2e', + display: 'flex', + alignItems: 'center', + padding: '0 20px', +}); + +const BrandLogo = styled(Box)({ + width: '40px', + height: '40px', + backgroundColor: '#fff', + color: '#1a1a2e', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + borderRadius: '50%', + fontWeight: 'bold', + fontSize: '1.2rem', +}); + +const LoginMain = styled(Box)({ + flex: 1, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}); + +const LoginCard = styled(Box)({ + width: '400px', + backgroundColor: '#fff', + borderRadius: '8px', + boxShadow: '0 4px 12px rgba(0,0,0,0.1)', + padding: '30px', +}); + +const ServiceName = styled(Typography)({ + fontSize: '0.75rem', + color: '#666', + marginBottom: '10px', +}); + +const LoginTitle = styled(Typography)({ + fontSize: '1.5rem', + fontWeight: 'bold', + marginBottom: '20px', +}); + +const LoginForm = styled('form')({ + display: 'flex', + flexDirection: 'column', + gap: '20px', +}); + +const RememberRow = styled(Box)({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +}); + +const ActionButtons = styled(Box)({ + display: 'flex', + gap: '10px', + marginTop: '10px', +}); + +const PrimaryButton = styled(Button)({ + backgroundColor: '#e20074', + '&:hover': { + backgroundColor: '#c10062', + }, +}); + +const SecondaryButton = styled(Button)({ + color: '#333', + backgroundColor: '#f5f5f5', + '&:hover': { + backgroundColor: '#e0e0e0', + }, +}); + +const HelpSection = styled(Box)({ + marginTop: '20px', + textAlign: 'center', + fontSize: '0.875rem', +}); + +const SocialLogin = styled(Box)({ + display: 'flex', + justifyContent: 'center', + gap: '10px', + marginTop: '15px', +}); + +const SocialButton = styled(Box)({ + width: '30px', + height: '30px', + borderRadius: '50%', + backgroundColor: '#e0e0e0', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + cursor: 'pointer', +}); + +const Footer = styled(Box)({ + height: '50px', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + padding: '0 20px', + borderTop: '1px solid #eee', + fontSize: '0.75rem', + color: '#666', +}); + +const LegalLinks = styled(Box)({ + display: 'flex', + gap: '15px', +}); + +const Login = () => { + const [username, setUsername] = useState(''); + const [rememberUser, setRememberUser] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(false); + const navigate = useNavigate(); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (!username.trim()) { + setError(true); + return; + } + + setIsSubmitting(true); + setError(false); + + // 模拟登录过程 + setTimeout(() => { + navigate('/'); + }, 800); + }; + + const handleCancel = () => { + navigate('/'); + }; + + return ( + + + T + + + + + Servicename + + Enter Login
Username +
+ + + setUsername(e.target.value)} + error={error} + required + /> + + + setRememberUser(e.target.checked)} + id="rememberUser" + /> + } + label="Remember username" + /> + + Forgot your username or password? + + + + + + {isSubmitting ? 'Processing...' : 'Next'} + + + Cancel + + + + + + Do you need help? + + No account? Sign up or log in with your social network account. + + + + + + f + + + t + + +
+
+ +
+ © Deutsche Telekom AG + + Imprint + Data privacy + +
+
+ ); +}; + +export default Login; \ No newline at end of file diff --git a/src/pages/MCP.tsx b/src/pages/MCP.tsx new file mode 100644 index 0000000..5e916a6 --- /dev/null +++ b/src/pages/MCP.tsx @@ -0,0 +1,516 @@ +import React, { useState } from 'react'; +import { + Box, + Typography, + Card, + CardContent, + Grid, + Button, + Switch, + FormControlLabel, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Chip, + IconButton, + Menu, + MenuItem, + TextField, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Alert, + Tabs, + Tab, +} from '@mui/material'; +import { + Add as AddIcon, + MoreVert as MoreVertIcon, + Settings as SettingsIcon, + Link as LinkIcon, + Security as SecurityIcon, + Speed as SpeedIcon, + CheckCircle as CheckCircleIcon, + Error as ErrorIcon, +} from '@mui/icons-material'; +import { styled } from '@mui/material/styles'; + +const PageContainer = styled(Box)(({ theme }) => ({ + padding: '1.5rem', + backgroundColor: '#F8F9FA', + minHeight: 'calc(100vh - 60px)', +})); + +const PageHeader = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: '1.5rem', +})); + +const StatusCard = styled(Card)(({ theme }) => ({ + height: '100%', + border: '1px solid #E5E5E5', + transition: 'all 0.2s ease-in-out', + '&:hover': { + boxShadow: '0 4px 12px rgba(0,0,0,0.1)', + }, +})); + +const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({ + fontSize: '0.75rem', + height: '24px', + backgroundColor: + status === 'connected' ? '#E8F5E8' : + status === 'connecting' ? '#FFF3CD' : + status === 'error' ? '#F8D7DA' : '#F8F9FA', + color: + status === 'connected' ? '#155724' : + status === 'connecting' ? '#856404' : + status === 'error' ? '#721C24' : '#666', +})); + +const mockMCPServers = [ + { + id: 1, + name: 'File System Server', + description: '文件系统操作服务器', + url: 'mcp://localhost:3001', + status: 'connected', + version: '1.0.0', + tools: ['read_file', 'write_file', 'list_directory'], + lastPing: '2024-01-15 14:30:25', + }, + { + id: 2, + name: 'Database Server', + description: '数据库查询服务器', + url: 'mcp://db.example.com:3002', + status: 'connected', + version: '1.2.1', + tools: ['query_sql', 'execute_sql', 'get_schema'], + lastPing: '2024-01-15 14:30:20', + }, + { + id: 3, + name: 'Web Scraper', + description: '网页抓取服务器', + url: 'mcp://scraper.example.com:3003', + status: 'connecting', + version: '0.9.5', + tools: ['scrape_url', 'extract_text', 'get_links'], + lastPing: '2024-01-15 14:28:15', + }, + { + id: 4, + name: 'API Gateway', + description: 'API网关服务器', + url: 'mcp://api.example.com:3004', + status: 'error', + version: '2.1.0', + tools: ['call_api', 'auth_token', 'rate_limit'], + lastPing: '2024-01-15 14:25:10', + }, +]; + +const MCP: React.FC = () => { + const [tabValue, setTabValue] = useState(0); + const [anchorEl, setAnchorEl] = useState(null); + const [selectedServer, setSelectedServer] = useState(null); + const [dialogOpen, setDialogOpen] = useState(false); + const [newServer, setNewServer] = useState({ + name: '', + url: '', + description: '', + }); + + const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { + setTabValue(newValue); + }; + + const handleMenuClick = (event: React.MouseEvent, serverId: number) => { + setAnchorEl(event.currentTarget); + setSelectedServer(serverId); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + setSelectedServer(null); + }; + + const handleAddServer = () => { + setDialogOpen(true); + }; + + const handleDialogClose = () => { + setDialogOpen(false); + setNewServer({ name: '', url: '', description: '' }); + }; + + const handleSaveServer = () => { + // 保存服务器逻辑 + console.log('添加服务器:', newServer); + handleDialogClose(); + }; + + const connectedServers = mockMCPServers.filter(s => s.status === 'connected').length; + const totalTools = mockMCPServers.reduce((acc, server) => acc + server.tools.length, 0); + + return ( + + + + + MCP 协议管理 + + + Model Context Protocol - 模型上下文协议服务器管理 + + + + + + + + + + + + + + {tabValue === 0 && ( + <> + {/* 状态概览 */} + + + + + + + + 连接服务器 + + + {connectedServers} + + + + + + + + + + + + + + + 总服务器数 + + + {mockMCPServers.length} + + + + + + + + + + + + + + + 可用工具 + + + {totalTools} + + + + + + + + + + + + + + + 平均延迟 + + + 45ms + + + + + + + + + + {/* 服务器列表 */} + + + + MCP 服务器列表 + + + + + + 服务器名称 + URL + 状态 + 版本 + 工具数量 + 最后心跳 + 操作 + + + + {mockMCPServers.map((server) => ( + + + + + {server.name} + + + {server.description} + + + + + + {server.url} + + + + + + + + + + + {server.tools.length} 个工具 + + + + + {server.lastPing} + + + + handleMenuClick(e, server.id)} + > + + + + + ))} + +
+
+
+
+ + )} + + {tabValue === 1 && ( + + + + 工具配置 + + + 配置各个 MCP 服务器提供的工具和权限设置 + + + {mockMCPServers.map((server) => ( + + + + + {server.name} + + + {server.tools.map((tool, index) => ( + } + label={ + + {tool} + + } + /> + ))} + + + + + ))} + + + + )} + + {tabValue === 2 && ( + + + + + + + 安全监控 + + + + SSL/TLS 加密 + + + + 身份验证 + + + + 访问控制 + + + + 审计日志 + + + + + + + + + + + + + 性能监控 + + + + + 平均响应时间 + 45ms + + + + 过去24小时内的平均值 + + + + + + 成功率 + + 99.2% + + + + + 过去24小时内的成功率 + + + + + + + + + )} + + {/* 菜单 */} + + + + 配置 + + 测试连接 + 查看日志 + + 断开连接 + + + + {/* 添加服务器对话框 */} + + 添加 MCP 服务器 + + + setNewServer(prev => ({ ...prev, name: e.target.value }))} + /> + setNewServer(prev => ({ ...prev, url: e.target.value }))} + /> + setNewServer(prev => ({ ...prev, description: e.target.value }))} + /> + + + + + + + +
+ ); +}; + +export default MCP; \ No newline at end of file diff --git a/src/pages/ModelsResources.tsx b/src/pages/ModelsResources.tsx new file mode 100644 index 0000000..43889fc --- /dev/null +++ b/src/pages/ModelsResources.tsx @@ -0,0 +1,540 @@ +import React, { useState } from 'react'; +import { + Box, + Typography, + Card, + CardContent, + Grid, + Button, + Chip, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + LinearProgress, + IconButton, + Menu, + MenuItem, + Tabs, + Tab, + TextField, + InputAdornment, +} from '@mui/material'; +import { + Memory as MemoryIcon, + Storage as StorageIcon, + Speed as SpeedIcon, + CloudDownload as CloudDownloadIcon, + Settings as SettingsIcon, + MoreVert as MoreVertIcon, + Search as SearchIcon, + Add as AddIcon, +} from '@mui/icons-material'; +import { styled } from '@mui/material/styles'; + +const PageContainer = styled(Box)(({ theme }) => ({ + padding: '1.5rem', + backgroundColor: '#F8F9FA', + minHeight: 'calc(100vh - 60px)', +})); + +const PageHeader = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: '1.5rem', +})); + +const ResourceCard = styled(Card)(({ theme }) => ({ + height: '100%', + border: '1px solid #E5E5E5', + transition: 'all 0.2s ease-in-out', + '&:hover': { + boxShadow: '0 4px 12px rgba(0,0,0,0.1)', + }, +})); + +const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({ + fontSize: '0.75rem', + height: '24px', + backgroundColor: + status === 'active' ? '#E8F5E8' : + status === 'loading' ? '#FFF3CD' : + status === 'error' ? '#F8D7DA' : '#F8F9FA', + color: + status === 'active' ? '#155724' : + status === 'loading' ? '#856404' : + status === 'error' ? '#721C24' : '#666', +})); + +const UsageBar = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: '0.5rem', + marginTop: '0.5rem', +})); + +const mockModels = [ + { + id: 1, + name: 'GPT-4', + type: 'LLM', + provider: 'OpenAI', + status: 'active', + usage: 75, + cost: '$234.56', + requests: '12.3K', + latency: '1.2s', + }, + { + id: 2, + name: 'text-embedding-ada-002', + type: 'Embedding', + provider: 'OpenAI', + status: 'active', + usage: 45, + cost: '$89.12', + requests: '45.6K', + latency: '0.3s', + }, + { + id: 3, + name: 'Claude-3-Sonnet', + type: 'LLM', + provider: 'Anthropic', + status: 'loading', + usage: 0, + cost: '$0.00', + requests: '0', + latency: 'N/A', + }, + { + id: 4, + name: 'BGE-Large-ZH', + type: 'Embedding', + provider: 'BAAI', + status: 'error', + usage: 0, + cost: '$0.00', + requests: '0', + latency: 'N/A', + }, +]; + +const mockResources = [ + { + id: 1, + name: 'GPU-Server-01', + type: 'GPU', + specs: 'NVIDIA A100 80GB', + usage: 85, + status: 'active', + location: '北京机房', + }, + { + id: 2, + name: 'CPU-Cluster-01', + type: 'CPU', + specs: '64 Core Intel Xeon', + usage: 45, + status: 'active', + location: '上海机房', + }, + { + id: 3, + name: 'Storage-Pool-01', + type: 'Storage', + specs: '10TB NVMe SSD', + usage: 67, + status: 'active', + location: '深圳机房', + }, +]; + +const ModelsResources: React.FC = () => { + const [tabValue, setTabValue] = useState(0); + const [searchTerm, setSearchTerm] = useState(''); + const [anchorEl, setAnchorEl] = useState(null); + const [selectedItem, setSelectedItem] = useState(null); + + const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { + setTabValue(newValue); + }; + + const handleMenuClick = (event: React.MouseEvent, itemId: number) => { + setAnchorEl(event.currentTarget); + setSelectedItem(itemId); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + setSelectedItem(null); + }; + + const filteredModels = mockModels.filter(model => + model.name.toLowerCase().includes(searchTerm.toLowerCase()) || + model.provider.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + const filteredResources = mockResources.filter(resource => + resource.name.toLowerCase().includes(searchTerm.toLowerCase()) || + resource.type.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + return ( + + + + 模型与资源 + + + + + + + + + + + + + setSearchTerm(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ width: '400px' }} + /> + + + {tabValue === 0 && ( + <> + {/* 模型概览卡片 */} + + + + + + + + 活跃模型 + + + {mockModels.filter(m => m.status === 'active').length} + + + + + + + + + + + + + + + 总请求数 + + + 57.9K + + + + + + + + + + + + + + + 总成本 + + + $323.68 + + + + + + + + + + + + + + + 平均延迟 + + + 0.8s + + + + + + + + + + {/* 模型列表 */} + + + + 模型列表 + + + + + + 模型名称 + 类型 + 提供商 + 状态 + 使用率 + 请求数 + 成本 + 延迟 + 操作 + + + + {filteredModels.map((model) => ( + + + + {model.name} + + + + + + {model.provider} + + + + + + 80 ? 'error' : model.usage > 60 ? 'warning' : 'primary'} + /> + + {model.usage}% + + + + {model.requests} + {model.cost} + {model.latency} + + handleMenuClick(e, model.id)} + > + + + + + ))} + +
+
+
+
+ + )} + + {tabValue === 1 && ( + <> + {/* 资源概览卡片 */} + + + + + + + + GPU 使用率 + + + 85% + + + + + + + + + + + + + + + CPU 使用率 + + + 45% + + + + + + + + + + + + + + + 存储使用率 + + + 67% + + + + + + + + + + {/* 资源列表 */} + + + + 计算资源 + + + + + + 资源名称 + 类型 + 规格 + 使用率 + 状态 + 位置 + 操作 + + + + {filteredResources.map((resource) => ( + + + + {resource.name} + + + + + + {resource.specs} + + + 80 ? 'error' : resource.usage > 60 ? 'warning' : 'primary'} + /> + + {resource.usage}% + + + + + + + {resource.location} + + handleMenuClick(e, resource.id)} + > + + + + + ))} + +
+
+
+
+ + )} + + + + + 配置 + + 查看详情 + 监控 + + 停用 + + +
+ ); +}; + +export default ModelsResources; \ No newline at end of file diff --git a/src/pages/PipelineConfig.tsx b/src/pages/PipelineConfig.tsx new file mode 100644 index 0000000..27d28df --- /dev/null +++ b/src/pages/PipelineConfig.tsx @@ -0,0 +1,349 @@ +import React, { useState } from 'react'; +import { + Box, + Typography, + Card, + CardContent, + Grid, + Button, + Switch, + FormControlLabel, + Slider, + TextField, + Select, + MenuItem, + FormControl, + InputLabel, + Chip, + Divider, + Alert, + LinearProgress, +} from '@mui/material'; +import { + Settings as SettingsIcon, + PlayArrow as PlayIcon, + Stop as StopIcon, + Refresh as RefreshIcon, + Save as SaveIcon, +} from '@mui/icons-material'; +import { styled } from '@mui/material/styles'; + +const PageContainer = styled(Box)(({ theme }) => ({ + padding: '1.5rem', + backgroundColor: '#F8F9FA', + minHeight: 'calc(100vh - 60px)', +})); + +const PageHeader = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: '1.5rem', +})); + +const ConfigCard = styled(Card)(({ theme }) => ({ + marginBottom: '1.5rem', + border: '1px solid #E5E5E5', +})); + +const ConfigSection = styled(Box)(({ theme }) => ({ + marginBottom: '1.5rem', + '&:last-child': { + marginBottom: 0, + }, +})); + +const StatusIndicator = styled(Box)<{ status: 'running' | 'stopped' | 'error' }>(({ status, theme }) => ({ + display: 'inline-flex', + alignItems: 'center', + gap: '0.5rem', + padding: '0.25rem 0.75rem', + borderRadius: '12px', + fontSize: '0.875rem', + fontWeight: 500, + backgroundColor: + status === 'running' ? '#E8F5E8' : + status === 'stopped' ? '#F8F9FA' : '#F8D7DA', + color: + status === 'running' ? '#155724' : + status === 'stopped' ? '#666' : '#721C24', + '&::before': { + content: '""', + width: '8px', + height: '8px', + borderRadius: '50%', + backgroundColor: + status === 'running' ? '#28A745' : + status === 'stopped' ? '#6C757D' : '#DC3545', + }, +})); + +const PipelineConfig: React.FC = () => { + const [pipelineStatus, setPipelineStatus] = useState<'running' | 'stopped' | 'error'>('stopped'); + const [config, setConfig] = useState({ + enabled: false, + chunkSize: 512, + chunkOverlap: 50, + embeddingModel: 'text-embedding-ada-002', + retrievalTopK: 5, + temperature: 0.7, + maxTokens: 2048, + systemPrompt: '你是一个专业的AI助手,请基于提供的知识库内容回答用户问题。', + }); + + const handleConfigChange = (key: string, value: any) => { + setConfig(prev => ({ ...prev, [key]: value })); + }; + + const handleStartPipeline = () => { + setPipelineStatus('running'); + }; + + const handleStopPipeline = () => { + setPipelineStatus('stopped'); + }; + + const handleSaveConfig = () => { + // 保存配置逻辑 + console.log('保存配置:', config); + }; + + return ( + + + + + RAG Pipeline 配置 + + + + {pipelineStatus === 'running' ? '运行中' : + pipelineStatus === 'stopped' ? '已停止' : '错误'} + + {pipelineStatus === 'running' && ( + + )} + + + + + {pipelineStatus === 'running' ? ( + + ) : ( + + )} + + + + + + + + + + 基础配置 + + + + handleConfigChange('enabled', e.target.checked)} + /> + } + label="启用 RAG Pipeline" + /> + + + + 文档分块大小 + handleConfigChange('chunkSize', value)} + min={128} + max={2048} + step={64} + marks={[ + { value: 128, label: '128' }, + { value: 512, label: '512' }, + { value: 1024, label: '1024' }, + { value: 2048, label: '2048' }, + ]} + valueLabelDisplay="on" + /> + + + + 分块重叠 (%) + handleConfigChange('chunkOverlap', value)} + min={0} + max={50} + step={5} + valueLabelDisplay="on" + /> + + + + + 嵌入模型 + + + + + + + + + + + + 检索与生成配置 + + + + handleConfigChange('retrievalTopK', parseInt(e.target.value))} + inputProps={{ min: 1, max: 20 }} + /> + + + + 生成温度 + handleConfigChange('temperature', value)} + min={0} + max={1} + step={0.1} + marks={[ + { value: 0, label: '0' }, + { value: 0.5, label: '0.5' }, + { value: 1, label: '1' }, + ]} + valueLabelDisplay="on" + /> + + + + handleConfigChange('maxTokens', parseInt(e.target.value))} + inputProps={{ min: 256, max: 4096 }} + /> + + + + handleConfigChange('systemPrompt', e.target.value)} + /> + + + + + + + + + + Pipeline 状态监控 + + + + + + + 1,234 + + + 已处理文档 + + + + + + + 98.5% + + + 成功率 + + + + + + + 2.3s + + + 平均响应时间 + + + + + + + 156 + + + 今日查询数 + + + + + + {pipelineStatus === 'error' && ( + + Pipeline 运行出现错误,请检查配置和日志。 + + )} + + + + + + ); +}; + +export default PipelineConfig; \ No newline at end of file diff --git a/src/routes/index.tsx b/src/routes/index.tsx new file mode 100644 index 0000000..6d28e14 --- /dev/null +++ b/src/routes/index.tsx @@ -0,0 +1,32 @@ +import { Routes, Route, Navigate } from 'react-router-dom'; +import MainLayout from '../components/Layout/MainLayout'; +import Login from '../pages/Login'; +import Home from '../pages/Home'; +import KnowledgeBaseList from '../pages/KnowledgeBaseList'; +import PipelineConfig from '../pages/PipelineConfig'; +import Dashboard from '../pages/Dashboard'; +import ModelsResources from '../pages/ModelsResources'; +import MCP from '../pages/MCP'; + +const AppRoutes = () => { + return ( + + } /> + + {/* 使用MainLayout作为受保护路由的布局 */} + }> + } /> + } /> + } /> + } /> + } /> + } /> + + + {/* 处理未匹配的路由 */} + } /> + + ); +}; + +export default AppRoutes; \ No newline at end of file diff --git a/src/theme/index.ts b/src/theme/index.ts new file mode 100644 index 0000000..32af04a --- /dev/null +++ b/src/theme/index.ts @@ -0,0 +1,164 @@ +import { createTheme } from '@mui/material/styles'; + +// Company branding colors extracted from web_prototype CSS +const brandColors = { + primary: '#E20074', + primaryHover: '#C40062', + background: '#F0F0F0', + surface: '#FFFFFF', + text: '#222', + textSecondary: '#555', + border: '#E2E2E2', + borderStrong: '#CFCFCF', + success: '#1E9E59', + danger: '#D22C32', + warning: '#D89200', +}; + +export const theme = createTheme({ + palette: { + primary: { + main: brandColors.primary, + dark: brandColors.primaryHover, + contrastText: '#FFFFFF', + }, + secondary: { + main: brandColors.primary, + dark: brandColors.primaryHover, + }, + background: { + default: brandColors.background, + paper: brandColors.surface, + }, + text: { + primary: brandColors.text, + secondary: brandColors.textSecondary, + }, + success: { + main: brandColors.success, + }, + error: { + main: brandColors.danger, + }, + warning: { + main: brandColors.warning, + }, + divider: brandColors.border, + }, + typography: { + fontFamily: '"Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif', + h1: { + fontWeight: 700, + letterSpacing: '0.5px', + }, + h2: { + fontWeight: 600, + letterSpacing: '0.5px', + }, + h3: { + fontWeight: 600, + letterSpacing: '0.3px', + }, + body1: { + fontSize: '0.875rem', + }, + body2: { + fontSize: '0.75rem', + }, + button: { + fontWeight: 600, + letterSpacing: '0.3px', + textTransform: 'none', + }, + }, + shape: { + borderRadius: 10, + }, + shadows: [ + 'none', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 4px 12px -2px rgba(226,0,116,0.45)', + '0 6px 14px -2px rgba(226,0,116,0.5)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + ], + components: { + MuiButton: { + styleOverrides: { + root: { + borderRadius: '8px', + padding: '0.65rem 1.1rem', + fontSize: '0.8rem', + fontWeight: 600, + letterSpacing: '0.3px', + }, + contained: { + boxShadow: '0 4px 10px -2px rgba(226,0,116,0.45)', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: '0 6px 14px -2px rgba(226,0,116,0.5)', + }, + }, + }, + }, + MuiCard: { + styleOverrides: { + root: { + borderRadius: '10px', + border: `1px solid ${brandColors.border}`, + boxShadow: '0 2px 4px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.06)', + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: '8px', + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: brandColors.primary, + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: brandColors.primary, + boxShadow: `0 0 0 3px rgba(226,0,116,0.25)`, + }, + }, + }, + }, + }, + MuiLinearProgress: { + styleOverrides: { + root: { + height: '8px', + borderRadius: '4px', + backgroundColor: '#ECECEC', + }, + bar: { + borderRadius: '4px', + background: `linear-gradient(90deg, ${brandColors.primary}, #FF4DA8)`, + }, + }, + }, + }, +}); + +export default theme; \ No newline at end of file