Просмотр исходного кода

feat: replace remote project with new web project

xuyonghao 1 месяц назад
Сommit
4c2b75f10b
67 измененных файлов с 15137 добавлено и 0 удалено
  1. 24 0
      .gitignore
  2. 57 0
      README.md
  3. 28 0
      eslint.config.js
  4. 26 0
      index.html
  5. 4074 0
      package-lock.json
  6. 43 0
      package.json
  7. 2755 0
      pnpm-lock.yaml
  8. 2 0
      pnpm-workspace.yaml
  9. 10 0
      postcss.config.js
  10. 368 0
      public/favicon.svg
  11. 305 0
      public/logo.svg
  12. 34 0
      src/App.tsx
  13. 294 0
      src/api/mockApi.ts
  14. 0 0
      src/assets/react.svg
  15. 8 0
      src/components/Empty.tsx
  16. 63 0
      src/components/about/CompanyIntro.tsx
  17. 22 0
      src/components/about/CompanyMap.tsx
  18. 163 0
      src/components/about/Vision.tsx
  19. 86 0
      src/components/cases/CaseDetail.tsx
  20. 53 0
      src/components/common/Button.tsx
  21. 31 0
      src/components/common/Card.tsx
  22. 33 0
      src/components/common/SectionHeader.tsx
  23. 87 0
      src/components/home/ApplicationScenarios.tsx
  24. 74 0
      src/components/home/BrandEndorsement.tsx
  25. 166 0
      src/components/home/BusinessConversion.tsx
  26. 101 0
      src/components/home/CaseStudies.tsx
  27. 104 0
      src/components/home/CoreCapabilities.tsx
  28. 83 0
      src/components/home/PainPointSolutions.tsx
  29. 100 0
      src/components/home/PlatformIntro.tsx
  30. 90 0
      src/components/home/RndAdvantage.tsx
  31. 86 0
      src/components/home/WhatWeDo.tsx
  32. 61 0
      src/components/layout/FloatingConsult.tsx
  33. 105 0
      src/components/layout/Footer.tsx
  34. 114 0
      src/components/layout/Header.tsx
  35. 107 0
      src/components/layout/HeroCarousel.tsx
  36. 166 0
      src/components/layout/Sidebar.tsx
  37. 116 0
      src/components/product/PlatformArchitecture.tsx
  38. 117 0
      src/components/solutions/PowerSolution.tsx
  39. 203 0
      src/data/mockData.ts
  40. 29 0
      src/hooks/useTheme.ts
  41. 187 0
      src/index.css
  42. 6 0
      src/lib/utils.ts
  43. 10 0
      src/main.tsx
  44. 365 0
      src/pages/APIKeys.tsx
  45. 15 0
      src/pages/About.tsx
  46. 337 0
      src/pages/Admins.tsx
  47. 196 0
      src/pages/CallLogs.tsx
  48. 29 0
      src/pages/Cases.tsx
  49. 360 0
      src/pages/Channels.tsx
  50. 250 0
      src/pages/CostStats.tsx
  51. 174 0
      src/pages/Dashboard.tsx
  52. 281 0
      src/pages/Departments.tsx
  53. 605 0
      src/pages/Enterprises.tsx
  54. 25 0
      src/pages/Home.tsx
  55. 105 0
      src/pages/Login.tsx
  56. 376 0
      src/pages/Models.tsx
  57. 17 0
      src/pages/Product.tsx
  58. 257 0
      src/pages/Settings.tsx
  59. 15 0
      src/pages/Solutions.tsx
  60. 213 0
      src/pages/TokenStats.tsx
  61. 479 0
      src/pages/Users.tsx
  62. 141 0
      src/store/authStore.ts
  63. 142 0
      src/types/index.ts
  64. 1 0
      src/vite-env.d.ts
  65. 104 0
      tailwind.config.js
  66. 36 0
      tsconfig.json
  67. 23 0
      vite.config.ts

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 57 - 0
README.md

@@ -0,0 +1,57 @@
+# React + TypeScript + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
+
+```js
+export default tseslint.config({
+  extends: [
+    // Remove ...tseslint.configs.recommended and replace with this
+    ...tseslint.configs.recommendedTypeChecked,
+    // Alternatively, use this for stricter rules
+    ...tseslint.configs.strictTypeChecked,
+    // Optionally, add this for stylistic rules
+    ...tseslint.configs.stylisticTypeChecked,
+  ],
+  languageOptions: {
+    // other options...
+    parserOptions: {
+      project: ['./tsconfig.node.json', './tsconfig.app.json'],
+      tsconfigRootDir: import.meta.dirname,
+    },
+  },
+})
+```
+
+You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
+
+```js
+// eslint.config.js
+import reactX from 'eslint-plugin-react-x'
+import reactDom from 'eslint-plugin-react-dom'
+
+export default tseslint.config({
+  extends: [
+    // other configs...
+    // Enable lint rules for React
+    reactX.configs['recommended-typescript'],
+    // Enable lint rules for React DOM
+    reactDom.configs.recommended,
+  ],
+  languageOptions: {
+    // other options...
+    parserOptions: {
+      project: ['./tsconfig.node.json', './tsconfig.app.json'],
+      tsconfigRootDir: import.meta.dirname,
+    },
+  },
+})
+```

+ 28 - 0
eslint.config.js

@@ -0,0 +1,28 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+
+export default tseslint.config(
+  { ignores: ['dist'] },
+  {
+    extends: [js.configs.recommended, ...tseslint.configs.recommended],
+    files: ['**/*.{ts,tsx}'],
+    languageOptions: {
+      ecmaVersion: 2020,
+      globals: globals.browser,
+    },
+    plugins: {
+      'react-hooks': reactHooks,
+      'react-refresh': reactRefresh,
+    },
+    rules: {
+      ...reactHooks.configs.recommended.rules,
+      'react-refresh/only-export-components': [
+        'warn',
+        { allowConstantExport: true },
+      ],
+    },
+  },
+)

+ 26 - 0
index.html

@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta name="description" content="哈灵AI - 哈工大计算学部联合研发的企业级大模型应用开发平台,0基础可视化智能体编排、极速搭建、全行业通用能力" />
+    <meta name="keywords" content="AI大模型,智能体,企业级AI,可视化编排,MCP协议,电力AI,政务AI" />
+    <title>哈灵AI - 企业级大模型应用开发平台 | 哈工大联合研发</title>
+    <script type="module">
+      if (import.meta.hot?.on) {
+        import.meta.hot.on('vite:error', (error) => {
+          if (error.err) {
+            console.error(
+              [error.err.message, error.err.frame].filter(Boolean).join('\n'),
+            )
+          }
+        })
+      }
+    </script>
+  </head>
+  <body>
+    <div id="root"></div>
+    <script type="module" src="/src/main.tsx"></script>
+  </body>
+</html>

+ 4074 - 0
package-lock.json

@@ -0,0 +1,4074 @@
+{
+  "name": "web",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "web",
+      "version": "0.0.0",
+      "dependencies": {
+        "@rollup/rollup-darwin-arm64": "^4.62.2",
+        "clsx": "^2.1.1",
+        "lucide-react": "^0.511.0",
+        "react": "^18.3.1",
+        "react-dom": "^18.3.1",
+        "react-router-dom": "^7.18.1",
+        "tailwind-merge": "^3.0.2",
+        "zustand": "^5.0.3"
+      },
+      "devDependencies": {
+        "@eslint/js": "^9.25.0",
+        "@types/node": "^22.15.30",
+        "@types/react": "^18.3.12",
+        "@types/react-dom": "^18.3.1",
+        "@vitejs/plugin-react": "^4.4.1",
+        "autoprefixer": "^10.5.2",
+        "babel-plugin-react-dev-locator": "^1.0.0",
+        "eslint": "^9.25.0",
+        "eslint-plugin-react-hooks": "^5.2.0",
+        "eslint-plugin-react-refresh": "^0.4.19",
+        "globals": "^16.0.0",
+        "postcss": "^8.5.16",
+        "tailwindcss": "^3.4.19",
+        "typescript": "~5.8.3",
+        "typescript-eslint": "^8.30.1",
+        "vite": "^6.3.5",
+        "vite-plugin-trae-solo-badge": "^1.0.0",
+        "vite-tsconfig-paths": "^5.1.4"
+      }
+    },
+    "node_modules/@alloc/quick-lru": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+      "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+      "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+      "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+      "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.7",
+        "@babel/helper-compilation-targets": "^7.29.7",
+        "@babel/helper-module-transforms": "^7.29.7",
+        "@babel/helpers": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/template": "^7.29.7",
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7",
+        "@jridgewell/remapping": "^2.3.5",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+      "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7",
+        "@jridgewell/gen-mapping": "^0.3.12",
+        "@jridgewell/trace-mapping": "^0.3.28",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+      "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/compat-data": "^7.29.7",
+        "@babel/helper-validator-option": "^7.29.7",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-globals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+      "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+      "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+      "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-plugin-utils": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+      "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+      "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+      "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+      "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+      "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+      "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.29.7"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-react-jsx-self": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+      "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-react-jsx-source": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+      "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+      "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+      "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.7",
+        "@babel/helper-globals": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.7",
+        "debug": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+      "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+      "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+      "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+      "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+      "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+      "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+      "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+      "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+      "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+      "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+      "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+      "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+      "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+      "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+      "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+      "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+      "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+      "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+      "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+      "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+      "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+      "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.9.1",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+      "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint-community/regexpp": {
+      "version": "4.12.2",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+      "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+      "dev": true,
+      "engines": {
+        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+      }
+    },
+    "node_modules/@eslint/config-array": {
+      "version": "0.21.2",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+      "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+      "dev": true,
+      "dependencies": {
+        "@eslint/object-schema": "^2.1.7",
+        "debug": "^4.3.1",
+        "minimatch": "^3.1.5"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/config-helpers": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+      "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+      "dev": true,
+      "dependencies": {
+        "@eslint/core": "^0.17.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/core": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+      "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.15"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
+      "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^6.14.0",
+        "debug": "^4.3.2",
+        "espree": "^10.0.1",
+        "globals": "^14.0.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.1",
+        "minimatch": "^3.1.5",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@eslint/eslintrc/node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+      "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
+      "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
+      "dev": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+      "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+      "dev": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@eslint/plugin-kit": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+      "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+      "dev": true,
+      "dependencies": {
+        "@eslint/core": "^0.17.0",
+        "levn": "^0.4.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      }
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+      "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+      "dev": true,
+      "dependencies": {
+        "@humanfs/types": "^0.15.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/node": {
+      "version": "0.16.8",
+      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+      "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+      "dev": true,
+      "dependencies": {
+        "@humanfs/core": "^0.19.2",
+        "@humanfs/types": "^0.15.0",
+        "@humanwhocodes/retry": "^0.4.0"
+      },
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanfs/types": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+      "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.18.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/retry": {
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+      "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/remapping": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "dev": true
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@rolldown/pluginutils": {
+      "version": "1.0.0-beta.27",
+      "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+      "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+      "dev": true
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
+      "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
+      "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
+      "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
+      "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
+      "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
+      "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
+      "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
+      "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
+      "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
+      "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-gnu": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
+      "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-musl": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
+      "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
+      "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-musl": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
+      "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
+      "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
+      "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
+      "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
+      "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
+      "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-openbsd-x64": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
+      "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-openharmony-arm64": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
+      "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
+      "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
+      "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
+      "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
+      "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@types/babel__core": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+      "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7",
+        "@types/babel__generator": "*",
+        "@types/babel__template": "*",
+        "@types/babel__traverse": "*"
+      }
+    },
+    "node_modules/@types/babel__generator": {
+      "version": "7.27.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+      "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__template": {
+      "version": "7.4.4",
+      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+      "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__traverse": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+      "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.28.2"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+      "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+      "dev": true
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true
+    },
+    "node_modules/@types/node": {
+      "version": "22.20.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
+      "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
+      "dev": true,
+      "dependencies": {
+        "undici-types": "~6.21.0"
+      }
+    },
+    "node_modules/@types/prop-types": {
+      "version": "15.7.15",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+      "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+      "dev": true
+    },
+    "node_modules/@types/react": {
+      "version": "18.3.31",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
+      "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
+      "dev": true,
+      "dependencies": {
+        "@types/prop-types": "*",
+        "csstype": "^3.2.2"
+      }
+    },
+    "node_modules/@types/react-dom": {
+      "version": "18.3.7",
+      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+      "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+      "dev": true,
+      "peerDependencies": {
+        "@types/react": "^18.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.63.0.tgz",
+      "integrity": "sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.12.2",
+        "@typescript-eslint/scope-manager": "8.63.0",
+        "@typescript-eslint/type-utils": "8.63.0",
+        "@typescript-eslint/utils": "8.63.0",
+        "@typescript-eslint/visitor-keys": "8.63.0",
+        "ignore": "^7.0.5",
+        "natural-compare": "^1.4.0",
+        "ts-api-utils": "^2.5.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^8.63.0",
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.63.0.tgz",
+      "integrity": "sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "8.63.0",
+        "@typescript-eslint/types": "8.63.0",
+        "@typescript-eslint/typescript-estree": "8.63.0",
+        "@typescript-eslint/visitor-keys": "8.63.0",
+        "debug": "^4.4.3"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/project-service": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz",
+      "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/tsconfig-utils": "^8.63.0",
+        "@typescript-eslint/types": "^8.63.0",
+        "debug": "^4.4.3"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.63.0.tgz",
+      "integrity": "sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "8.63.0",
+        "@typescript-eslint/visitor-keys": "8.63.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/tsconfig-utils": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz",
+      "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==",
+      "dev": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.63.0.tgz",
+      "integrity": "sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "8.63.0",
+        "@typescript-eslint/typescript-estree": "8.63.0",
+        "@typescript-eslint/utils": "8.63.0",
+        "debug": "^4.4.3",
+        "ts-api-utils": "^2.5.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.63.0.tgz",
+      "integrity": "sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==",
+      "dev": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz",
+      "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/project-service": "8.63.0",
+        "@typescript-eslint/tsconfig-utils": "8.63.0",
+        "@typescript-eslint/types": "8.63.0",
+        "@typescript-eslint/visitor-keys": "8.63.0",
+        "debug": "^4.4.3",
+        "minimatch": "^10.2.2",
+        "semver": "^7.7.3",
+        "tinyglobby": "^0.2.15",
+        "ts-api-utils": "^2.5.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+      "dev": true,
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+      "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^4.0.2"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+      "version": "10.2.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^5.0.5"
+      },
+      "engines": {
+        "node": "18 || 20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+      "version": "7.8.5",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.63.0.tgz",
+      "integrity": "sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.9.1",
+        "@typescript-eslint/scope-manager": "8.63.0",
+        "@typescript-eslint/types": "8.63.0",
+        "@typescript-eslint/typescript-estree": "8.63.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.63.0.tgz",
+      "integrity": "sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/types": "8.63.0",
+        "eslint-visitor-keys": "^5.0.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+      "dev": true,
+      "engines": {
+        "node": "^20.19.0 || ^22.13.0 || >=24"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@vitejs/plugin-react": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+      "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.28.0",
+        "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+        "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+        "@rolldown/pluginutils": "1.0.0-beta.27",
+        "@types/babel__core": "^7.20.5",
+        "react-refresh": "^0.17.0"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.17.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+      "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.15.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+      "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/any-promise": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+      "dev": true
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/arg": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+      "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+      "dev": true
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
+    "node_modules/autoprefixer": {
+      "version": "10.5.2",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz",
+      "integrity": "sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "browserslist": "^4.28.4",
+        "caniuse-lite": "^1.0.30001799",
+        "fraction.js": "^5.3.4",
+        "picocolors": "^1.1.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "bin": {
+        "autoprefixer": "bin/autoprefixer"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/babel-plugin-react-dev-locator": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/babel-plugin-react-dev-locator/-/babel-plugin-react-dev-locator-1.0.6.tgz",
+      "integrity": "sha512-XWi+6x6e4NvVwvOqwitci/BZU1xbNfNuL94kfT4kWfP/d9p3RYRVOrogs1Z1otlYfQUO07cy/20z8eY9blFSpw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.27.1",
+        "@babel/generator": "^7.27.1",
+        "@babel/parser": "^7.27.1",
+        "@babel/traverse": "^7.27.1",
+        "@babel/types": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "node_modules/baseline-browser-mapping": {
+      "version": "2.10.42",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz",
+      "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==",
+      "dev": true,
+      "bin": {
+        "baseline-browser-mapping": "dist/cli.cjs"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.16",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+      "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.28.5",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz",
+      "integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "baseline-browser-mapping": "^2.10.42",
+        "caniuse-lite": "^1.0.30001800",
+        "electron-to-chromium": "^1.5.387",
+        "node-releases": "^2.0.50",
+        "update-browserslist-db": "^1.2.3"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camelcase-css": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001803",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz",
+      "integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ]
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/chokidar/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/commander": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true
+    },
+    "node_modules/cookie": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+      "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true,
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "dev": true
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
+    },
+    "node_modules/didyoumean": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+      "dev": true
+    },
+    "node_modules/dlv": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+      "dev": true
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.389",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz",
+      "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==",
+      "dev": true
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+      "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.25.12",
+        "@esbuild/android-arm": "0.25.12",
+        "@esbuild/android-arm64": "0.25.12",
+        "@esbuild/android-x64": "0.25.12",
+        "@esbuild/darwin-arm64": "0.25.12",
+        "@esbuild/darwin-x64": "0.25.12",
+        "@esbuild/freebsd-arm64": "0.25.12",
+        "@esbuild/freebsd-x64": "0.25.12",
+        "@esbuild/linux-arm": "0.25.12",
+        "@esbuild/linux-arm64": "0.25.12",
+        "@esbuild/linux-ia32": "0.25.12",
+        "@esbuild/linux-loong64": "0.25.12",
+        "@esbuild/linux-mips64el": "0.25.12",
+        "@esbuild/linux-ppc64": "0.25.12",
+        "@esbuild/linux-riscv64": "0.25.12",
+        "@esbuild/linux-s390x": "0.25.12",
+        "@esbuild/linux-x64": "0.25.12",
+        "@esbuild/netbsd-arm64": "0.25.12",
+        "@esbuild/netbsd-x64": "0.25.12",
+        "@esbuild/openbsd-arm64": "0.25.12",
+        "@esbuild/openbsd-x64": "0.25.12",
+        "@esbuild/openharmony-arm64": "0.25.12",
+        "@esbuild/sunos-x64": "0.25.12",
+        "@esbuild/win32-arm64": "0.25.12",
+        "@esbuild/win32-ia32": "0.25.12",
+        "@esbuild/win32-x64": "0.25.12"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "9.39.4",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
+      "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
+      "dev": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.8.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.21.2",
+        "@eslint/config-helpers": "^0.4.2",
+        "@eslint/core": "^0.17.0",
+        "@eslint/eslintrc": "^3.3.5",
+        "@eslint/js": "9.39.4",
+        "@eslint/plugin-kit": "^0.4.1",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.2",
+        "@types/estree": "^1.0.6",
+        "ajv": "^6.14.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^8.4.0",
+        "eslint-visitor-keys": "^4.2.1",
+        "espree": "^10.4.0",
+        "esquery": "^1.5.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.5",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-plugin-react-hooks": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+      "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-react-refresh": {
+      "version": "0.4.26",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
+      "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
+      "dev": true,
+      "peerDependencies": {
+        "eslint": ">=8.40"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+      "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+      "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+      "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+      "dev": true,
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/espree": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+      "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+      "dev": true,
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true
+    },
+    "node_modules/fastq": {
+      "version": "1.20.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+      "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+      "dev": true,
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+      "dev": true,
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+      "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+      "dev": true,
+      "dependencies": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+      "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+      "dev": true
+    },
+    "node_modules/fraction.js": {
+      "version": "5.3.4",
+      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+      "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/rawify"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globrex": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+      "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
+      "dev": true
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+      "dev": true,
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.16.2",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+      "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+      "dev": true,
+      "dependencies": {
+        "hasown": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true
+    },
+    "node_modules/jiti": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+      "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+      "dev": true,
+      "bin": {
+        "jiti": "bin/jiti.js"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+    },
+    "node_modules/js-yaml": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+      "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/puzrin"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/nodeca"
+        }
+      ],
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/lilconfig": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+      "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+      "dev": true,
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antonk52"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/lucide-react": {
+      "version": "0.511.0",
+      "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.511.0.tgz",
+      "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==",
+      "peerDependencies": {
+        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+      "dev": true,
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+      "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true
+    },
+    "node_modules/mz": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+      "dev": true,
+      "dependencies": {
+        "any-promise": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "thenify-all": "^1.0.0"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.15",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
+      "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.51",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
+      "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-hash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+      "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+      "dev": true,
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "dev": true
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+      "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pify": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/pirates": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+      "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.16",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
+      "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.12",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-import": {
+      "version": "15.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+      "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+      "dev": true,
+      "dependencies": {
+        "postcss-value-parser": "^4.0.0",
+        "read-cache": "^1.0.0",
+        "resolve": "^1.1.7"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.0.0"
+      }
+    },
+    "node_modules/postcss-js": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
+      "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "camelcase-css": "^2.0.1"
+      },
+      "engines": {
+        "node": "^12 || ^14 || >= 16"
+      },
+      "peerDependencies": {
+        "postcss": "^8.4.21"
+      }
+    },
+    "node_modules/postcss-load-config": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
+      "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "lilconfig": "^3.1.1"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "peerDependencies": {
+        "jiti": ">=1.21.0",
+        "postcss": ">=8.0.9",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        },
+        "postcss": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss-nested": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+      "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "postcss-selector-parser": "^6.1.1"
+      },
+      "engines": {
+        "node": ">=12.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.14"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "6.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz",
+      "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==",
+      "dev": true,
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-value-parser": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+      "dev": true
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/react": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+      "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+      "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+      "dependencies": {
+        "loose-envify": "^1.1.0",
+        "scheduler": "^0.23.2"
+      },
+      "peerDependencies": {
+        "react": "^18.3.1"
+      }
+    },
+    "node_modules/react-refresh": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+      "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-router": {
+      "version": "7.18.1",
+      "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz",
+      "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==",
+      "dependencies": {
+        "cookie": "^1.0.1",
+        "set-cookie-parser": "^2.6.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=18",
+        "react-dom": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/react-router-dom": {
+      "version": "7.18.1",
+      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz",
+      "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==",
+      "dependencies": {
+        "react-router": "7.18.1"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=18",
+        "react-dom": ">=18"
+      }
+    },
+    "node_modules/read-cache": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+      "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+      "dev": true,
+      "dependencies": {
+        "pify": "^2.3.0"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.12",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+      "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+      "dev": true,
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "is-core-module": "^2.16.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+      "dev": true,
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.62.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
+      "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.9"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.62.2",
+        "@rollup/rollup-android-arm64": "4.62.2",
+        "@rollup/rollup-darwin-arm64": "4.62.2",
+        "@rollup/rollup-darwin-x64": "4.62.2",
+        "@rollup/rollup-freebsd-arm64": "4.62.2",
+        "@rollup/rollup-freebsd-x64": "4.62.2",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
+        "@rollup/rollup-linux-arm-musleabihf": "4.62.2",
+        "@rollup/rollup-linux-arm64-gnu": "4.62.2",
+        "@rollup/rollup-linux-arm64-musl": "4.62.2",
+        "@rollup/rollup-linux-loong64-gnu": "4.62.2",
+        "@rollup/rollup-linux-loong64-musl": "4.62.2",
+        "@rollup/rollup-linux-ppc64-gnu": "4.62.2",
+        "@rollup/rollup-linux-ppc64-musl": "4.62.2",
+        "@rollup/rollup-linux-riscv64-gnu": "4.62.2",
+        "@rollup/rollup-linux-riscv64-musl": "4.62.2",
+        "@rollup/rollup-linux-s390x-gnu": "4.62.2",
+        "@rollup/rollup-linux-x64-gnu": "4.62.2",
+        "@rollup/rollup-linux-x64-musl": "4.62.2",
+        "@rollup/rollup-openbsd-x64": "4.62.2",
+        "@rollup/rollup-openharmony-arm64": "4.62.2",
+        "@rollup/rollup-win32-arm64-msvc": "4.62.2",
+        "@rollup/rollup-win32-ia32-msvc": "4.62.2",
+        "@rollup/rollup-win32-x64-gnu": "4.62.2",
+        "@rollup/rollup-win32-x64-msvc": "4.62.2",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/scheduler": {
+      "version": "0.23.2",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+      "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      }
+    },
+    "node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/set-cookie-parser": {
+      "version": "2.7.2",
+      "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+      "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/sucrase": {
+      "version": "3.35.1",
+      "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+      "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.2",
+        "commander": "^4.0.0",
+        "lines-and-columns": "^1.1.6",
+        "mz": "^2.7.0",
+        "pirates": "^4.0.1",
+        "tinyglobby": "^0.2.11",
+        "ts-interface-checker": "^0.1.9"
+      },
+      "bin": {
+        "sucrase": "bin/sucrase",
+        "sucrase-node": "bin/sucrase-node"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/tailwind-merge": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
+      "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/dcastil"
+      }
+    },
+    "node_modules/tailwindcss": {
+      "version": "3.4.19",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
+      "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
+      "dev": true,
+      "dependencies": {
+        "@alloc/quick-lru": "^5.2.0",
+        "arg": "^5.0.2",
+        "chokidar": "^3.6.0",
+        "didyoumean": "^1.2.2",
+        "dlv": "^1.1.3",
+        "fast-glob": "^3.3.2",
+        "glob-parent": "^6.0.2",
+        "is-glob": "^4.0.3",
+        "jiti": "^1.21.7",
+        "lilconfig": "^3.1.3",
+        "micromatch": "^4.0.8",
+        "normalize-path": "^3.0.0",
+        "object-hash": "^3.0.0",
+        "picocolors": "^1.1.1",
+        "postcss": "^8.4.47",
+        "postcss-import": "^15.1.0",
+        "postcss-js": "^4.0.1",
+        "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
+        "postcss-nested": "^6.2.0",
+        "postcss-selector-parser": "^6.1.2",
+        "resolve": "^1.22.8",
+        "sucrase": "^3.35.0"
+      },
+      "bin": {
+        "tailwind": "lib/cli.js",
+        "tailwindcss": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/thenify": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+      "dev": true,
+      "dependencies": {
+        "any-promise": "^1.0.0"
+      }
+    },
+    "node_modules/thenify-all": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+      "dev": true,
+      "dependencies": {
+        "thenify": ">= 3.1.0 < 4"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.17",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+      "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+      "dev": true,
+      "dependencies": {
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
+    "node_modules/tinyglobby/node_modules/picomatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+      "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/ts-api-utils": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+      "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+      "dev": true,
+      "engines": {
+        "node": ">=18.12"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4"
+      }
+    },
+    "node_modules/ts-interface-checker": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+      "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+      "dev": true
+    },
+    "node_modules/tsconfck": {
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz",
+      "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==",
+      "deprecated": "unmaintained",
+      "dev": true,
+      "bin": {
+        "tsconfck": "bin/tsconfck.js"
+      },
+      "engines": {
+        "node": "^18 || >=20"
+      },
+      "peerDependencies": {
+        "typescript": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "dev": true
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.8.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+      "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+      "dev": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/typescript-eslint": {
+      "version": "8.63.0",
+      "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.63.0.tgz",
+      "integrity": "sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==",
+      "dev": true,
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "8.63.0",
+        "@typescript-eslint/parser": "8.63.0",
+        "@typescript-eslint/typescript-estree": "8.63.0",
+        "@typescript-eslint/utils": "8.63.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+        "typescript": ">=4.8.4 <6.1.0"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "dev": true
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+      "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "dev": true
+    },
+    "node_modules/vite": {
+      "version": "6.4.3",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+      "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.25.0",
+        "fdir": "^6.4.4",
+        "picomatch": "^4.0.2",
+        "postcss": "^8.5.3",
+        "rollup": "^4.34.9",
+        "tinyglobby": "^0.2.13"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+        "jiti": ">=1.21.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-plugin-trae-solo-badge": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/vite-plugin-trae-solo-badge/-/vite-plugin-trae-solo-badge-1.0.0.tgz",
+      "integrity": "sha512-vrNr4QmbcqqMeJPzHBqef70nqDzN6CQngFwuXTJMAgn3zMU4TWzvY0B3BRdjUjvg/BtvNJLlwu2cPKXTUjalHg==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^2.8.1"
+      },
+      "peerDependencies": {
+        "vite": ">=4.0.0"
+      }
+    },
+    "node_modules/vite-tsconfig-paths": {
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz",
+      "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.1.1",
+        "globrex": "^0.1.2",
+        "tsconfck": "^3.0.3"
+      },
+      "peerDependencies": {
+        "vite": "*"
+      },
+      "peerDependenciesMeta": {
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite/node_modules/picomatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+      "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zustand": {
+      "version": "5.0.14",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz",
+      "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==",
+      "engines": {
+        "node": ">=12.20.0"
+      },
+      "peerDependencies": {
+        "@types/react": ">=18.0.0",
+        "immer": ">=9.0.6",
+        "react": ">=18.0.0",
+        "use-sync-external-store": ">=1.2.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "immer": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "use-sync-external-store": {
+          "optional": true
+        }
+      }
+    }
+  }
+}

+ 43 - 0
package.json

@@ -0,0 +1,43 @@
+{
+  "name": "web",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "tsc -b && vite build",
+    "lint": "eslint .",
+    "preview": "vite preview",
+    "check": "tsc -b --noEmit"
+  },
+  "dependencies": {
+    "@rollup/rollup-darwin-arm64": "^4.62.2",
+    "clsx": "^2.1.1",
+    "lucide-react": "^0.511.0",
+    "react": "^18.3.1",
+    "react-dom": "^18.3.1",
+    "react-router-dom": "^7.18.1",
+    "tailwind-merge": "^3.0.2",
+    "zustand": "^5.0.3"
+  },
+  "devDependencies": {
+    "@eslint/js": "^9.25.0",
+    "@types/node": "^22.15.30",
+    "@types/react": "^18.3.12",
+    "@types/react-dom": "^18.3.1",
+    "@vitejs/plugin-react": "^4.4.1",
+    "autoprefixer": "^10.5.2",
+    "babel-plugin-react-dev-locator": "^1.0.0",
+    "eslint": "^9.25.0",
+    "eslint-plugin-react-hooks": "^5.2.0",
+    "eslint-plugin-react-refresh": "^0.4.19",
+    "globals": "^16.0.0",
+    "postcss": "^8.5.16",
+    "tailwindcss": "^3.4.19",
+    "typescript": "~5.8.3",
+    "typescript-eslint": "^8.30.1",
+    "vite": "^6.3.5",
+    "vite-plugin-trae-solo-badge": "^1.0.0",
+    "vite-tsconfig-paths": "^5.1.4"
+  }
+}

+ 2755 - 0
pnpm-lock.yaml

@@ -0,0 +1,2755 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      '@rollup/rollup-darwin-arm64':
+        specifier: ^4.62.2
+        version: 4.62.2
+      clsx:
+        specifier: ^2.1.1
+        version: 2.1.1
+      lucide-react:
+        specifier: ^0.511.0
+        version: 0.511.0(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-router-dom:
+        specifier: ^7.18.1
+        version: 7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      tailwind-merge:
+        specifier: ^3.0.2
+        version: 3.6.0
+      zustand:
+        specifier: ^5.0.3
+        version: 5.0.14(@types/react@18.3.31)(react@18.3.1)
+    devDependencies:
+      '@eslint/js':
+        specifier: ^9.25.0
+        version: 9.39.5
+      '@types/node':
+        specifier: ^22.15.30
+        version: 22.20.1
+      '@types/react':
+        specifier: ^18.3.12
+        version: 18.3.31
+      '@types/react-dom':
+        specifier: ^18.3.1
+        version: 18.3.7(@types/react@18.3.31)
+      '@vitejs/plugin-react':
+        specifier: ^4.4.1
+        version: 4.7.0(vite@6.4.3(@types/node@22.20.1)(jiti@1.21.7))
+      autoprefixer:
+        specifier: ^10.5.2
+        version: 10.5.3(postcss@8.5.19)
+      babel-plugin-react-dev-locator:
+        specifier: ^1.0.0
+        version: 1.0.6
+      eslint:
+        specifier: ^9.25.0
+        version: 9.39.5(jiti@1.21.7)
+      eslint-plugin-react-hooks:
+        specifier: ^5.2.0
+        version: 5.2.0(eslint@9.39.5(jiti@1.21.7))
+      eslint-plugin-react-refresh:
+        specifier: ^0.4.19
+        version: 0.4.26(eslint@9.39.5(jiti@1.21.7))
+      globals:
+        specifier: ^16.0.0
+        version: 16.5.0
+      postcss:
+        specifier: ^8.5.16
+        version: 8.5.19
+      tailwindcss:
+        specifier: ^3.4.19
+        version: 3.4.19
+      typescript:
+        specifier: ~5.8.3
+        version: 5.8.3
+      typescript-eslint:
+        specifier: ^8.30.1
+        version: 8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      vite:
+        specifier: ^6.3.5
+        version: 6.4.3(@types/node@22.20.1)(jiti@1.21.7)
+      vite-plugin-trae-solo-badge:
+        specifier: ^1.0.0
+        version: 1.0.0(vite@6.4.3(@types/node@22.20.1)(jiti@1.21.7))
+      vite-tsconfig-paths:
+        specifier: ^5.1.4
+        version: 5.1.4(typescript@5.8.3)(vite@6.4.3(@types/node@22.20.1)(jiti@1.21.7))
+
+packages:
+
+  '@alloc/quick-lru@5.2.0':
+    resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+    engines: {node: '>=10'}
+
+  '@babel/code-frame@7.29.7':
+    resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/compat-data@7.29.7':
+    resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/core@7.29.7':
+    resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/generator@7.29.7':
+    resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-compilation-targets@7.29.7':
+    resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-globals@7.29.7':
+    resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-imports@7.29.7':
+    resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-module-transforms@7.29.7':
+    resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+
+  '@babel/helper-plugin-utils@7.29.7':
+    resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-string-parser@7.29.7':
+    resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-identifier@7.29.7':
+    resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helper-validator-option@7.29.7':
+    resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/helpers@7.29.7':
+    resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/parser@7.29.7':
+    resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  '@babel/plugin-transform-react-jsx-self@7.29.7':
+    resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/plugin-transform-react-jsx-source@7.29.7':
+    resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+
+  '@babel/template@7.29.7':
+    resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/traverse@7.29.7':
+    resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==}
+    engines: {node: '>=6.9.0'}
+
+  '@babel/types@7.29.7':
+    resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
+    engines: {node: '>=6.9.0'}
+
+  '@esbuild/aix-ppc64@0.25.12':
+    resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [aix]
+
+  '@esbuild/android-arm64@0.25.12':
+    resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [android]
+
+  '@esbuild/android-arm@0.25.12':
+    resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [android]
+
+  '@esbuild/android-x64@0.25.12':
+    resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [android]
+
+  '@esbuild/darwin-arm64@0.25.12':
+    resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@esbuild/darwin-x64@0.25.12':
+    resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@esbuild/freebsd-arm64@0.25.12':
+    resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@esbuild/freebsd-x64@0.25.12':
+    resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@esbuild/linux-arm64@0.25.12':
+    resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@esbuild/linux-arm@0.25.12':
+    resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
+    engines: {node: '>=18'}
+    cpu: [arm]
+    os: [linux]
+
+  '@esbuild/linux-ia32@0.25.12':
+    resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [linux]
+
+  '@esbuild/linux-loong64@0.25.12':
+    resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
+    engines: {node: '>=18'}
+    cpu: [loong64]
+    os: [linux]
+
+  '@esbuild/linux-mips64el@0.25.12':
+    resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
+    engines: {node: '>=18'}
+    cpu: [mips64el]
+    os: [linux]
+
+  '@esbuild/linux-ppc64@0.25.12':
+    resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
+    engines: {node: '>=18'}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@esbuild/linux-riscv64@0.25.12':
+    resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
+    engines: {node: '>=18'}
+    cpu: [riscv64]
+    os: [linux]
+
+  '@esbuild/linux-s390x@0.25.12':
+    resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
+    engines: {node: '>=18'}
+    cpu: [s390x]
+    os: [linux]
+
+  '@esbuild/linux-x64@0.25.12':
+    resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [linux]
+
+  '@esbuild/netbsd-arm64@0.25.12':
+    resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [netbsd]
+
+  '@esbuild/netbsd-x64@0.25.12':
+    resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [netbsd]
+
+  '@esbuild/openbsd-arm64@0.25.12':
+    resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openbsd]
+
+  '@esbuild/openbsd-x64@0.25.12':
+    resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@esbuild/openharmony-arm64@0.25.12':
+    resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@esbuild/sunos-x64@0.25.12':
+    resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [sunos]
+
+  '@esbuild/win32-arm64@0.25.12':
+    resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
+    engines: {node: '>=18'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@esbuild/win32-ia32@0.25.12':
+    resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
+    engines: {node: '>=18'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@esbuild/win32-x64@0.25.12':
+    resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
+    engines: {node: '>=18'}
+    cpu: [x64]
+    os: [win32]
+
+  '@eslint-community/eslint-utils@4.9.1':
+    resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+  '@eslint-community/regexpp@4.12.2':
+    resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
+    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+  '@eslint/config-array@0.21.2':
+    resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/config-helpers@0.4.2':
+    resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/core@0.17.0':
+    resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/eslintrc@3.3.6':
+    resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/js@9.39.5':
+    resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/object-schema@2.1.7':
+    resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@eslint/plugin-kit@0.4.1':
+    resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@humanfs/core@0.19.2':
+    resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanfs/node@0.16.8':
+    resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanfs/types@0.15.0':
+    resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
+    engines: {node: '>=18.18.0'}
+
+  '@humanwhocodes/module-importer@1.0.1':
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+
+  '@humanwhocodes/retry@0.4.3':
+    resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+    engines: {node: '>=18.18'}
+
+  '@jridgewell/gen-mapping@0.3.13':
+    resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+  '@jridgewell/remapping@2.3.5':
+    resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
+  '@jridgewell/sourcemap-codec@1.5.5':
+    resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+  '@jridgewell/trace-mapping@0.3.31':
+    resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+  '@nodelib/fs.scandir@2.1.5':
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.stat@2.0.5':
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+
+  '@nodelib/fs.walk@1.2.8':
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+
+  '@rolldown/pluginutils@1.0.0-beta.27':
+    resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
+
+  '@rollup/rollup-android-arm-eabi@4.62.2':
+    resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==}
+    cpu: [arm]
+    os: [android]
+
+  '@rollup/rollup-android-arm64@4.62.2':
+    resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==}
+    cpu: [arm64]
+    os: [android]
+
+  '@rollup/rollup-darwin-arm64@4.62.2':
+    resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@rollup/rollup-darwin-x64@4.62.2':
+    resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==}
+    cpu: [x64]
+    os: [darwin]
+
+  '@rollup/rollup-freebsd-arm64@4.62.2':
+    resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==}
+    cpu: [arm64]
+    os: [freebsd]
+
+  '@rollup/rollup-freebsd-x64@4.62.2':
+    resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.62.2':
+    resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm-musleabihf@4.62.2':
+    resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-arm64-gnu@4.62.2':
+    resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-arm64-musl@4.62.2':
+    resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-loong64-gnu@4.62.2':
+    resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-loong64-musl@4.62.2':
+    resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-ppc64-gnu@4.62.2':
+    resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-ppc64-musl@4.62.2':
+    resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-riscv64-gnu@4.62.2':
+    resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-riscv64-musl@4.62.2':
+    resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-linux-s390x-gnu@4.62.2':
+    resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-gnu@4.62.2':
+    resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+
+  '@rollup/rollup-linux-x64-musl@4.62.2':
+    resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+
+  '@rollup/rollup-openbsd-x64@4.62.2':
+    resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==}
+    cpu: [x64]
+    os: [openbsd]
+
+  '@rollup/rollup-openharmony-arm64@4.62.2':
+    resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==}
+    cpu: [arm64]
+    os: [openharmony]
+
+  '@rollup/rollup-win32-arm64-msvc@4.62.2':
+    resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==}
+    cpu: [arm64]
+    os: [win32]
+
+  '@rollup/rollup-win32-ia32-msvc@4.62.2':
+    resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==}
+    cpu: [ia32]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-gnu@4.62.2':
+    resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==}
+    cpu: [x64]
+    os: [win32]
+
+  '@rollup/rollup-win32-x64-msvc@4.62.2':
+    resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==}
+    cpu: [x64]
+    os: [win32]
+
+  '@types/babel__core@7.20.5':
+    resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+  '@types/babel__generator@7.27.0':
+    resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+  '@types/babel__template@7.4.4':
+    resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+  '@types/babel__traverse@7.28.0':
+    resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+  '@types/estree@1.0.9':
+    resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+  '@types/json-schema@7.0.15':
+    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+  '@types/node@22.20.1':
+    resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==}
+
+  '@types/prop-types@15.7.15':
+    resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
+
+  '@types/react-dom@18.3.7':
+    resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==}
+    peerDependencies:
+      '@types/react': ^18.0.0
+
+  '@types/react@18.3.31':
+    resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==}
+
+  '@typescript-eslint/eslint-plugin@8.64.0':
+    resolution: {integrity: sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^8.64.0
+      eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+      typescript: '>=4.8.4 <6.1.0'
+
+  '@typescript-eslint/parser@8.64.0':
+    resolution: {integrity: sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+      typescript: '>=4.8.4 <6.1.0'
+
+  '@typescript-eslint/project-service@8.64.0':
+    resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <6.1.0'
+
+  '@typescript-eslint/scope-manager@8.64.0':
+    resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/tsconfig-utils@8.64.0':
+    resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <6.1.0'
+
+  '@typescript-eslint/type-utils@8.64.0':
+    resolution: {integrity: sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+      typescript: '>=4.8.4 <6.1.0'
+
+  '@typescript-eslint/types@8.64.0':
+    resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@typescript-eslint/typescript-estree@8.64.0':
+    resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      typescript: '>=4.8.4 <6.1.0'
+
+  '@typescript-eslint/utils@8.64.0':
+    resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+      typescript: '>=4.8.4 <6.1.0'
+
+  '@typescript-eslint/visitor-keys@8.64.0':
+    resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  '@vitejs/plugin-react@4.7.0':
+    resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+
+  acorn-jsx@5.3.2:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+  acorn@8.17.0:
+    resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  ajv@6.15.0:
+    resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+
+  ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+
+  any-promise@1.3.0:
+    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+  anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+
+  arg@5.0.2:
+    resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+  argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+  autoprefixer@10.5.3:
+    resolution: {integrity: sha512-bJRzflk8GgE4JX+iZNEwz9f9p460NCHnU7bd+CZ9vIjIlZuTkt6F3WSl2oNO8StZBFx17nLEsiQ6H2wcZiY7nA==}
+    engines: {node: ^10 || ^12 || >=14}
+    hasBin: true
+    peerDependencies:
+      postcss: ^8.1.0
+
+  babel-plugin-react-dev-locator@1.0.6:
+    resolution: {integrity: sha512-XWi+6x6e4NvVwvOqwitci/BZU1xbNfNuL94kfT4kWfP/d9p3RYRVOrogs1Z1otlYfQUO07cy/20z8eY9blFSpw==}
+    engines: {node: '>=12.0.0'}
+
+  balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+  balanced-match@4.0.4:
+    resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+    engines: {node: 18 || 20 || >=22}
+
+  baseline-browser-mapping@2.10.43:
+    resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+
+  binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+    engines: {node: '>=8'}
+
+  brace-expansion@1.1.16:
+    resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==}
+
+  brace-expansion@5.0.7:
+    resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==}
+    engines: {node: 18 || 20 || >=22}
+
+  braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+
+  browserslist@4.28.6:
+    resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+
+  callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+
+  camelcase-css@2.0.1:
+    resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+    engines: {node: '>= 6'}
+
+  caniuse-lite@1.0.30001805:
+    resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==}
+
+  chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+
+  chokidar@3.6.0:
+    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+    engines: {node: '>= 8.10.0'}
+
+  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'}
+
+  color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+  commander@4.1.1:
+    resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+    engines: {node: '>= 6'}
+
+  concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+  convert-source-map@2.0.0:
+    resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+  cookie@1.1.1:
+    resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
+    engines: {node: '>=18'}
+
+  cross-spawn@7.0.6:
+    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+    engines: {node: '>= 8'}
+
+  cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  csstype@3.2.3:
+    resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+  debug@4.4.3:
+    resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+  didyoumean@1.2.2:
+    resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+  dlv@1.1.3:
+    resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+  electron-to-chromium@1.5.392:
+    resolution: {integrity: sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==}
+
+  es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+
+  esbuild@0.25.12:
+    resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
+  escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+
+  eslint-plugin-react-hooks@5.2.0:
+    resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+
+  eslint-plugin-react-refresh@0.4.26:
+    resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==}
+    peerDependencies:
+      eslint: '>=8.40'
+
+  eslint-scope@8.4.0:
+    resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint-visitor-keys@3.4.3:
+    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+  eslint-visitor-keys@4.2.1:
+    resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  eslint-visitor-keys@5.0.1:
+    resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+    engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+  eslint@9.39.5:
+    resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    hasBin: true
+    peerDependencies:
+      jiti: '*'
+    peerDependenciesMeta:
+      jiti:
+        optional: true
+
+  espree@10.4.0:
+    resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+  esquery@1.7.0:
+    resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+    engines: {node: '>=0.10'}
+
+  esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+
+  estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+
+  esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+
+  fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+  fast-glob@3.3.3:
+    resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+    engines: {node: '>=8.6.0'}
+
+  fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+  fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+  fastq@1.20.1:
+    resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
+
+  fdir@6.5.0:
+    resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+    engines: {node: '>=12.0.0'}
+    peerDependencies:
+      picomatch: ^3 || ^4
+    peerDependenciesMeta:
+      picomatch:
+        optional: true
+
+  file-entry-cache@8.0.0:
+    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+    engines: {node: '>=16.0.0'}
+
+  fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+
+  find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+
+  flat-cache@4.0.1:
+    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+    engines: {node: '>=16'}
+
+  flatted@3.4.2:
+    resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
+
+  fraction.js@5.3.4:
+    resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
+
+  fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    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'}
+
+  glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+
+  glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+
+  globals@14.0.0:
+    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+    engines: {node: '>=18'}
+
+  globals@16.5.0:
+    resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==}
+    engines: {node: '>=18'}
+
+  globrex@0.1.2:
+    resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+
+  has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+
+  hasown@2.0.4:
+    resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
+    engines: {node: '>= 0.4'}
+
+  ignore@5.3.2:
+    resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+    engines: {node: '>= 4'}
+
+  ignore@7.0.6:
+    resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==}
+    engines: {node: '>= 4'}
+
+  import-fresh@3.3.1:
+    resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+    engines: {node: '>=6'}
+
+  imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+
+  is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+
+  is-core-module@2.16.2:
+    resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==}
+    engines: {node: '>= 0.4'}
+
+  is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+
+  is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+
+  is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+  jiti@1.21.7:
+    resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
+    hasBin: true
+
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  js-yaml@4.3.0:
+    resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==}
+    hasBin: true
+
+  jsesc@3.1.0:
+    resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  json-buffer@3.0.1:
+    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+  json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+  json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+  json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+  levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+
+  lilconfig@3.1.3:
+    resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+    engines: {node: '>=14'}
+
+  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'}
+
+  lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+  loose-envify@1.4.0:
+    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+    hasBin: true
+
+  lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+  lucide-react@0.511.0:
+    resolution: {integrity: sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==}
+    peerDependencies:
+      react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+  merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  micromatch@4.0.8:
+    resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+    engines: {node: '>=8.6'}
+
+  minimatch@10.2.5:
+    resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
+    engines: {node: 18 || 20 || >=22}
+
+  minimatch@3.1.5:
+    resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  mz@2.7.0:
+    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+  nanoid@3.3.16:
+    resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+  node-releases@2.0.51:
+    resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==}
+    engines: {node: '>=18'}
+
+  normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
+  object-hash@3.0.0:
+    resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+    engines: {node: '>= 6'}
+
+  optionator@0.9.4:
+    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+    engines: {node: '>= 0.8.0'}
+
+  p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+
+  p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+
+  parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+
+  path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+
+  path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+
+  path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  picomatch@2.3.2:
+    resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+    engines: {node: '>=8.6'}
+
+  picomatch@4.0.5:
+    resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
+    engines: {node: '>=12'}
+
+  pify@2.3.0:
+    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+    engines: {node: '>=0.10.0'}
+
+  pirates@4.0.7:
+    resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+    engines: {node: '>= 6'}
+
+  postcss-import@15.1.0:
+    resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      postcss: ^8.0.0
+
+  postcss-js@4.1.0:
+    resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==}
+    engines: {node: ^12 || ^14 || >= 16}
+    peerDependencies:
+      postcss: ^8.4.21
+
+  postcss-load-config@6.0.1:
+    resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
+    engines: {node: '>= 18'}
+    peerDependencies:
+      jiti: '>=1.21.0'
+      postcss: '>=8.0.9'
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      jiti:
+        optional: true
+      postcss:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+  postcss-nested@6.2.0:
+    resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+    engines: {node: '>=12.0'}
+    peerDependencies:
+      postcss: ^8.2.14
+
+  postcss-selector-parser@6.1.4:
+    resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==}
+    engines: {node: '>=4'}
+
+  postcss-value-parser@4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+  postcss@8.5.19:
+    resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==}
+    engines: {node: ^10 || ^12 || >=14}
+
+  prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
+  queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+  react-dom@18.3.1:
+    resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
+    peerDependencies:
+      react: ^18.3.1
+
+  react-refresh@0.17.0:
+    resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
+    engines: {node: '>=0.10.0'}
+
+  react-router-dom@7.18.1:
+    resolution: {integrity: sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==}
+    engines: {node: '>=20.0.0'}
+    peerDependencies:
+      react: '>=18'
+      react-dom: '>=18'
+
+  react-router@7.18.1:
+    resolution: {integrity: sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==}
+    engines: {node: '>=20.0.0'}
+    peerDependencies:
+      react: '>=18'
+      react-dom: '>=18'
+    peerDependenciesMeta:
+      react-dom:
+        optional: true
+
+  react@18.3.1:
+    resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
+    engines: {node: '>=0.10.0'}
+
+  read-cache@1.0.0:
+    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+  readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+
+  resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+
+  resolve@1.22.12:
+    resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
+    engines: {node: '>= 0.4'}
+    hasBin: true
+
+  reusify@1.1.0:
+    resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  rollup@4.62.2:
+    resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+
+  run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+  scheduler@0.23.2:
+    resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
+
+  semver@6.3.1:
+    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+    hasBin: true
+
+  semver@7.8.5:
+    resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
+    engines: {node: '>=10'}
+    hasBin: true
+
+  set-cookie-parser@2.7.2:
+    resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
+
+  shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+
+  shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+
+  source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+
+  sucrase@3.35.1:
+    resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+
+  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'}
+
+  tailwind-merge@3.6.0:
+    resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==}
+
+  tailwindcss@3.4.19:
+    resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
+  thenify-all@1.6.0:
+    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+    engines: {node: '>=0.8'}
+
+  thenify@3.3.1:
+    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+  tinyglobby@0.2.17:
+    resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+    engines: {node: '>=12.0.0'}
+
+  to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+
+  ts-api-utils@2.5.0:
+    resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
+    engines: {node: '>=18.12'}
+    peerDependencies:
+      typescript: '>=4.8.4'
+
+  ts-interface-checker@0.1.13:
+    resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+  tsconfck@3.1.6:
+    resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==}
+    engines: {node: ^18 || >=20}
+    deprecated: unmaintained
+    hasBin: true
+    peerDependencies:
+      typescript: ^5.0.0
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+
+  tslib@2.8.1:
+    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+  type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+
+  typescript-eslint@8.64.0:
+    resolution: {integrity: sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==}
+    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+    peerDependencies:
+      eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+      typescript: '>=4.8.4 <6.1.0'
+
+  typescript@5.8.3:
+    resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  undici-types@6.21.0:
+    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+  update-browserslist-db@1.2.3:
+    resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+
+  uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+  util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  vite-plugin-trae-solo-badge@1.0.0:
+    resolution: {integrity: sha512-vrNr4QmbcqqMeJPzHBqef70nqDzN6CQngFwuXTJMAgn3zMU4TWzvY0B3BRdjUjvg/BtvNJLlwu2cPKXTUjalHg==}
+    peerDependencies:
+      vite: '>=4.0.0'
+
+  vite-tsconfig-paths@5.1.4:
+    resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==}
+    peerDependencies:
+      vite: '*'
+    peerDependenciesMeta:
+      vite:
+        optional: true
+
+  vite@6.4.3:
+    resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==}
+    engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+      jiti: '>=1.21.0'
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      sass-embedded: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.16.0
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      jiti:
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+
+  which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+
+  word-wrap@1.2.5:
+    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+    engines: {node: '>=0.10.0'}
+
+  yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+
+  zustand@5.0.14:
+    resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==}
+    engines: {node: '>=12.20.0'}
+    peerDependencies:
+      '@types/react': '>=18.0.0'
+      immer: '>=9.0.6'
+      react: '>=18.0.0'
+      use-sync-external-store: '>=1.2.0'
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      immer:
+        optional: true
+      react:
+        optional: true
+      use-sync-external-store:
+        optional: true
+
+snapshots:
+
+  '@alloc/quick-lru@5.2.0': {}
+
+  '@babel/code-frame@7.29.7':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.29.7
+      js-tokens: 4.0.0
+      picocolors: 1.1.1
+
+  '@babel/compat-data@7.29.7': {}
+
+  '@babel/core@7.29.7':
+    dependencies:
+      '@babel/code-frame': 7.29.7
+      '@babel/generator': 7.29.7
+      '@babel/helper-compilation-targets': 7.29.7
+      '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
+      '@babel/helpers': 7.29.7
+      '@babel/parser': 7.29.7
+      '@babel/template': 7.29.7
+      '@babel/traverse': 7.29.7
+      '@babel/types': 7.29.7
+      '@jridgewell/remapping': 2.3.5
+      convert-source-map: 2.0.0
+      debug: 4.4.3
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/generator@7.29.7':
+    dependencies:
+      '@babel/parser': 7.29.7
+      '@babel/types': 7.29.7
+      '@jridgewell/gen-mapping': 0.3.13
+      '@jridgewell/trace-mapping': 0.3.31
+      jsesc: 3.1.0
+
+  '@babel/helper-compilation-targets@7.29.7':
+    dependencies:
+      '@babel/compat-data': 7.29.7
+      '@babel/helper-validator-option': 7.29.7
+      browserslist: 4.28.6
+      lru-cache: 5.1.1
+      semver: 6.3.1
+
+  '@babel/helper-globals@7.29.7': {}
+
+  '@babel/helper-module-imports@7.29.7':
+    dependencies:
+      '@babel/traverse': 7.29.7
+      '@babel/types': 7.29.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
+    dependencies:
+      '@babel/core': 7.29.7
+      '@babel/helper-module-imports': 7.29.7
+      '@babel/helper-validator-identifier': 7.29.7
+      '@babel/traverse': 7.29.7
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/helper-plugin-utils@7.29.7': {}
+
+  '@babel/helper-string-parser@7.29.7': {}
+
+  '@babel/helper-validator-identifier@7.29.7': {}
+
+  '@babel/helper-validator-option@7.29.7': {}
+
+  '@babel/helpers@7.29.7':
+    dependencies:
+      '@babel/template': 7.29.7
+      '@babel/types': 7.29.7
+
+  '@babel/parser@7.29.7':
+    dependencies:
+      '@babel/types': 7.29.7
+
+  '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)':
+    dependencies:
+      '@babel/core': 7.29.7
+      '@babel/helper-plugin-utils': 7.29.7
+
+  '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)':
+    dependencies:
+      '@babel/core': 7.29.7
+      '@babel/helper-plugin-utils': 7.29.7
+
+  '@babel/template@7.29.7':
+    dependencies:
+      '@babel/code-frame': 7.29.7
+      '@babel/parser': 7.29.7
+      '@babel/types': 7.29.7
+
+  '@babel/traverse@7.29.7':
+    dependencies:
+      '@babel/code-frame': 7.29.7
+      '@babel/generator': 7.29.7
+      '@babel/helper-globals': 7.29.7
+      '@babel/parser': 7.29.7
+      '@babel/template': 7.29.7
+      '@babel/types': 7.29.7
+      debug: 4.4.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@babel/types@7.29.7':
+    dependencies:
+      '@babel/helper-string-parser': 7.29.7
+      '@babel/helper-validator-identifier': 7.29.7
+
+  '@esbuild/aix-ppc64@0.25.12':
+    optional: true
+
+  '@esbuild/android-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/android-arm@0.25.12':
+    optional: true
+
+  '@esbuild/android-x64@0.25.12':
+    optional: true
+
+  '@esbuild/darwin-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/darwin-x64@0.25.12':
+    optional: true
+
+  '@esbuild/freebsd-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/freebsd-x64@0.25.12':
+    optional: true
+
+  '@esbuild/linux-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/linux-arm@0.25.12':
+    optional: true
+
+  '@esbuild/linux-ia32@0.25.12':
+    optional: true
+
+  '@esbuild/linux-loong64@0.25.12':
+    optional: true
+
+  '@esbuild/linux-mips64el@0.25.12':
+    optional: true
+
+  '@esbuild/linux-ppc64@0.25.12':
+    optional: true
+
+  '@esbuild/linux-riscv64@0.25.12':
+    optional: true
+
+  '@esbuild/linux-s390x@0.25.12':
+    optional: true
+
+  '@esbuild/linux-x64@0.25.12':
+    optional: true
+
+  '@esbuild/netbsd-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/netbsd-x64@0.25.12':
+    optional: true
+
+  '@esbuild/openbsd-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/openbsd-x64@0.25.12':
+    optional: true
+
+  '@esbuild/openharmony-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/sunos-x64@0.25.12':
+    optional: true
+
+  '@esbuild/win32-arm64@0.25.12':
+    optional: true
+
+  '@esbuild/win32-ia32@0.25.12':
+    optional: true
+
+  '@esbuild/win32-x64@0.25.12':
+    optional: true
+
+  '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@1.21.7))':
+    dependencies:
+      eslint: 9.39.5(jiti@1.21.7)
+      eslint-visitor-keys: 3.4.3
+
+  '@eslint-community/regexpp@4.12.2': {}
+
+  '@eslint/config-array@0.21.2':
+    dependencies:
+      '@eslint/object-schema': 2.1.7
+      debug: 4.4.3
+      minimatch: 3.1.5
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/config-helpers@0.4.2':
+    dependencies:
+      '@eslint/core': 0.17.0
+
+  '@eslint/core@0.17.0':
+    dependencies:
+      '@types/json-schema': 7.0.15
+
+  '@eslint/eslintrc@3.3.6':
+    dependencies:
+      ajv: 6.15.0
+      debug: 4.4.3
+      espree: 10.4.0
+      globals: 14.0.0
+      ignore: 5.3.2
+      import-fresh: 3.3.1
+      js-yaml: 4.3.0
+      minimatch: 3.1.5
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+
+  '@eslint/js@9.39.5': {}
+
+  '@eslint/object-schema@2.1.7': {}
+
+  '@eslint/plugin-kit@0.4.1':
+    dependencies:
+      '@eslint/core': 0.17.0
+      levn: 0.4.1
+
+  '@humanfs/core@0.19.2':
+    dependencies:
+      '@humanfs/types': 0.15.0
+
+  '@humanfs/node@0.16.8':
+    dependencies:
+      '@humanfs/core': 0.19.2
+      '@humanfs/types': 0.15.0
+      '@humanwhocodes/retry': 0.4.3
+
+  '@humanfs/types@0.15.0': {}
+
+  '@humanwhocodes/module-importer@1.0.1': {}
+
+  '@humanwhocodes/retry@0.4.3': {}
+
+  '@jridgewell/gen-mapping@0.3.13':
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.5
+      '@jridgewell/trace-mapping': 0.3.31
+
+  '@jridgewell/remapping@2.3.5':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.13
+      '@jridgewell/trace-mapping': 0.3.31
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
+  '@jridgewell/sourcemap-codec@1.5.5': {}
+
+  '@jridgewell/trace-mapping@0.3.31':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.5
+
+  '@nodelib/fs.scandir@2.1.5':
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+
+  '@nodelib/fs.stat@2.0.5': {}
+
+  '@nodelib/fs.walk@1.2.8':
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.20.1
+
+  '@rolldown/pluginutils@1.0.0-beta.27': {}
+
+  '@rollup/rollup-android-arm-eabi@4.62.2':
+    optional: true
+
+  '@rollup/rollup-android-arm64@4.62.2':
+    optional: true
+
+  '@rollup/rollup-darwin-arm64@4.62.2': {}
+
+  '@rollup/rollup-darwin-x64@4.62.2':
+    optional: true
+
+  '@rollup/rollup-freebsd-arm64@4.62.2':
+    optional: true
+
+  '@rollup/rollup-freebsd-x64@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm-gnueabihf@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm-musleabihf@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-gnu@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-arm64-musl@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-loong64-gnu@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-loong64-musl@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-ppc64-gnu@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-ppc64-musl@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-gnu@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-riscv64-musl@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-s390x-gnu@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-x64-gnu@4.62.2':
+    optional: true
+
+  '@rollup/rollup-linux-x64-musl@4.62.2':
+    optional: true
+
+  '@rollup/rollup-openbsd-x64@4.62.2':
+    optional: true
+
+  '@rollup/rollup-openharmony-arm64@4.62.2':
+    optional: true
+
+  '@rollup/rollup-win32-arm64-msvc@4.62.2':
+    optional: true
+
+  '@rollup/rollup-win32-ia32-msvc@4.62.2':
+    optional: true
+
+  '@rollup/rollup-win32-x64-gnu@4.62.2':
+    optional: true
+
+  '@rollup/rollup-win32-x64-msvc@4.62.2':
+    optional: true
+
+  '@types/babel__core@7.20.5':
+    dependencies:
+      '@babel/parser': 7.29.7
+      '@babel/types': 7.29.7
+      '@types/babel__generator': 7.27.0
+      '@types/babel__template': 7.4.4
+      '@types/babel__traverse': 7.28.0
+
+  '@types/babel__generator@7.27.0':
+    dependencies:
+      '@babel/types': 7.29.7
+
+  '@types/babel__template@7.4.4':
+    dependencies:
+      '@babel/parser': 7.29.7
+      '@babel/types': 7.29.7
+
+  '@types/babel__traverse@7.28.0':
+    dependencies:
+      '@babel/types': 7.29.7
+
+  '@types/estree@1.0.9': {}
+
+  '@types/json-schema@7.0.15': {}
+
+  '@types/node@22.20.1':
+    dependencies:
+      undici-types: 6.21.0
+
+  '@types/prop-types@15.7.15': {}
+
+  '@types/react-dom@18.3.7(@types/react@18.3.31)':
+    dependencies:
+      '@types/react': 18.3.31
+
+  '@types/react@18.3.31':
+    dependencies:
+      '@types/prop-types': 15.7.15
+      csstype: 3.2.3
+
+  '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)':
+    dependencies:
+      '@eslint-community/regexpp': 4.12.2
+      '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      '@typescript-eslint/scope-manager': 8.64.0
+      '@typescript-eslint/type-utils': 8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      '@typescript-eslint/visitor-keys': 8.64.0
+      eslint: 9.39.5(jiti@1.21.7)
+      ignore: 7.0.6
+      natural-compare: 1.4.0
+      ts-api-utils: 2.5.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/scope-manager': 8.64.0
+      '@typescript-eslint/types': 8.64.0
+      '@typescript-eslint/typescript-estree': 8.64.0(typescript@5.8.3)
+      '@typescript-eslint/visitor-keys': 8.64.0
+      debug: 4.4.3
+      eslint: 9.39.5(jiti@1.21.7)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/project-service@8.64.0(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@5.8.3)
+      '@typescript-eslint/types': 8.64.0
+      debug: 4.4.3
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/scope-manager@8.64.0':
+    dependencies:
+      '@typescript-eslint/types': 8.64.0
+      '@typescript-eslint/visitor-keys': 8.64.0
+
+  '@typescript-eslint/tsconfig-utils@8.64.0(typescript@5.8.3)':
+    dependencies:
+      typescript: 5.8.3
+
+  '@typescript-eslint/type-utils@8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/types': 8.64.0
+      '@typescript-eslint/typescript-estree': 8.64.0(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      debug: 4.4.3
+      eslint: 9.39.5(jiti@1.21.7)
+      ts-api-utils: 2.5.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/types@8.64.0': {}
+
+  '@typescript-eslint/typescript-estree@8.64.0(typescript@5.8.3)':
+    dependencies:
+      '@typescript-eslint/project-service': 8.64.0(typescript@5.8.3)
+      '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@5.8.3)
+      '@typescript-eslint/types': 8.64.0
+      '@typescript-eslint/visitor-keys': 8.64.0
+      debug: 4.4.3
+      minimatch: 10.2.5
+      semver: 7.8.5
+      tinyglobby: 0.2.17
+      ts-api-utils: 2.5.0(typescript@5.8.3)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)':
+    dependencies:
+      '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@1.21.7))
+      '@typescript-eslint/scope-manager': 8.64.0
+      '@typescript-eslint/types': 8.64.0
+      '@typescript-eslint/typescript-estree': 8.64.0(typescript@5.8.3)
+      eslint: 9.39.5(jiti@1.21.7)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  '@typescript-eslint/visitor-keys@8.64.0':
+    dependencies:
+      '@typescript-eslint/types': 8.64.0
+      eslint-visitor-keys: 5.0.1
+
+  '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@22.20.1)(jiti@1.21.7))':
+    dependencies:
+      '@babel/core': 7.29.7
+      '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7)
+      '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7)
+      '@rolldown/pluginutils': 1.0.0-beta.27
+      '@types/babel__core': 7.20.5
+      react-refresh: 0.17.0
+      vite: 6.4.3(@types/node@22.20.1)(jiti@1.21.7)
+    transitivePeerDependencies:
+      - supports-color
+
+  acorn-jsx@5.3.2(acorn@8.17.0):
+    dependencies:
+      acorn: 8.17.0
+
+  acorn@8.17.0: {}
+
+  ajv@6.15.0:
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+
+  ansi-styles@4.3.0:
+    dependencies:
+      color-convert: 2.0.1
+
+  any-promise@1.3.0: {}
+
+  anymatch@3.1.3:
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.2
+
+  arg@5.0.2: {}
+
+  argparse@2.0.1: {}
+
+  autoprefixer@10.5.3(postcss@8.5.19):
+    dependencies:
+      browserslist: 4.28.6
+      caniuse-lite: 1.0.30001805
+      fraction.js: 5.3.4
+      picocolors: 1.1.1
+      postcss: 8.5.19
+      postcss-value-parser: 4.2.0
+
+  babel-plugin-react-dev-locator@1.0.6:
+    dependencies:
+      '@babel/core': 7.29.7
+      '@babel/generator': 7.29.7
+      '@babel/parser': 7.29.7
+      '@babel/traverse': 7.29.7
+      '@babel/types': 7.29.7
+    transitivePeerDependencies:
+      - supports-color
+
+  balanced-match@1.0.2: {}
+
+  balanced-match@4.0.4: {}
+
+  baseline-browser-mapping@2.10.43: {}
+
+  binary-extensions@2.3.0: {}
+
+  brace-expansion@1.1.16:
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+
+  brace-expansion@5.0.7:
+    dependencies:
+      balanced-match: 4.0.4
+
+  braces@3.0.3:
+    dependencies:
+      fill-range: 7.1.1
+
+  browserslist@4.28.6:
+    dependencies:
+      baseline-browser-mapping: 2.10.43
+      caniuse-lite: 1.0.30001805
+      electron-to-chromium: 1.5.392
+      node-releases: 2.0.51
+      update-browserslist-db: 1.2.3(browserslist@4.28.6)
+
+  callsites@3.1.0: {}
+
+  camelcase-css@2.0.1: {}
+
+  caniuse-lite@1.0.30001805: {}
+
+  chalk@4.1.2:
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+
+  chokidar@3.6.0:
+    dependencies:
+      anymatch: 3.1.3
+      braces: 3.0.3
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.3
+
+  clsx@2.1.1: {}
+
+  color-convert@2.0.1:
+    dependencies:
+      color-name: 1.1.4
+
+  color-name@1.1.4: {}
+
+  commander@4.1.1: {}
+
+  concat-map@0.0.1: {}
+
+  convert-source-map@2.0.0: {}
+
+  cookie@1.1.1: {}
+
+  cross-spawn@7.0.6:
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+
+  cssesc@3.0.0: {}
+
+  csstype@3.2.3: {}
+
+  debug@4.4.3:
+    dependencies:
+      ms: 2.1.3
+
+  deep-is@0.1.4: {}
+
+  didyoumean@1.2.2: {}
+
+  dlv@1.1.3: {}
+
+  electron-to-chromium@1.5.392: {}
+
+  es-errors@1.3.0: {}
+
+  esbuild@0.25.12:
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.25.12
+      '@esbuild/android-arm': 0.25.12
+      '@esbuild/android-arm64': 0.25.12
+      '@esbuild/android-x64': 0.25.12
+      '@esbuild/darwin-arm64': 0.25.12
+      '@esbuild/darwin-x64': 0.25.12
+      '@esbuild/freebsd-arm64': 0.25.12
+      '@esbuild/freebsd-x64': 0.25.12
+      '@esbuild/linux-arm': 0.25.12
+      '@esbuild/linux-arm64': 0.25.12
+      '@esbuild/linux-ia32': 0.25.12
+      '@esbuild/linux-loong64': 0.25.12
+      '@esbuild/linux-mips64el': 0.25.12
+      '@esbuild/linux-ppc64': 0.25.12
+      '@esbuild/linux-riscv64': 0.25.12
+      '@esbuild/linux-s390x': 0.25.12
+      '@esbuild/linux-x64': 0.25.12
+      '@esbuild/netbsd-arm64': 0.25.12
+      '@esbuild/netbsd-x64': 0.25.12
+      '@esbuild/openbsd-arm64': 0.25.12
+      '@esbuild/openbsd-x64': 0.25.12
+      '@esbuild/openharmony-arm64': 0.25.12
+      '@esbuild/sunos-x64': 0.25.12
+      '@esbuild/win32-arm64': 0.25.12
+      '@esbuild/win32-ia32': 0.25.12
+      '@esbuild/win32-x64': 0.25.12
+
+  escalade@3.2.0: {}
+
+  escape-string-regexp@4.0.0: {}
+
+  eslint-plugin-react-hooks@5.2.0(eslint@9.39.5(jiti@1.21.7)):
+    dependencies:
+      eslint: 9.39.5(jiti@1.21.7)
+
+  eslint-plugin-react-refresh@0.4.26(eslint@9.39.5(jiti@1.21.7)):
+    dependencies:
+      eslint: 9.39.5(jiti@1.21.7)
+
+  eslint-scope@8.4.0:
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+
+  eslint-visitor-keys@3.4.3: {}
+
+  eslint-visitor-keys@4.2.1: {}
+
+  eslint-visitor-keys@5.0.1: {}
+
+  eslint@9.39.5(jiti@1.21.7):
+    dependencies:
+      '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@1.21.7))
+      '@eslint-community/regexpp': 4.12.2
+      '@eslint/config-array': 0.21.2
+      '@eslint/config-helpers': 0.4.2
+      '@eslint/core': 0.17.0
+      '@eslint/eslintrc': 3.3.6
+      '@eslint/js': 9.39.5
+      '@eslint/plugin-kit': 0.4.1
+      '@humanfs/node': 0.16.8
+      '@humanwhocodes/module-importer': 1.0.1
+      '@humanwhocodes/retry': 0.4.3
+      '@types/estree': 1.0.9
+      ajv: 6.15.0
+      chalk: 4.1.2
+      cross-spawn: 7.0.6
+      debug: 4.4.3
+      escape-string-regexp: 4.0.0
+      eslint-scope: 8.4.0
+      eslint-visitor-keys: 4.2.1
+      espree: 10.4.0
+      esquery: 1.7.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 8.0.0
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      ignore: 5.3.2
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      json-stable-stringify-without-jsonify: 1.0.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.5
+      natural-compare: 1.4.0
+      optionator: 0.9.4
+    optionalDependencies:
+      jiti: 1.21.7
+    transitivePeerDependencies:
+      - supports-color
+
+  espree@10.4.0:
+    dependencies:
+      acorn: 8.17.0
+      acorn-jsx: 5.3.2(acorn@8.17.0)
+      eslint-visitor-keys: 4.2.1
+
+  esquery@1.7.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  esrecurse@4.3.0:
+    dependencies:
+      estraverse: 5.3.0
+
+  estraverse@5.3.0: {}
+
+  esutils@2.0.3: {}
+
+  fast-deep-equal@3.1.3: {}
+
+  fast-glob@3.3.3:
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.8
+
+  fast-json-stable-stringify@2.1.0: {}
+
+  fast-levenshtein@2.0.6: {}
+
+  fastq@1.20.1:
+    dependencies:
+      reusify: 1.1.0
+
+  fdir@6.5.0(picomatch@4.0.5):
+    optionalDependencies:
+      picomatch: 4.0.5
+
+  file-entry-cache@8.0.0:
+    dependencies:
+      flat-cache: 4.0.1
+
+  fill-range@7.1.1:
+    dependencies:
+      to-regex-range: 5.0.1
+
+  find-up@5.0.0:
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+
+  flat-cache@4.0.1:
+    dependencies:
+      flatted: 3.4.2
+      keyv: 4.5.4
+
+  flatted@3.4.2: {}
+
+  fraction.js@5.3.4: {}
+
+  fsevents@2.3.3:
+    optional: true
+
+  function-bind@1.1.2: {}
+
+  gensync@1.0.0-beta.2: {}
+
+  glob-parent@5.1.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  glob-parent@6.0.2:
+    dependencies:
+      is-glob: 4.0.3
+
+  globals@14.0.0: {}
+
+  globals@16.5.0: {}
+
+  globrex@0.1.2: {}
+
+  has-flag@4.0.0: {}
+
+  hasown@2.0.4:
+    dependencies:
+      function-bind: 1.1.2
+
+  ignore@5.3.2: {}
+
+  ignore@7.0.6: {}
+
+  import-fresh@3.3.1:
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+
+  imurmurhash@0.1.4: {}
+
+  is-binary-path@2.1.0:
+    dependencies:
+      binary-extensions: 2.3.0
+
+  is-core-module@2.16.2:
+    dependencies:
+      hasown: 2.0.4
+
+  is-extglob@2.1.1: {}
+
+  is-glob@4.0.3:
+    dependencies:
+      is-extglob: 2.1.1
+
+  is-number@7.0.0: {}
+
+  isexe@2.0.0: {}
+
+  jiti@1.21.7: {}
+
+  js-tokens@4.0.0: {}
+
+  js-yaml@4.3.0:
+    dependencies:
+      argparse: 2.0.1
+
+  jsesc@3.1.0: {}
+
+  json-buffer@3.0.1: {}
+
+  json-schema-traverse@0.4.1: {}
+
+  json-stable-stringify-without-jsonify@1.0.1: {}
+
+  json5@2.2.3: {}
+
+  keyv@4.5.4:
+    dependencies:
+      json-buffer: 3.0.1
+
+  levn@0.4.1:
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+
+  lilconfig@3.1.3: {}
+
+  lines-and-columns@1.2.4: {}
+
+  locate-path@6.0.0:
+    dependencies:
+      p-locate: 5.0.0
+
+  lodash.merge@4.6.2: {}
+
+  loose-envify@1.4.0:
+    dependencies:
+      js-tokens: 4.0.0
+
+  lru-cache@5.1.1:
+    dependencies:
+      yallist: 3.1.1
+
+  lucide-react@0.511.0(react@18.3.1):
+    dependencies:
+      react: 18.3.1
+
+  merge2@1.4.1: {}
+
+  micromatch@4.0.8:
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.2
+
+  minimatch@10.2.5:
+    dependencies:
+      brace-expansion: 5.0.7
+
+  minimatch@3.1.5:
+    dependencies:
+      brace-expansion: 1.1.16
+
+  ms@2.1.3: {}
+
+  mz@2.7.0:
+    dependencies:
+      any-promise: 1.3.0
+      object-assign: 4.1.1
+      thenify-all: 1.6.0
+
+  nanoid@3.3.16: {}
+
+  natural-compare@1.4.0: {}
+
+  node-releases@2.0.51: {}
+
+  normalize-path@3.0.0: {}
+
+  object-assign@4.1.1: {}
+
+  object-hash@3.0.0: {}
+
+  optionator@0.9.4:
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.5
+
+  p-limit@3.1.0:
+    dependencies:
+      yocto-queue: 0.1.0
+
+  p-locate@5.0.0:
+    dependencies:
+      p-limit: 3.1.0
+
+  parent-module@1.0.1:
+    dependencies:
+      callsites: 3.1.0
+
+  path-exists@4.0.0: {}
+
+  path-key@3.1.1: {}
+
+  path-parse@1.0.7: {}
+
+  picocolors@1.1.1: {}
+
+  picomatch@2.3.2: {}
+
+  picomatch@4.0.5: {}
+
+  pify@2.3.0: {}
+
+  pirates@4.0.7: {}
+
+  postcss-import@15.1.0(postcss@8.5.19):
+    dependencies:
+      postcss: 8.5.19
+      postcss-value-parser: 4.2.0
+      read-cache: 1.0.0
+      resolve: 1.22.12
+
+  postcss-js@4.1.0(postcss@8.5.19):
+    dependencies:
+      camelcase-css: 2.0.1
+      postcss: 8.5.19
+
+  postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.19):
+    dependencies:
+      lilconfig: 3.1.3
+    optionalDependencies:
+      jiti: 1.21.7
+      postcss: 8.5.19
+
+  postcss-nested@6.2.0(postcss@8.5.19):
+    dependencies:
+      postcss: 8.5.19
+      postcss-selector-parser: 6.1.4
+
+  postcss-selector-parser@6.1.4:
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  postcss-value-parser@4.2.0: {}
+
+  postcss@8.5.19:
+    dependencies:
+      nanoid: 3.3.16
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  prelude-ls@1.2.1: {}
+
+  punycode@2.3.1: {}
+
+  queue-microtask@1.2.3: {}
+
+  react-dom@18.3.1(react@18.3.1):
+    dependencies:
+      loose-envify: 1.4.0
+      react: 18.3.1
+      scheduler: 0.23.2
+
+  react-refresh@0.17.0: {}
+
+  react-router-dom@7.18.1(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.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+
+  react-router@7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+    dependencies:
+      cookie: 1.1.1
+      react: 18.3.1
+      set-cookie-parser: 2.7.2
+    optionalDependencies:
+      react-dom: 18.3.1(react@18.3.1)
+
+  react@18.3.1:
+    dependencies:
+      loose-envify: 1.4.0
+
+  read-cache@1.0.0:
+    dependencies:
+      pify: 2.3.0
+
+  readdirp@3.6.0:
+    dependencies:
+      picomatch: 2.3.2
+
+  resolve-from@4.0.0: {}
+
+  resolve@1.22.12:
+    dependencies:
+      es-errors: 1.3.0
+      is-core-module: 2.16.2
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+
+  reusify@1.1.0: {}
+
+  rollup@4.62.2:
+    dependencies:
+      '@types/estree': 1.0.9
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.62.2
+      '@rollup/rollup-android-arm64': 4.62.2
+      '@rollup/rollup-darwin-arm64': 4.62.2
+      '@rollup/rollup-darwin-x64': 4.62.2
+      '@rollup/rollup-freebsd-arm64': 4.62.2
+      '@rollup/rollup-freebsd-x64': 4.62.2
+      '@rollup/rollup-linux-arm-gnueabihf': 4.62.2
+      '@rollup/rollup-linux-arm-musleabihf': 4.62.2
+      '@rollup/rollup-linux-arm64-gnu': 4.62.2
+      '@rollup/rollup-linux-arm64-musl': 4.62.2
+      '@rollup/rollup-linux-loong64-gnu': 4.62.2
+      '@rollup/rollup-linux-loong64-musl': 4.62.2
+      '@rollup/rollup-linux-ppc64-gnu': 4.62.2
+      '@rollup/rollup-linux-ppc64-musl': 4.62.2
+      '@rollup/rollup-linux-riscv64-gnu': 4.62.2
+      '@rollup/rollup-linux-riscv64-musl': 4.62.2
+      '@rollup/rollup-linux-s390x-gnu': 4.62.2
+      '@rollup/rollup-linux-x64-gnu': 4.62.2
+      '@rollup/rollup-linux-x64-musl': 4.62.2
+      '@rollup/rollup-openbsd-x64': 4.62.2
+      '@rollup/rollup-openharmony-arm64': 4.62.2
+      '@rollup/rollup-win32-arm64-msvc': 4.62.2
+      '@rollup/rollup-win32-ia32-msvc': 4.62.2
+      '@rollup/rollup-win32-x64-gnu': 4.62.2
+      '@rollup/rollup-win32-x64-msvc': 4.62.2
+      fsevents: 2.3.3
+
+  run-parallel@1.2.0:
+    dependencies:
+      queue-microtask: 1.2.3
+
+  scheduler@0.23.2:
+    dependencies:
+      loose-envify: 1.4.0
+
+  semver@6.3.1: {}
+
+  semver@7.8.5: {}
+
+  set-cookie-parser@2.7.2: {}
+
+  shebang-command@2.0.0:
+    dependencies:
+      shebang-regex: 3.0.0
+
+  shebang-regex@3.0.0: {}
+
+  source-map-js@1.2.1: {}
+
+  strip-json-comments@3.1.1: {}
+
+  sucrase@3.35.1:
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.13
+      commander: 4.1.1
+      lines-and-columns: 1.2.4
+      mz: 2.7.0
+      pirates: 4.0.7
+      tinyglobby: 0.2.17
+      ts-interface-checker: 0.1.13
+
+  supports-color@7.2.0:
+    dependencies:
+      has-flag: 4.0.0
+
+  supports-preserve-symlinks-flag@1.0.0: {}
+
+  tailwind-merge@3.6.0: {}
+
+  tailwindcss@3.4.19:
+    dependencies:
+      '@alloc/quick-lru': 5.2.0
+      arg: 5.0.2
+      chokidar: 3.6.0
+      didyoumean: 1.2.2
+      dlv: 1.1.3
+      fast-glob: 3.3.3
+      glob-parent: 6.0.2
+      is-glob: 4.0.3
+      jiti: 1.21.7
+      lilconfig: 3.1.3
+      micromatch: 4.0.8
+      normalize-path: 3.0.0
+      object-hash: 3.0.0
+      picocolors: 1.1.1
+      postcss: 8.5.19
+      postcss-import: 15.1.0(postcss@8.5.19)
+      postcss-js: 4.1.0(postcss@8.5.19)
+      postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.19)
+      postcss-nested: 6.2.0(postcss@8.5.19)
+      postcss-selector-parser: 6.1.4
+      resolve: 1.22.12
+      sucrase: 3.35.1
+    transitivePeerDependencies:
+      - tsx
+      - yaml
+
+  thenify-all@1.6.0:
+    dependencies:
+      thenify: 3.3.1
+
+  thenify@3.3.1:
+    dependencies:
+      any-promise: 1.3.0
+
+  tinyglobby@0.2.17:
+    dependencies:
+      fdir: 6.5.0(picomatch@4.0.5)
+      picomatch: 4.0.5
+
+  to-regex-range@5.0.1:
+    dependencies:
+      is-number: 7.0.0
+
+  ts-api-utils@2.5.0(typescript@5.8.3):
+    dependencies:
+      typescript: 5.8.3
+
+  ts-interface-checker@0.1.13: {}
+
+  tsconfck@3.1.6(typescript@5.8.3):
+    optionalDependencies:
+      typescript: 5.8.3
+
+  tslib@2.8.1: {}
+
+  type-check@0.4.0:
+    dependencies:
+      prelude-ls: 1.2.1
+
+  typescript-eslint@8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3):
+    dependencies:
+      '@typescript-eslint/eslint-plugin': 8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      '@typescript-eslint/typescript-estree': 8.64.0(typescript@5.8.3)
+      '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.8.3)
+      eslint: 9.39.5(jiti@1.21.7)
+      typescript: 5.8.3
+    transitivePeerDependencies:
+      - supports-color
+
+  typescript@5.8.3: {}
+
+  undici-types@6.21.0: {}
+
+  update-browserslist-db@1.2.3(browserslist@4.28.6):
+    dependencies:
+      browserslist: 4.28.6
+      escalade: 3.2.0
+      picocolors: 1.1.1
+
+  uri-js@4.4.1:
+    dependencies:
+      punycode: 2.3.1
+
+  util-deprecate@1.0.2: {}
+
+  vite-plugin-trae-solo-badge@1.0.0(vite@6.4.3(@types/node@22.20.1)(jiti@1.21.7)):
+    dependencies:
+      tslib: 2.8.1
+      vite: 6.4.3(@types/node@22.20.1)(jiti@1.21.7)
+
+  vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.4.3(@types/node@22.20.1)(jiti@1.21.7)):
+    dependencies:
+      debug: 4.4.3
+      globrex: 0.1.2
+      tsconfck: 3.1.6(typescript@5.8.3)
+    optionalDependencies:
+      vite: 6.4.3(@types/node@22.20.1)(jiti@1.21.7)
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+
+  vite@6.4.3(@types/node@22.20.1)(jiti@1.21.7):
+    dependencies:
+      esbuild: 0.25.12
+      fdir: 6.5.0(picomatch@4.0.5)
+      picomatch: 4.0.5
+      postcss: 8.5.19
+      rollup: 4.62.2
+      tinyglobby: 0.2.17
+    optionalDependencies:
+      '@types/node': 22.20.1
+      fsevents: 2.3.3
+      jiti: 1.21.7
+
+  which@2.0.2:
+    dependencies:
+      isexe: 2.0.0
+
+  word-wrap@1.2.5: {}
+
+  yallist@3.1.1: {}
+
+  yocto-queue@0.1.0: {}
+
+  zustand@5.0.14(@types/react@18.3.31)(react@18.3.1):
+    optionalDependencies:
+      '@types/react': 18.3.31
+      react: 18.3.1

+ 2 - 0
pnpm-workspace.yaml

@@ -0,0 +1,2 @@
+allowBuilds:
+  esbuild: set this to true or false

+ 10 - 0
postcss.config.js

@@ -0,0 +1,10 @@
+/** WARNING: DON'T EDIT THIS FILE */
+/** WARNING: DON'T EDIT THIS FILE */
+/** WARNING: DON'T EDIT THIS FILE */
+
+export default {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+};

+ 368 - 0
public/favicon.svg

@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">  <image id="image0" width="512" height="512" x="0" y="0"
+    xlink:href="data:image/png;base64,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" />
+</svg>

+ 305 - 0
public/logo.svg

@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="593px" height="337px" viewBox="0 0 593 337" enable-background="new 0 0 593 337" xml:space="preserve">  <image id="image0" width="593" height="337" x="0" y="0"
+    xlink:href="data:image/png;base64,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
+j1x0pqU6CiIiIhWYRBXPVW/UhOoJGDt1rOpzKJoEko9ctCimOg4iIiJVmEQVqff++hSKHMqqGC8w
+6STde6G4Ph4REZU3JlHutKCIkgeTjj8IYrxQHbsb8ccvWpRQHQQREZFKTKJc2Hf/pRaksx6ZMZPG
+4KCj9V4fD5DshSIiorLHJMql3gcujQFI2t3+kPnaD+O1P3ZxJKU6CCIiItWYRHnDVs/M+MPHYfzh
+41TH6kZqP0saEBERAWAS5YneBy5NAIgX2m7KvMmqQ3VFQLY8cXHEUh0HERFREDCJ8s6IvVEHH38Q
+xkzS+nInfnRxTUx1EEREREGh9V/1INn34GUp5FlXr2KcwEFHTVAdoitCertmIBERke6YRHmrDcOs
+qzd5ziSIcVqXNIg9vqomoToIIiKiIGES5aGeBy+zIOWgHpuxxhhMNPVeH28/SxoQEREdgEmUx/Zt
+vTyGnHX1poQPUh2SW+0/XnWWpToIIiKioGES5Y8WAJgwYxzGH6bv+ngAUj9edVaz6iCIiIiCiEmU
+D/ZtvTwxZkJFbLL+vVAcxiMiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi
+IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiyvH/
+Abxd/fDVI8TMAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI2LTA3LTE2VDA2OjA1OjI1KzAwOjAwtrUp
+3QAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNi0wNy0xNlQwNjowNToyNSswMDowMMfokWEAAAAodEVY
+dGRhdGU6dGltZXN0YW1wADIwMjYtMDctMTZUMDY6MDU6MjUrMDA6MDCQ/bC+AAAAGXRFWHRTb2Z0
+d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAABJRU5ErkJggg==" />
+</svg>

+ 34 - 0
src/App.tsx

@@ -0,0 +1,34 @@
+import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
+import { Header } from '@/components/layout/Header';
+import { Footer } from '@/components/layout/Footer';
+import { Home } from '@/pages/Home';
+import { Product } from '@/pages/Product';
+import { Solutions } from '@/pages/Solutions';
+import { Cases } from '@/pages/Cases';
+import { About } from '@/pages/About';
+import { Login } from '@/pages/Login';
+import { Dashboard } from '@/pages/Dashboard';
+
+function App() {
+  return (
+    <Router>
+      <div className="min-h-screen">
+        <Header />
+        <main>
+          <Routes>
+            <Route path="/" element={<Home />} />
+            <Route path="/product" element={<Product />} />
+            <Route path="/solutions" element={<Solutions />} />
+            <Route path="/cases" element={<Cases />} />
+            <Route path="/about" element={<About />} />
+            <Route path="/login" element={<Login />} />
+            <Route path="/dashboard" element={<Dashboard />} />
+          </Routes>
+        </main>
+        <Footer />
+      </div>
+    </Router>
+  );
+}
+
+export default App;

+ 294 - 0
src/api/mockApi.ts

@@ -0,0 +1,294 @@
+import type { Channel, Model, Enterprise, Department, User, APIKey, CallLog, TokenStats, CostStats, DashboardData } from '@/types';
+
+const channels: Channel[] = [
+  { id: 'c1', name: 'DeepSeek官方', provider: 'deepseek', apiKeys: ['sk-xxx1', 'sk-xxx2'], weight: 100, priority: 1, status: 'enabled', baseUrl: 'https://api.deepseek.com', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'c2', name: '通义千问', provider: 'tongyi', apiKeys: ['sk-yyy1'], weight: 100, priority: 2, status: 'enabled', baseUrl: 'https://dashscope.aliyuncs.com', createdAt: '2024-01-02', updatedAt: '2024-01-02' },
+  { id: 'c3', name: '智谱AI', provider: 'zhipu', apiKeys: ['sk-zzz1', 'sk-zzz2', 'sk-zzz3'], weight: 100, priority: 3, status: 'enabled', baseUrl: 'https://open.bigmodel.cn', createdAt: '2024-01-03', updatedAt: '2024-01-03' },
+  { id: 'c4', name: '文心一言', provider: 'wenxin', apiKeys: ['sk-aaa1'], weight: 50, priority: 4, status: 'error', baseUrl: 'https://aip.baidubce.com', createdAt: '2024-01-04', updatedAt: '2024-01-04' },
+];
+
+const models: Model[] = [
+  { id: 'm1', name: 'deepseek-chat', provider: 'deepseek', type: 'chat', version: 'v1', contextWindow: 128000, inputLimit: 64000, outputLimit: 64000, inputPrice: 0.0015, outputPrice: 0.0015, status: 'enabled', channelId: 'c1', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'm2', name: 'deepseek-r1', provider: 'deepseek', type: 'chat', version: 'v1', contextWindow: 128000, inputLimit: 64000, outputLimit: 64000, inputPrice: 0.002, outputPrice: 0.002, status: 'enabled', channelId: 'c1', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'm3', name: 'qwen-2', provider: 'tongyi', type: 'chat', version: 'v2', contextWindow: 128000, inputLimit: 64000, outputLimit: 64000, inputPrice: 0.0012, outputPrice: 0.0012, status: 'enabled', channelId: 'c2', createdAt: '2024-01-02', updatedAt: '2024-01-02' },
+  { id: 'm4', name: 'qwen-2-plus', provider: 'tongyi', type: 'chat', version: 'v2', contextWindow: 128000, inputLimit: 64000, outputLimit: 64000, inputPrice: 0.0018, outputPrice: 0.0018, status: 'enabled', channelId: 'c2', createdAt: '2024-01-02', updatedAt: '2024-01-02' },
+  { id: 'm5', name: 'glm-4', provider: 'zhipu', type: 'chat', version: 'v4', contextWindow: 128000, inputLimit: 64000, outputLimit: 64000, inputPrice: 0.0016, outputPrice: 0.0016, status: 'enabled', channelId: 'c3', createdAt: '2024-01-03', updatedAt: '2024-01-03' },
+  { id: 'm6', name: 'glm-4-flash', provider: 'zhipu', type: 'chat', version: 'v4', contextWindow: 128000, inputLimit: 64000, outputLimit: 64000, inputPrice: 0.0008, outputPrice: 0.0008, status: 'enabled', channelId: 'c3', createdAt: '2024-01-03', updatedAt: '2024-01-03' },
+  { id: 'm7', name: 'ernie-4', provider: 'wenxin', type: 'chat', version: 'v4', contextWindow: 128000, inputLimit: 64000, outputLimit: 64000, inputPrice: 0.0014, outputPrice: 0.0014, status: 'disabled', channelId: 'c4', createdAt: '2024-01-04', updatedAt: '2024-01-04' },
+];
+
+const enterprises: Enterprise[] = [
+  { id: 'e1', name: '哈工大计算学部', contact: '张教授', phone: '0451-864xxx', email: 'zhang@hit.edu.cn', status: 'active', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'e2', name: '灵慧云智科技', contact: '李经理', phone: '010-8xxx', email: 'li@linwit.com', status: 'active', createdAt: '2024-01-02', updatedAt: '2024-01-02' },
+  { id: 'e3', name: '湖北电力公司', contact: '王主任', phone: '027-8xxx', email: 'wang@hubei-power.com', status: 'frozen', createdAt: '2024-01-03', updatedAt: '2024-01-15' },
+];
+
+const departments: Department[] = [
+  { id: 'd1', name: '管理层', enterpriseId: 'e1', parentId: null, createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'd2', name: '研发部', enterpriseId: 'e1', parentId: null, createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'd3', name: 'AI实验室', enterpriseId: 'e1', parentId: 'd2', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'd4', name: '产品部', enterpriseId: 'e2', parentId: null, createdAt: '2024-01-02', updatedAt: '2024-01-02' },
+];
+
+const users: User[] = [
+  { id: 'u1', username: 'super_admin', email: 'super@token.com', phone: '13800000001', role: 'super_admin', status: 'enabled', enterpriseId: '', departmentId: null, quota: -1, usedQuota: 0, createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'u2', username: 'platform_operator', email: 'operator@token.com', phone: '13800000002', role: 'platform_operator', status: 'enabled', enterpriseId: '', departmentId: null, quota: -1, usedQuota: 0, createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'u3', username: 'enterprise_admin', email: 'admin@enterprise.com', phone: '13800000003', role: 'enterprise_admin', status: 'enabled', enterpriseId: 'e1', departmentId: 'd1', quota: 1000000, usedQuota: 250000, createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'u4', username: 'employee', email: 'employee@enterprise.com', phone: '13800000004', role: 'employee', status: 'enabled', enterpriseId: 'e1', departmentId: 'd2', quota: 100000, usedQuota: 35000, createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'u5', username: 'developer1', email: 'dev1@enterprise.com', phone: '13800000005', role: 'employee', status: 'enabled', enterpriseId: 'e1', departmentId: 'd3', quota: 200000, usedQuota: 150000, createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'u6', username: 'developer2', email: 'dev2@enterprise.com', phone: '13800000006', role: 'employee', status: 'disabled', enterpriseId: 'e1', departmentId: 'd3', quota: 100000, usedQuota: 80000, createdAt: '2024-01-01', updatedAt: '2024-01-10' },
+];
+
+const apiKeys: APIKey[] = [
+  { id: 'k1', key: 'sk-user4-xxxxxxxxxxxxxxxx', name: '默认密钥', userId: 'u4', quota: 50000, usedQuota: 35000, allowedModels: ['m1', 'm2', 'm3'], expiresAt: '2025-12-31', status: 'active', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'k2', key: 'sk-user5-xxxxxxxxxxxxxxxx', name: '测试密钥', userId: 'u5', quota: 100000, usedQuota: 80000, allowedModels: ['m1', 'm5'], expiresAt: '2024-12-31', status: 'active', createdAt: '2024-01-01', updatedAt: '2024-01-01' },
+  { id: 'k3', key: 'sk-user6-xxxxxxxxxxxxxxxx', name: '旧密钥', userId: 'u6', quota: 50000, usedQuota: 45000, allowedModels: ['m1'], expiresAt: '2024-06-30', status: 'expired', createdAt: '2024-01-01', updatedAt: '2024-06-30' },
+];
+
+const callLogs: CallLog[] = [
+  { id: 'l1', userId: 'u4', userName: 'employee', enterpriseId: 'e1', modelName: 'deepseek-chat', inputTokens: 150, outputTokens: 300, totalTokens: 450, status: 'success', errorMessage: '', responseTime: 850, apiKeyId: 'k1', createdAt: '2024-01-15 10:30:00' },
+  { id: 'l2', userId: 'u4', userName: 'employee', enterpriseId: 'e1', modelName: 'deepseek-chat', inputTokens: 200, outputTokens: 450, totalTokens: 650, status: 'success', errorMessage: '', responseTime: 920, apiKeyId: 'k1', createdAt: '2024-01-15 10:35:00' },
+  { id: 'l3', userId: 'u5', userName: 'developer1', enterpriseId: 'e1', modelName: 'glm-4', inputTokens: 500, outputTokens: 800, totalTokens: 1300, status: 'success', errorMessage: '', responseTime: 1200, apiKeyId: 'k2', createdAt: '2024-01-15 11:00:00' },
+  { id: 'l4', userId: 'u4', userName: 'employee', enterpriseId: 'e1', modelName: 'qwen-2', inputTokens: 100, outputTokens: 200, totalTokens: 300, status: 'failed', errorMessage: '模型调用超时', responseTime: 30000, apiKeyId: 'k1', createdAt: '2024-01-15 11:30:00' },
+  { id: 'l5', userId: 'u5', userName: 'developer1', enterpriseId: 'e1', modelName: 'deepseek-r1', inputTokens: 800, outputTokens: 1200, totalTokens: 2000, status: 'success', errorMessage: '', responseTime: 1500, apiKeyId: 'k2', createdAt: '2024-01-15 14:00:00' },
+];
+
+const tokenStats: TokenStats[] = [
+  { date: '2024-01-09', inputTokens: 120000, outputTokens: 180000, totalTokens: 300000, callCount: 520 },
+  { date: '2024-01-10', inputTokens: 135000, outputTokens: 195000, totalTokens: 330000, callCount: 580 },
+  { date: '2024-01-11', inputTokens: 110000, outputTokens: 160000, totalTokens: 270000, callCount: 480 },
+  { date: '2024-01-12', inputTokens: 145000, outputTokens: 210000, totalTokens: 355000, callCount: 620 },
+  { date: '2024-01-13', inputTokens: 160000, outputTokens: 230000, totalTokens: 390000, callCount: 680 },
+  { date: '2024-01-14', inputTokens: 175000, outputTokens: 255000, totalTokens: 430000, callCount: 750 },
+  { date: '2024-01-15', inputTokens: 190000, outputTokens: 280000, totalTokens: 470000, callCount: 820 },
+];
+
+const costStats: CostStats[] = [
+  { date: '2024-01-09', amount: 450 },
+  { date: '2024-01-10', amount: 495 },
+  { date: '2024-01-11', amount: 405 },
+  { date: '2024-01-12', amount: 532 },
+  { date: '2024-01-13', amount: 585 },
+  { date: '2024-01-14', amount: 645 },
+  { date: '2024-01-15', amount: 705 },
+];
+
+const dashboardData: DashboardData = {
+  totalCalls: 15680,
+  totalTokens: 12500000,
+  totalCost: 18750,
+  activeUsers: 128,
+  hotModels: [
+    { name: 'deepseek-chat', count: 4520 },
+    { name: 'glm-4', count: 3890 },
+    { name: 'qwen-2', count: 3210 },
+    { name: 'deepseek-r1', count: 2150 },
+    { name: 'glm-4-flash', count: 1910 },
+  ],
+  abnormalCalls: 32,
+  tokenTrend: tokenStats,
+  costTrend: costStats,
+};
+
+const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
+
+export const api = {
+  channels: {
+    list: async () => { await delay(300); return channels; },
+    get: async (id: string) => { await delay(200); return channels.find(c => c.id === id) || null; },
+    create: async (data: Omit<Channel, 'id' | 'createdAt' | 'updatedAt'>) => { 
+      await delay(200); 
+      const newChannel: Channel = { ...data, id: `c${Date.now()}`, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
+      channels.push(newChannel);
+      return newChannel;
+    },
+    update: async (id: string, data: Partial<Channel>) => { 
+      await delay(200); 
+      const index = channels.findIndex(c => c.id === id);
+      if (index !== -1) {
+        channels[index] = { ...channels[index], ...data, updatedAt: new Date().toISOString() };
+        return channels[index];
+      }
+      return null;
+    },
+    delete: async (id: string) => { 
+      await delay(200); 
+      const index = channels.findIndex(c => c.id === id);
+      if (index !== -1) {
+        channels.splice(index, 1);
+        return true;
+      }
+      return false;
+    },
+  },
+  
+  models: {
+    list: async () => { await delay(300); return models; },
+    get: async (id: string) => { await delay(200); return models.find(m => m.id === id) || null; },
+    create: async (data: Omit<Model, 'id' | 'createdAt' | 'updatedAt'>) => { 
+      await delay(200); 
+      const newModel: Model = { ...data, id: `m${Date.now()}`, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
+      models.push(newModel);
+      return newModel;
+    },
+    update: async (id: string, data: Partial<Model>) => { 
+      await delay(200); 
+      const index = models.findIndex(m => m.id === id);
+      if (index !== -1) {
+        models[index] = { ...models[index], ...data, updatedAt: new Date().toISOString() };
+        return models[index];
+      }
+      return null;
+    },
+    delete: async (id: string) => { 
+      await delay(200); 
+      const index = models.findIndex(m => m.id === id);
+      if (index !== -1) {
+        models.splice(index, 1);
+        return true;
+      }
+      return false;
+    },
+  },
+  
+  enterprises: {
+    list: async () => { await delay(300); return enterprises; },
+    get: async (id: string) => { await delay(200); return enterprises.find(e => e.id === id) || null; },
+    create: async (data: Omit<Enterprise, 'id' | 'createdAt' | 'updatedAt'>) => { 
+      await delay(200); 
+      const newEnterprise: Enterprise = { ...data, id: `e${Date.now()}`, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
+      enterprises.push(newEnterprise);
+      return newEnterprise;
+    },
+    update: async (id: string, data: Partial<Enterprise>) => { 
+      await delay(200); 
+      const index = enterprises.findIndex(e => e.id === id);
+      if (index !== -1) {
+        enterprises[index] = { ...enterprises[index], ...data, updatedAt: new Date().toISOString() };
+        return enterprises[index];
+      }
+      return null;
+    },
+    delete: async (id: string) => { 
+      await delay(200); 
+      const index = enterprises.findIndex(e => e.id === id);
+      if (index !== -1) {
+        enterprises.splice(index, 1);
+        return true;
+      }
+      return false;
+    },
+  },
+  
+  departments: {
+    list: async (enterpriseId?: string) => { 
+      await delay(300); 
+      return enterpriseId ? departments.filter(d => d.enterpriseId === enterpriseId) : departments;
+    },
+    get: async (id: string) => { await delay(200); return departments.find(d => d.id === id) || null; },
+    create: async (data: Omit<Department, 'id' | 'createdAt' | 'updatedAt'>) => { 
+      await delay(200); 
+      const newDepartment: Department = { ...data, id: `d${Date.now()}`, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
+      departments.push(newDepartment);
+      return newDepartment;
+    },
+    update: async (id: string, data: Partial<Department>) => { 
+      await delay(200); 
+      const index = departments.findIndex(d => d.id === id);
+      if (index !== -1) {
+        departments[index] = { ...departments[index], ...data, updatedAt: new Date().toISOString() };
+        return departments[index];
+      }
+      return null;
+    },
+    delete: async (id: string) => { 
+      await delay(200); 
+      const index = departments.findIndex(d => d.id === id);
+      if (index !== -1) {
+        departments.splice(index, 1);
+        return true;
+      }
+      return false;
+    },
+  },
+  
+  users: {
+    list: async (enterpriseId?: string) => { 
+      await delay(300); 
+      return enterpriseId ? users.filter(u => u.enterpriseId === enterpriseId) : users;
+    },
+    get: async (id: string) => { await delay(200); return users.find(u => u.id === id) || null; },
+    create: async (data: Omit<User, 'id' | 'createdAt' | 'updatedAt'>) => { 
+      await delay(200); 
+      const newUser: User = { ...data, id: `u${Date.now()}`, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
+      users.push(newUser);
+      return newUser;
+    },
+    update: async (id: string, data: Partial<User>) => { 
+      await delay(200); 
+      const index = users.findIndex(u => u.id === id);
+      if (index !== -1) {
+        users[index] = { ...users[index], ...data, updatedAt: new Date().toISOString() };
+        return users[index];
+      }
+      return null;
+    },
+    delete: async (id: string) => { 
+      await delay(200); 
+      const index = users.findIndex(u => u.id === id);
+      if (index !== -1) {
+        users.splice(index, 1);
+        return true;
+      }
+      return false;
+    },
+  },
+  
+  apiKeys: {
+    list: async (userId?: string) => { 
+      await delay(300); 
+      return userId ? apiKeys.filter(k => k.userId === userId) : apiKeys;
+    },
+    get: async (id: string) => { await delay(200); return apiKeys.find(k => k.id === id) || null; },
+    create: async (data: Omit<APIKey, 'id' | 'createdAt' | 'updatedAt'>) => { 
+      await delay(200); 
+      const newKey: APIKey = { ...data, id: `k${Date.now()}`, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() };
+      apiKeys.push(newKey);
+      return newKey;
+    },
+    update: async (id: string, data: Partial<APIKey>) => { 
+      await delay(200); 
+      const index = apiKeys.findIndex(k => k.id === id);
+      if (index !== -1) {
+        apiKeys[index] = { ...apiKeys[index], ...data, updatedAt: new Date().toISOString() };
+        return apiKeys[index];
+      }
+      return null;
+    },
+    delete: async (id: string) => { 
+      await delay(200); 
+      const index = apiKeys.findIndex(k => k.id === id);
+      if (index !== -1) {
+        apiKeys.splice(index, 1);
+        return true;
+      }
+      return false;
+    },
+  },
+  
+  callLogs: {
+    list: async (userId?: string, enterpriseId?: string) => { 
+      await delay(300); 
+      let result = [...callLogs];
+      if (userId) result = result.filter(l => l.userId === userId);
+      if (enterpriseId) result = result.filter(l => l.enterpriseId === enterpriseId);
+      return result;
+    },
+  },
+  
+  stats: {
+    tokens: async () => { await delay(300); return tokenStats; },
+    costs: async () => { await delay(300); return costStats; },
+    dashboard: async () => { await delay(300); return dashboardData; },
+  },
+};

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/assets/react.svg


+ 8 - 0
src/components/Empty.tsx

@@ -0,0 +1,8 @@
+import { cn } from '@/lib/utils'
+
+// Empty component
+export default function Empty() {
+  return (
+    <div className={cn('flex h-full items-center justify-center')}>Empty</div>
+  )
+}

+ 63 - 0
src/components/about/CompanyIntro.tsx

@@ -0,0 +1,63 @@
+import { Building2, GraduationCap, Sparkles } from 'lucide-react';
+
+export function CompanyIntro() {
+  return (
+    <section className="pt-32 pb-20 bg-gradient-to-br from-deep-space-900 via-deep-space-800 to-deep-space-900">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
+          <div>
+            <span className="inline-block text-glow-green text-sm font-medium mb-4 tracking-wider uppercase">
+              关于我们
+            </span>
+            <h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
+              灵慧云智科技有限公司
+            </h1>
+            <p className="text-xl text-deep-space-200 mb-8">
+              哈灵 - 企业级大模型应用开发平台
+            </p>
+            <p className="text-deep-space-300 text-lg leading-relaxed mb-8">
+              灵慧云智成立于2024年12月,是一家专注于企业级AI应用的科技创新企业。公司核心产品为自研企业级大模型应用开发平台,主打0基础可视化智能体编排、极速搭建、全行业通用能力。
+            </p>
+            <p className="text-deep-space-300 text-lg leading-relaxed mb-8">
+              依托自研MCP调度协议与可复用Skills技能组件库,解决企业AI落地难、门槛高、复用差、浮于表面的行业痛点。平台不局限单一行业,可赋能千行百业智能化升级。
+            </p>
+            <div className="flex flex-wrap gap-4">
+              <div className="flex items-center gap-3 px-4 py-2 bg-white/10 rounded-lg">
+                <Sparkles className="w-5 h-5 text-glow-green" />
+                <span className="text-white">0基础极速搭建</span>
+              </div>
+              <div className="flex items-center gap-3 px-4 py-2 bg-white/10 rounded-lg">
+                <Building2 className="w-5 h-5 text-tech-cyan-400" />
+                <span className="text-white">政企标杆验证</span>
+              </div>
+              <div className="flex items-center gap-3 px-4 py-2 bg-white/10 rounded-lg">
+                <GraduationCap className="w-5 h-5 text-purple-400" />
+                <span className="text-white">技术自主创新</span>
+              </div>
+            </div>
+          </div>
+          <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10">
+            <div className="grid grid-cols-2 gap-6">
+              <div className="text-center p-6 bg-white/5 rounded-xl">
+                <div className="text-4xl font-bold text-glow-green mb-2">2024</div>
+                <div className="text-deep-space-300">公司成立</div>
+              </div>
+              <div className="text-center p-6 bg-white/5 rounded-xl">
+                <div className="text-4xl font-bold text-tech-cyan-400 mb-2">20+</div>
+                <div className="text-deep-space-300">核心研发人员</div>
+              </div>
+              <div className="text-center p-6 bg-white/5 rounded-xl">
+                <div className="text-4xl font-bold text-purple-400 mb-2">10+</div>
+                <div className="text-deep-space-300">标杆落地项目</div>
+              </div>
+              <div className="text-center p-6 bg-white/5 rounded-xl">
+                <div className="text-4xl font-bold text-white mb-2">20+</div>
+                <div className="text-deep-space-300">覆盖行业</div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 22 - 0
src/components/about/CompanyMap.tsx

@@ -0,0 +1,22 @@
+import { MapPin } from 'lucide-react';
+
+export function CompanyMap() {
+  return (
+    <div className="relative bg-white rounded-2xl overflow-hidden shadow-lg h-80 md:h-96">
+      <img
+        src="https://api.map.baidu.com/staticimage?center=116.5888,39.9942&width=800&height=400&zoom=15&markers=116.5888,39.9942&markerStyles=l,BORDER|label:灵慧云智"
+        alt="公司位置地图"
+        className="w-full h-full object-cover"
+      />
+      <div className="absolute bottom-4 left-4 bg-white/95 backdrop-blur-sm rounded-lg px-4 py-3 shadow-lg">
+        <div className="flex items-center gap-2">
+          <MapPin className="w-5 h-5 text-glow-green" />
+          <div>
+            <p className="text-deep-space-900 font-semibold text-sm">灵慧云智科技有限公司</p>
+            <p className="text-deep-space-500 text-xs">北京市朝阳区</p>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 163 - 0
src/components/about/Vision.tsx

@@ -0,0 +1,163 @@
+import React from 'react';
+import { Target, Rocket, Lightbulb, Users } from 'lucide-react';
+import { SectionHeader } from '@/components/common/SectionHeader';
+
+const strategies = [
+  {
+    icon: Lightbulb,
+    title: '技术自主创新',
+    description: '持续深耕大模型核心技术,突破关键技术瓶颈,构建自主可控的技术体系',
+  },
+  {
+    icon: Rocket,
+    title: '成果产业落地',
+    description: '加速科技成果转化,推动人工智能技术从实验室走向产业应用,实现规模化落地',
+  },
+  {
+    icon: Users,
+    title: '人才持续培育',
+    description: '深化校企合作,培养复合型AI人才,为行业发展提供坚实的人才支撑',
+  },
+];
+
+const milestones = [
+  { 
+    year: '2016年', 
+    phase: '技术沉淀期',
+    events: ['自主研发SmartAI操作系统', '构建覆盖智慧教育、督查、工地等领域的100+算法库'],
+    color: 'from-blue-500 to-blue-600'
+  },
+  { 
+    year: '2024年', 
+    phase: '灵慧云智公司成立',
+    events: ['将大模型业务独立,成立灵慧云智科技有限公司', '打造灵慧云智大模型应用平台,实现从"通用AI"到"垂直深耕"的战略升级'],
+    color: 'from-blue-500 to-blue-600'
+  },
+  { 
+    year: '2025年初', 
+    phase: '战略聚焦期',
+    events: ['灵慧云智大模型应用平台接入DeepSeek', '落地上海电科院大模型平台、湖北电力信通小数平台等项目'],
+    color: 'from-blue-500 to-blue-600'
+  },
+  { 
+    year: '2025至今', 
+    phase: '场景突破期',
+    events: ['以电力行业为切口,依托"光明电力大模型"在四川配网运维场景实现规模化落地'],
+    color: 'from-blue-500 to-blue-600'
+  },
+];
+
+export const Vision: React.FC = () => {
+  return (
+    <section className="py-24 bg-gradient-to-b from-white to-gray-50">
+      <div className="container mx-auto px-6">
+        <SectionHeader
+          title="发展与愿景"
+          subtitle="核心战略"
+          description="公司以技术自主创新、成果产业落地、人才持续培育为核心战略,持续深化校企产学研合作"
+        />
+
+        <div className="grid md:grid-cols-3 gap-6 mb-20">
+          {strategies.map((strategy, index) => (
+            <div
+              key={index}
+              className="bg-white rounded-xl p-8 border border-gray-100 hover:border-glow-green/50 hover:shadow-xl hover:shadow-glow-green/5 hover:-translate-y-1 transition-all duration-300 group"
+            >
+              <div className="w-14 h-14 bg-gradient-to-br from-glow-green/10 to-tech-cyan-100 rounded-xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
+                <strategy.icon size={28} className="text-glow-green" />
+              </div>
+              <h4 className="text-xl font-semibold text-deep-space-900 mb-3">
+                {strategy.title}
+              </h4>
+              <p className="text-gray-500 leading-relaxed">
+                {strategy.description}
+              </p>
+            </div>
+          ))}
+        </div>
+
+        <div className="bg-gradient-to-r from-deep-space-900 to-deep-space-950 rounded-2xl p-10 lg:p-16 overflow-hidden">
+          <div className="absolute top-0 right-0 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl" />
+          
+          <div className="text-center mb-16 relative z-10">
+            <h3 className="text-3xl md:text-4xl font-bold text-white mb-4">
+              发展历程:从算法到智能的进化之路
+            </h3>
+            <p className="text-deep-space-300">
+              从科研创新到产业落地,我们稳步前行
+            </p>
+          </div>
+
+          <div className="relative">
+            <div className="absolute left-1/2 top-0 bottom-0 w-1 bg-gradient-to-b from-blue-400 via-blue-500 to-blue-600 hidden md:block rounded-full" />
+            
+            <div className="absolute left-1/2 top-0 bottom-0 w-1 md:hidden bg-gradient-to-b from-blue-400 via-blue-500 to-blue-600 rounded-full" />
+
+            <div className="space-y-8 md:space-y-0">
+              {milestones.map((milestone, index) => (
+                <div
+                  key={index}
+                  className={`relative md:flex items-center gap-8 ${
+                    index % 2 === 0 ? 'md:flex-row' : 'md:flex-row-reverse'
+                  }`}
+                >
+                  <div className={`md:w-1/2 ${index % 2 === 0 ? 'md:text-right' : 'md:text-left'}`}>
+                    <div className={`inline-block bg-white/10 backdrop-blur-sm rounded-2xl p-6 ${index % 2 === 0 ? 'md:mr-8' : 'md:ml-8'} border border-white/10`}>
+                      <div className="flex items-center gap-3 mb-4" style={{ justifyContent: index % 2 === 0 ? 'flex-end' : 'flex-start' }}>
+                        <span className={`text-2xl font-bold bg-gradient-to-r ${milestone.color} bg-clip-text text-transparent`}>
+                          {milestone.year}
+                        </span>
+                      </div>
+                      <div className={`inline-block px-3 py-1 bg-blue-500/20 rounded-full text-blue-400 text-sm font-medium mb-4 ${index % 2 === 0 ? 'float-right' : ''}`}>
+                        {milestone.phase}
+                      </div>
+                      <ul className={`space-y-2 ${index % 2 === 0 ? 'text-right' : 'text-left'}`}>
+                        {milestone.events.map((event, eventIndex) => (
+                          <li key={eventIndex} className="text-deep-space-200 text-sm flex items-start gap-2" style={{ justifyContent: index % 2 === 0 ? 'flex-end' : 'flex-start' }}>
+                            <span className="text-blue-400 mt-1">→</span>
+                            <span>{event}</span>
+                          </li>
+                        ))}
+                      </ul>
+                    </div>
+                  </div>
+
+                  <div className="hidden md:flex items-center justify-center w-16 relative z-20">
+                    <div className={`w-12 h-12 bg-gradient-to-br ${milestone.color} rounded-full flex items-center justify-center shadow-lg shadow-blue-500/30`}>
+                      <Target size={24} className="text-white" />
+                    </div>
+                  </div>
+
+                  <div className="md:w-1/2 hidden md:block" />
+                </div>
+              ))}
+            </div>
+          </div>
+        </div>
+
+        <div className="mt-20 text-center">
+          <h3 className="text-3xl font-bold text-deep-space-900 mb-6">
+            未来规划
+          </h3>
+          <p className="text-gray-500 max-w-3xl mx-auto mb-8">
+            未来将持续拓展各行业智能化解决方案,推动人工智能技术从科研创新走向规模化产业落地,助力更多企业实现降本增效与智能升级。
+          </p>
+          <div className="flex flex-wrap justify-center gap-4">
+            <span className="px-6 py-3 bg-deep-space-900 text-white rounded-lg">
+              金融行业解决方案
+            </span>
+            <span className="px-6 py-3 bg-deep-space-900 text-white rounded-lg">
+              制造业智能化
+            </span>
+            <span className="px-6 py-3 bg-deep-space-900 text-white rounded-lg">
+              医疗健康AI
+            </span>
+            <span className="px-6 py-3 bg-deep-space-900 text-white rounded-lg">
+              政务智能化
+            </span>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+};

+ 86 - 0
src/components/cases/CaseDetail.tsx

@@ -0,0 +1,86 @@
+import { Zap, Database, CheckCircle, Calendar, MapPin, Building } from 'lucide-react';
+
+interface CaseDetailProps {
+  title: string;
+  year: string;
+  client: string;
+  industry: string;
+  background: string;
+  solution: string;
+  results: string[];
+  highlight: string;
+  icon: 'Zap' | 'Database';
+}
+
+const iconMap: Record<string, React.ReactNode> = {
+  Zap: <Zap className="w-12 h-12" />,
+  Database: <Database className="w-12 h-12" />,
+};
+
+export function CaseDetail({ title, year, client, industry, background, solution, results, highlight, icon }: CaseDetailProps) {
+  return (
+    <section className="py-20 bg-white">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="max-w-5xl mx-auto">
+          <div className="text-center mb-12">
+            <div className="w-20 h-20 bg-gradient-to-br from-glow-green/10 to-tech-cyan-500/10 rounded-2xl flex items-center justify-center text-glow-green mx-auto mb-6">
+              {iconMap[icon]}
+            </div>
+            <h1 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+              {title}
+            </h1>
+            <div className="flex flex-wrap justify-center gap-4 text-deep-space-500">
+              <div className="flex items-center gap-2">
+                <Calendar className="w-4 h-4" />
+                <span>{year}</span>
+              </div>
+              <div className="flex items-center gap-2">
+                <Building className="w-4 h-4" />
+                <span>{client}</span>
+              </div>
+              <div className="flex items-center gap-2">
+                <MapPin className="w-4 h-4" />
+                <span>{industry}</span>
+              </div>
+            </div>
+          </div>
+
+          <div className="space-y-12">
+            <div className="bg-deep-space-50 rounded-2xl p-8">
+              <h2 className="text-xl font-bold text-deep-space-900 mb-4">项目背景</h2>
+              <p className="text-deep-space-600 text-lg leading-relaxed">
+                {background}
+              </p>
+            </div>
+
+            <div className="bg-deep-space-50 rounded-2xl p-8">
+              <h2 className="text-xl font-bold text-deep-space-900 mb-4">实施方案</h2>
+              <p className="text-deep-space-600 text-lg leading-relaxed">
+                {solution}
+              </p>
+            </div>
+
+            <div className="bg-deep-space-50 rounded-2xl p-8">
+              <h2 className="text-xl font-bold text-deep-space-900 mb-6">落地功能</h2>
+              <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
+                {results.map((result) => (
+                  <div key={result} className="flex items-center gap-3 bg-white rounded-xl p-4 shadow-sm">
+                    <CheckCircle className="w-5 h-5 text-glow-green flex-shrink-0" />
+                    <span className="text-deep-space-700 font-medium">{result}</span>
+                  </div>
+                ))}
+              </div>
+            </div>
+
+            <div className="bg-gradient-to-br from-glow-green/10 to-tech-cyan-500/10 rounded-2xl p-8 border border-glow-green/20">
+              <h2 className="text-xl font-bold text-deep-space-900 mb-4">项目价值与示范意义</h2>
+              <p className="text-deep-space-700 text-lg leading-relaxed">
+                {highlight}
+              </p>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 53 - 0
src/components/common/Button.tsx

@@ -0,0 +1,53 @@
+import { type ReactNode } from 'react';
+import { clsx } from 'clsx';
+
+interface ButtonProps {
+  children: ReactNode;
+  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
+  size?: 'sm' | 'md' | 'lg';
+  className?: string;
+  href?: string;
+  onClick?: () => void;
+  disabled?: boolean;
+  icon?: ReactNode;
+}
+
+export function Button({
+  children,
+  variant = 'primary',
+  size = 'md',
+  className = '',
+  href,
+  onClick,
+  disabled = false,
+  icon,
+}: ButtonProps) {
+  const baseStyles = 'inline-flex items-center justify-center gap-2 font-semibold rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-glow-green/50 disabled:opacity-50 disabled:cursor-not-allowed';
+
+  const variantStyles = {
+    primary: 'bg-gradient-to-r from-glow-green to-tech-cyan-500 text-white hover:shadow-lg hover:shadow-glow-green/30 hover:-translate-y-0.5',
+    secondary: 'bg-deep-space-800 text-white hover:bg-deep-space-700 hover:-translate-y-0.5',
+    outline: 'border-2 border-glow-green text-glow-green hover:bg-glow-green hover:text-white',
+    ghost: 'text-deep-space-600 hover:text-glow-green hover:bg-glow-green/5',
+  };
+
+  const sizeStyles = {
+    sm: 'px-4 py-2 text-sm',
+    md: 'px-6 py-3 text-base',
+    lg: 'px-8 py-4 text-lg',
+  };
+
+  const Component = href ? 'a' : 'button';
+
+  return (
+    <Component
+      href={href}
+      onClick={onClick}
+      disabled={disabled}
+      className={clsx(baseStyles, variantStyles[variant], sizeStyles[size], className)}
+    >
+      {icon && <span>{icon}</span>}
+      {children}
+    </Component>
+  );
+}

+ 31 - 0
src/components/common/Card.tsx

@@ -0,0 +1,31 @@
+import { type ReactNode } from 'react';
+import { clsx } from 'clsx';
+
+interface CardProps {
+  children: ReactNode;
+  className?: string;
+  hover?: boolean;
+  gradientBorder?: boolean;
+  onClick?: () => void;
+}
+
+export function Card({ children, className = '', hover = false, gradientBorder = false, onClick }: CardProps) {
+  const baseStyles = 'bg-white rounded-xl p-6 shadow-sm transition-all duration-300';
+  
+  const hoverStyles = hover 
+    ? 'hover:shadow-xl hover:-translate-y-1 hover:shadow-deep-space-100/50 cursor-pointer' 
+    : '';
+  
+  const gradientStyles = gradientBorder 
+    ? 'border border-transparent bg-clip-padding bg-white' 
+    : 'border border-deep-space-100';
+
+  return (
+    <div 
+      className={clsx(baseStyles, hoverStyles, gradientStyles, className)}
+      onClick={onClick}
+    >
+      {children}
+    </div>
+  );
+}

+ 33 - 0
src/components/common/SectionHeader.tsx

@@ -0,0 +1,33 @@
+interface SectionHeaderProps {
+  title: string;
+  subtitle?: string;
+  description?: string;
+  align?: 'left' | 'center' | 'right';
+  dark?: boolean;
+}
+
+export function SectionHeader({ title, subtitle, description, align = 'center', dark = false }: SectionHeaderProps) {
+  const alignStyles = {
+    left: 'text-left',
+    center: 'text-center',
+    right: 'text-right',
+  };
+
+  return (
+    <div className={alignStyles[align]}>
+      {subtitle && (
+        <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+          {subtitle}
+        </span>
+      )}
+      <h2 className={`text-3xl md:text-4xl font-bold mb-4 ${dark ? 'text-white' : 'text-deep-space-900'}`}>
+        {title}
+      </h2>
+      {description && (
+        <p className={`text-lg max-w-3xl mx-auto ${dark ? 'text-white/70' : 'text-deep-space-500'}`}>
+          {description}
+        </p>
+      )}
+    </div>
+  );
+}

+ 87 - 0
src/components/home/ApplicationScenarios.tsx

@@ -0,0 +1,87 @@
+import { BookOpen, Headphones, FileText, BarChart3, ClipboardCheck, FileSearch, Sparkles } from 'lucide-react';
+import { applicationScenarios } from '@/data/mockData';
+
+const iconMap: Record<string, React.ReactNode> = {
+  BookOpen: <BookOpen className="w-6 h-6" />,
+  Headphones: <Headphones className="w-6 h-6" />,
+  FileText: <FileText className="w-6 h-6" />,
+  BarChart3: <BarChart3 className="w-6 h-6" />,
+  ClipboardCheck: <ClipboardCheck className="w-6 h-6" />,
+  FileSearch: <FileSearch className="w-6 h-6" />,
+};
+
+export function ApplicationScenarios() {
+  return (
+    <section className="py-20 bg-deep-space-50 relative overflow-hidden">
+      <div className="absolute top-0 left-1/4 w-96 h-96 bg-glow-green/5 rounded-full blur-3xl" />
+      <div className="absolute bottom-0 right-1/4 w-96 h-96 bg-tech-cyan-500/5 rounded-full blur-3xl" />
+      
+      <div className="container mx-auto px-4 md:px-6 relative z-10">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+            全行业场景落地
+          </span>
+          <h2 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+            赋能千行百业数字化转型
+          </h2>
+          <p className="text-deep-space-500 text-lg max-w-3xl mx-auto">
+            平台通用性强、适配场景广,可快速搭建各类企业AI应用,助力企业实现智能化升级。
+          </p>
+        </div>
+
+        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
+          {applicationScenarios.map((scenario) => (
+            <div
+              key={scenario.id}
+              className="group bg-white rounded-xl p-6 hover:shadow-xl hover:-translate-y-1 transition-all duration-300 border border-deep-space-100 relative overflow-hidden"
+            >
+              <div className="absolute top-0 right-0 w-24 h-24 bg-gradient-to-br from-glow-green/5 to-transparent rounded-bl-full" />
+              <div className="flex items-start gap-4">
+                <div className="w-12 h-12 bg-gradient-to-br from-glow-green/10 to-tech-cyan-500/10 rounded-lg flex items-center justify-center text-glow-green flex-shrink-0 group-hover:scale-110 transition-transform shadow-sm">
+                  {iconMap[scenario.icon]}
+                </div>
+                <div className="flex-1">
+                  <div className="flex items-center gap-2 mb-2">
+                    <h3 className="text-lg font-semibold text-deep-space-900">
+                      {scenario.name}
+                    </h3>
+                    <Sparkles className="w-4 h-4 text-glow-green" />
+                  </div>
+                  <p className="text-deep-space-500 text-sm mb-4 leading-relaxed">
+                    {scenario.description}
+                  </p>
+                  <div className="flex flex-wrap gap-2">
+                    {scenario.industries.map((industry) => (
+                      <span
+                        key={industry}
+                        className="px-3 py-1 text-xs font-medium text-glow-green bg-glow-green/10 rounded-full border border-glow-green/20"
+                      >
+                        {industry}
+                      </span>
+                    ))}
+                  </div>
+                </div>
+              </div>
+            </div>
+          ))}
+        </div>
+
+        <div className="mt-16 text-center">
+          <div className="inline-flex items-center gap-4 bg-white rounded-full px-8 py-4 shadow-lg">
+            <div className="flex -space-x-3">
+              {['电', '政', '制', '金', '办'].map((char, i) => (
+                <div
+                  key={i}
+                  className="w-10 h-10 rounded-full bg-gradient-to-br from-glow-green to-tech-cyan-500 flex items-center justify-center text-white text-sm font-bold border-2 border-white"
+                >
+                  {char}
+                </div>
+              ))}
+            </div>
+            <span className="text-deep-space-600">覆盖电力、政务、制造、金融、办公等全行业</span>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 74 - 0
src/components/home/BrandEndorsement.tsx

@@ -0,0 +1,74 @@
+import { Award, Building2, Users, CheckCircle, Shield } from 'lucide-react';
+import { brandStats } from '@/data/mockData';
+
+export function BrandEndorsement() {
+  return (
+    <section className="py-20 bg-deep-space-50 relative overflow-hidden">
+      <div className="absolute top-0 right-0 w-96 h-96 bg-glow-green/5 rounded-full blur-3xl" />
+      <div className="absolute bottom-0 left-0 w-96 h-96 bg-tech-cyan-500/5 rounded-full blur-3xl" />
+      
+      <div className="container mx-auto px-4 md:px-6 relative z-10">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+            企业资质
+          </span>
+          <h2 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+            硬核技术支撑
+          </h2>
+        </div>
+
+        <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
+          <div className="flex flex-col items-center">
+            <div className="w-full max-w-sm bg-white rounded-2xl p-8 shadow-lg mb-6 relative overflow-hidden">
+              <div className="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-glow-green/10 to-transparent rounded-bl-full" />
+              <div className="flex items-center justify-center gap-4 mb-6">
+                <div className="w-16 h-16 bg-gradient-to-br from-glow-green to-tech-cyan-500 rounded-lg flex items-center justify-center shadow-lg">
+                  <Shield className="w-8 h-8 text-white" />
+                </div>
+                <div className="text-left">
+                  <h3 className="text-xl font-bold text-deep-space-900">灵慧云智</h3>
+                  <p className="text-sm text-deep-space-500">科技有限公司</p>
+                </div>
+              </div>
+              <div className="bg-deep-space-50 rounded-xl p-4 text-center">
+                <div className="flex items-center justify-center gap-2 text-glow-green font-semibold mb-2">
+                  <Award className="w-5 h-5" />
+                  <span>企业级资质认证</span>
+                </div>
+                <p className="text-sm text-deep-space-600">企业级大模型智能体平台</p>
+              </div>
+            </div>
+            <div className="flex items-center gap-2 text-deep-space-500 text-sm">
+              <CheckCircle className="w-4 h-4 text-glow-green" />
+              <span>2024年12月正式成立</span>
+            </div>
+          </div>
+
+          <div>
+            <h3 className="text-2xl font-bold text-deep-space-900 mb-4">
+              企业资质
+            </h3>
+            <p className="text-deep-space-600 leading-relaxed mb-8">
+              灵慧云智成立于2024年12月,专注企业级大模型智能体平台研发与产业落地。公司主打0基础、低成本、可复用、全行业适配的AI解决方案,致力于帮助企业快速实现智能化转型升级。
+            </p>
+            <div className="grid grid-cols-2 md:grid-cols-4 gap-6">
+              {brandStats.map((stat, index) => (
+                <div
+                  key={index}
+                  className="bg-white rounded-xl p-6 text-center shadow-sm hover:shadow-md hover:-translate-y-1 transition-all"
+                >
+                  <div className="text-3xl md:text-4xl font-bold text-glow-green mb-2">
+                    {stat.value}
+                  </div>
+                  <div className="text-sm text-deep-space-500">
+                    {stat.label}
+                  </div>
+                </div>
+              ))}
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 166 - 0
src/components/home/BusinessConversion.tsx

@@ -0,0 +1,166 @@
+import { useState } from 'react';
+import { ArrowRight, CheckCircle } from 'lucide-react';
+import { cooperationTypes } from '@/data/mockData';
+
+export function BusinessConversion() {
+  const [formData, setFormData] = useState({
+    name: '',
+    phone: '',
+    company: '',
+    cooperation: '',
+    message: '',
+  });
+  const [isSubmitted, setIsSubmitted] = useState(false);
+
+  const handleSubmit = (e: React.FormEvent) => {
+    e.preventDefault();
+    setIsSubmitted(true);
+    setTimeout(() => {
+      setIsSubmitted(false);
+      setFormData({ name: '', phone: '', company: '', cooperation: '', message: '' });
+    }, 3000);
+  };
+
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
+    setFormData({ ...formData, [e.target.name]: e.target.value });
+  };
+
+  return (
+    <section className="py-20 bg-gradient-to-br from-deep-space-900 via-deep-space-800 to-deep-space-900">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
+          <div>
+            <h2 className="text-3xl md:text-4xl font-bold text-white mb-6">
+              定制专属AI智能体解决方案
+            </h2>
+            <p className="text-xl text-white/80 mb-8">
+              开启企业轻量化智能升级
+            </p>
+            <p className="text-white/60 mb-8">
+              支持全行业定制、私有化部署、渠道合作、技术共建,助力企业快速实现AI深度落地。
+            </p>
+            <div className="flex flex-col sm:flex-row gap-4">
+              <a
+                href="/about"
+                className="inline-flex items-center justify-center gap-2 px-8 py-4 text-lg font-semibold text-white bg-gradient-to-r from-glow-green to-tech-cyan-500 rounded-lg hover:shadow-xl hover:shadow-glow-green/30 hover:-translate-y-1 transition-all"
+              >
+                商务咨询
+                <ArrowRight className="w-5 h-5" />
+              </a>
+              <a
+                href="/product"
+                className="inline-flex items-center justify-center gap-2 px-8 py-4 text-lg font-semibold text-glow-green border-2 border-glow-green rounded-lg hover:bg-glow-green hover:text-white transition-all"
+              >
+                免费试用
+              </a>
+            </div>
+
+            <div className="flex flex-wrap gap-3 mt-10">
+              {cooperationTypes.map((type) => (
+                <span
+                  key={type}
+                  className="px-4 py-2 text-sm font-medium text-white/80 bg-white/10 rounded-full"
+                >
+                  {type}
+                </span>
+              ))}
+            </div>
+          </div>
+
+          <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10">
+            {isSubmitted ? (
+              <div className="text-center py-12">
+                <div className="w-16 h-16 bg-glow-green/20 rounded-full flex items-center justify-center mx-auto mb-6">
+                  <CheckCircle className="w-8 h-8 text-glow-green" />
+                </div>
+                <h3 className="text-xl font-semibold text-white mb-2">提交成功</h3>
+                <p className="text-white/60">我们会尽快与您联系</p>
+              </div>
+            ) : (
+              <form onSubmit={handleSubmit} className="space-y-6">
+                <div>
+                  <label className="block text-sm font-medium text-white/80 mb-2">
+                    姓名 <span className="text-glow-green">*</span>
+                  </label>
+                  <input
+                    type="text"
+                    name="name"
+                    value={formData.name}
+                    onChange={handleChange}
+                    required
+                    className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-glow-green transition-colors"
+                    placeholder="请输入您的姓名"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-white/80 mb-2">
+                    手机号 <span className="text-glow-green">*</span>
+                  </label>
+                  <input
+                    type="tel"
+                    name="phone"
+                    value={formData.phone}
+                    onChange={handleChange}
+                    required
+                    className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-glow-green transition-colors"
+                    placeholder="请输入您的手机号"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-white/80 mb-2">
+                    公司名称
+                  </label>
+                  <input
+                    type="text"
+                    name="company"
+                    value={formData.company}
+                    onChange={handleChange}
+                    className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-glow-green transition-colors"
+                    placeholder="请输入您的公司名称"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-white/80 mb-2">
+                    合作类型
+                  </label>
+                  <select
+                    name="cooperation"
+                    value={formData.cooperation}
+                    onChange={handleChange}
+                    className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white focus:outline-none focus:border-glow-green transition-colors"
+                  >
+                    <option value="" className="bg-deep-space-800">请选择合作类型</option>
+                    {cooperationTypes.map((type) => (
+                      <option key={type} value={type} className="bg-deep-space-800">
+                        {type}
+                      </option>
+                    ))}
+                  </select>
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-white/80 mb-2">
+                    需求描述
+                  </label>
+                  <textarea
+                    name="message"
+                    value={formData.message}
+                    onChange={handleChange}
+                    rows={4}
+                    className="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/40 focus:outline-none focus:border-glow-green transition-colors resize-none"
+                    placeholder="请简要描述您的需求..."
+                  />
+                </div>
+                <button
+                  type="submit"
+                  className="w-full py-4 text-lg font-semibold text-white bg-gradient-to-r from-glow-green to-tech-cyan-500 rounded-lg hover:shadow-xl hover:shadow-glow-green/30 hover:-translate-y-0.5 transition-all"
+                >
+                  提交咨询
+                </button>
+              </form>
+            )}
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 101 - 0
src/components/home/CaseStudies.tsx

@@ -0,0 +1,101 @@
+import { Zap, Database, CheckCircle } from 'lucide-react';
+import { caseStudies } from '@/data/mockData';
+
+const iconMap: Record<string, React.ReactNode> = {
+  Zap: <Zap className="w-8 h-8" />,
+  Database: <Database className="w-8 h-8" />,
+};
+
+export function CaseStudies() {
+  return (
+    <section className="py-20 bg-deep-space-50">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+            标杆落地案例
+          </span>
+          <h2 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+            实战验证平台硬核能力
+          </h2>
+          <p className="text-deep-space-500 text-lg max-w-3xl mx-auto">
+            首页展示两大真实可溯源政企落地案例,兼顾电力标杆深度与全行业通用能力。
+          </p>
+        </div>
+
+        <div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
+          {caseStudies.map((caseItem) => (
+            <div
+              key={caseItem.id}
+              className="bg-white rounded-2xl p-8 shadow-lg hover:shadow-xl transition-shadow"
+            >
+              <div className="flex items-center gap-4 mb-6">
+                <div className="w-12 h-12 bg-gradient-to-br from-glow-green/10 to-tech-cyan-500/10 rounded-xl flex items-center justify-center text-glow-green">
+                  {iconMap[caseItem.icon]}
+                </div>
+                <div>
+                  <h3 className="text-xl font-bold text-deep-space-900">
+                    {caseItem.title}
+                  </h3>
+                  <span className="text-sm text-deep-space-400">
+                    {caseItem.year}
+                  </span>
+                </div>
+              </div>
+
+              <div className="mb-6">
+                <h4 className="text-sm font-semibold text-deep-space-600 mb-2">
+                  项目背景
+                </h4>
+                <p className="text-deep-space-500 text-sm leading-relaxed">
+                  {caseItem.background}
+                </p>
+              </div>
+
+              <div className="mb-6">
+                <h4 className="text-sm font-semibold text-deep-space-600 mb-2">
+                  落地方案
+                </h4>
+                <p className="text-deep-space-500 text-sm leading-relaxed">
+                  {caseItem.solution}
+                </p>
+              </div>
+
+              <div className="mb-6">
+                <h4 className="text-sm font-semibold text-deep-space-600 mb-3">
+                  落地成果
+                </h4>
+                <div className="grid grid-cols-2 gap-2">
+                  {caseItem.results.map((result) => (
+                    <div
+                      key={result}
+                      className="flex items-center gap-2 text-sm text-deep-space-600"
+                    >
+                      <CheckCircle className="w-4 h-4 text-glow-green" />
+                      {result}
+                    </div>
+                  ))}
+                </div>
+              </div>
+
+              <div className="bg-glow-green/5 rounded-xl p-4">
+                <p className="text-sm text-deep-space-700 font-medium">
+                  <span className="text-glow-green">重点成果:</span>
+                  {caseItem.highlight}
+                </p>
+              </div>
+            </div>
+          ))}
+        </div>
+
+        <div className="text-center mt-12">
+          <a
+            href="/cases"
+            className="inline-flex items-center gap-2 text-glow-green font-semibold hover:underline"
+          >
+            查看更多案例
+          </a>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 104 - 0
src/components/home/CoreCapabilities.tsx

@@ -0,0 +1,104 @@
+import { Blocks, Network, Wrench, Shield, Zap, Layers } from 'lucide-react';
+import { coreCapabilities } from '@/data/mockData';
+
+const iconMap: Record<string, React.ReactNode> = {
+  Blocks: <Blocks className="w-8 h-8" />,
+  Network: <Network className="w-8 h-8" />,
+  Wrench: <Wrench className="w-8 h-8" />,
+  Shield: <Shield className="w-8 h-8" />,
+};
+
+const imageMap: Record<string, string> = {
+  '可视化智能体编排': 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=visual%20workflow%20orchestration%20drag%20drop%20AI%20agent%20editor%20modern%20interface%20technology&image_size=landscape_4_3',
+  'MCP核心协议': 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=MCP%20protocol%20network%20communication%20data%20flow%20system%20integration%20enterprise%20architecture&image_size=landscape_4_3',
+  'Skills可复用技能库': 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=reusable%20skill%20components%20library%20AI%20modules%20toolbox%20software%20architecture%20technology&image_size=landscape_4_3',
+  '企业级安全适配': 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=enterprise%20security%20protection%20data%20privacy%20encryption%20shield%20technology&image_size=landscape_4_3',
+};
+
+export function CoreCapabilities() {
+  return (
+    <section className="py-20 bg-white relative overflow-hidden">
+      <div className="absolute top-1/4 left-0 w-full h-px bg-gradient-to-r from-transparent via-deep-space-200 to-transparent" />
+      <div className="absolute bottom-1/4 left-0 w-full h-px bg-gradient-to-r from-transparent via-deep-space-200 to-transparent" />
+      
+      <div className="container mx-auto px-4 md:px-6 relative z-10">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+            核心技术能力
+          </span>
+          <h2 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+            自研企业级大模型开发平台
+          </h2>
+          <p className="text-deep-space-500 text-lg max-w-3xl mx-auto">
+            平台摒弃传统AI高门槛开发模式,通过可视化拖拽编排、自研MCP协议、标准化Skills组件,支持业务人员0基础、10分钟极速搭建企业智能体,适配全行业轻量化落地与深度业务迭代。
+          </p>
+        </div>
+
+        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
+          {coreCapabilities.map((capability) => (
+            <div
+              key={capability.id}
+              className="group relative bg-white rounded-2xl p-6 border border-deep-space-100 hover:border-glow-green/30 hover:shadow-xl hover:shadow-glow-green/5 transition-all duration-300"
+            >
+              <div className="absolute inset-0 bg-gradient-to-br from-glow-green/5 to-tech-cyan-500/5 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity" />
+              <div className="relative z-10">
+                <div className="w-12 h-12 bg-gradient-to-br from-glow-green/10 to-tech-cyan-500/10 rounded-xl flex items-center justify-center text-glow-green mb-4 group-hover:scale-110 transition-transform shadow-md">
+                  {iconMap[capability.icon]}
+                </div>
+                <h3 className="text-lg font-bold text-deep-space-900 mb-2">
+                  {capability.title}
+                </h3>
+                <p className="text-sm text-glow-green font-medium mb-3">
+                  {capability.subtitle}
+                </p>
+                <div className="mb-4 rounded-xl overflow-hidden">
+                  <img
+                    src={imageMap[capability.title]}
+                    alt={capability.title}
+                    className="w-full h-24 object-cover group-hover:scale-105 transition-transform duration-300"
+                  />
+                </div>
+                <p className="text-deep-space-500 text-sm leading-relaxed line-clamp-3">
+                  {capability.description}
+                </p>
+              </div>
+            </div>
+          ))}
+        </div>
+
+        <div className="mt-16 bg-gradient-to-r from-deep-space-900 to-deep-space-800 rounded-2xl p-8 md:p-12">
+          <div className="flex flex-col md:flex-row items-center justify-between gap-8">
+            <div className="text-center md:text-left">
+              <h3 className="text-2xl md:text-3xl font-bold text-white mb-4">
+                构建企业级AI基础设施
+              </h3>
+              <p className="text-white/70 max-w-xl">
+                从底层架构到应用层,全栈式AI解决方案,助力企业快速实现智能化转型
+              </p>
+            </div>
+            <div className="flex items-center gap-6">
+              <div className="flex flex-col items-center">
+                <div className="w-16 h-16 bg-glow-green/20 rounded-xl flex items-center justify-center mb-3">
+                  <Layers className="w-8 h-8 text-glow-green" />
+                </div>
+                <span className="text-white font-semibold">6层架构</span>
+              </div>
+              <div className="flex flex-col items-center">
+                <div className="w-16 h-16 bg-tech-cyan-500/20 rounded-xl flex items-center justify-center mb-3">
+                  <Zap className="w-8 h-8 text-tech-cyan-500" />
+                </div>
+                <span className="text-white font-semibold">极速部署</span>
+              </div>
+              <div className="flex flex-col items-center">
+                <div className="w-16 h-16 bg-purple-500/20 rounded-xl flex items-center justify-center mb-3">
+                  <Network className="w-8 h-8 text-purple-500" />
+                </div>
+                <span className="text-white font-semibold">全链路打通</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 83 - 0
src/components/home/PainPointSolutions.tsx

@@ -0,0 +1,83 @@
+import { useState } from 'react';
+import { Target, Database, Zap, TrendingDown } from 'lucide-react';
+import { painPointSolutions } from '@/data/mockData';
+
+const iconMap: Record<string, React.ReactNode> = {
+  Target: <Target className="w-10 h-10" />,
+  Database: <Database className="w-10 h-10" />,
+  Zap: <Zap className="w-10 h-10" />,
+  TrendingDown: <TrendingDown className="w-10 h-10" />,
+};
+
+const imageMap: Record<string, string> = {
+  '解决AI应用浮于表面': 'https://images.unsplash.com/photo-1518770660439-4636190af475?w=1200&h=675&fit=crop&auto=format&q=80',
+  '解决数据孤岛问题': 'https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=1200&h=675&fit=crop&auto=format&q=80',
+  '解决自动化程度低': 'https://images.unsplash.com/photo-1526379095098-d400fd0bf935?w=1200&h=675&fit=crop&auto=format&q=80',
+  '解决落地成本高、复用差': 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=1200&h=675&fit=crop&auto=format&q=80',
+};
+
+export function PainPointSolutions() {
+  const [activeTab, setActiveTab] = useState(0);
+
+  return (
+    <section className="py-20 bg-deep-space-50">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+            痛点解决方案
+          </span>
+          <h2 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+            直击企业AI落地痛点
+          </h2>
+          <p className="text-deep-space-500 text-lg max-w-3xl mx-auto">
+            当前企业AI普遍存在四大难题:AI应用浮于表面、数据孤岛严重、业务自动化程度低、落地成本高复用差。哈灵平台针对性提供全套解决方案,实现企业AI从「浅层试用」到「深度落地」。
+          </p>
+        </div>
+
+        <div className="flex flex-wrap justify-center gap-2 mb-12">
+          {painPointSolutions.map((solution, index) => (
+            <button
+              key={solution.id}
+              onClick={() => setActiveTab(index)}
+              className={`px-6 py-3 rounded-xl font-medium transition-all ${
+                activeTab === index
+                  ? 'bg-gradient-to-r from-glow-green to-tech-cyan-500 text-white shadow-lg shadow-glow-green/30'
+                  : 'bg-white text-deep-space-600 hover:bg-deep-space-100'
+              }`}
+            >
+              {solution.title}
+            </button>
+          ))}
+        </div>
+
+        <div className="bg-white rounded-2xl p-8 md:p-12 shadow-lg overflow-hidden">
+          <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
+            <div className="relative">
+              <div className="absolute inset-0 bg-gradient-to-br from-glow-green/5 to-tech-cyan-500/5 rounded-2xl" />
+              <img
+                src={imageMap[painPointSolutions[activeTab].title]}
+                alt={painPointSolutions[activeTab].title}
+                className="relative w-full h-64 md:h-80 lg:h-96 object-cover rounded-2xl"
+                loading="lazy"
+                onError={(e) => {
+                  (e.target as HTMLImageElement).src = 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=technology%20abstract%20background%20gradient%20blue%20cyan%20modern%20minimal&image_size=landscape_16_9';
+                }}
+              />
+              <div className="absolute -bottom-4 -right-4 w-24 h-24 bg-gradient-to-br from-glow-green/20 to-tech-cyan-500/20 rounded-2xl flex items-center justify-center text-glow-green">
+                {iconMap[painPointSolutions[activeTab].icon]}
+              </div>
+            </div>
+            <div>
+              <h3 className="text-2xl md:text-3xl font-bold text-deep-space-900 mb-6">
+                {painPointSolutions[activeTab].title}
+              </h3>
+              <p className="text-deep-space-600 text-lg leading-relaxed">
+                {painPointSolutions[activeTab].description}
+              </p>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 100 - 0
src/components/home/PlatformIntro.tsx

@@ -0,0 +1,100 @@
+import React from 'react';
+import { ChevronRight, Cpu, Code, Link, Database } from 'lucide-react';
+import { SectionHeader } from '@/components/common/SectionHeader';
+
+const coreTechs = [
+  {
+    icon: Cpu,
+    title: '智能体Agent',
+    description: '自主思考与决策',
+  },
+  {
+    icon: Code,
+    title: 'Skill技能体系',
+    description: '模块化业务能力',
+  },
+  {
+    icon: Link,
+    title: 'MCP系统互联',
+    description: '无缝对接现有系统',
+  },
+  {
+    icon: Database,
+    title: 'RAG知识库',
+    description: '精准知识支撑',
+  },
+];
+
+export const PlatformIntro: React.FC = () => {
+  return (
+    <section className="py-24 bg-gradient-to-b from-deep-space-950 to-white">
+      <div className="container mx-auto px-6">
+        <SectionHeader
+          title="大模型平台简介"
+          subtitle="核心技术体系"
+          description="平台聚焦解决企业AI应用浮于表面、数据孤岛、自动化程度低等行业痛点,依托四大核心技术,构建完整业务闭环"
+        />
+
+        <div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6 mb-12">
+          {coreTechs.map((tech, index) => (
+            <div
+              key={index}
+              className="bg-white rounded-xl p-6 border border-gray-100 hover:border-glow-green/50 hover:shadow-xl hover:shadow-glow-green/5 hover:-translate-y-1 transition-all duration-300 group"
+            >
+              <div className="w-14 h-14 bg-gradient-to-br from-glow-green/10 to-tech-cyan-100 rounded-xl flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
+                <tech.icon size={28} className="text-glow-green" />
+              </div>
+              <h4 className="text-lg font-semibold text-deep-space-900 mb-2">
+                {tech.title}
+              </h4>
+              <p className="text-gray-500">{tech.description}</p>
+            </div>
+          ))}
+        </div>
+
+        <div className="bg-gradient-to-r from-deep-space-900 to-deep-space-950 rounded-2xl p-10 lg:p-16">
+          <div className="grid lg:grid-cols-2 gap-12 items-center">
+            <div>
+              <h3 className="text-3xl font-bold text-white mb-6">
+                构建「思考-规划-调度-执行」完整业务闭环
+              </h3>
+              <p className="text-lg text-deep-space-300 mb-8 leading-relaxed">
+                哈灵企业级大模型应用平台打破传统AI应用天花板,为企业提供低成本、可落地、高安全的私有化AI智能化解决方案。支持私有化本地部署、全流程低代码开发、老旧系统零改造对接,适配全行业企业智能化转型需求。
+              </p>
+              <div className="flex flex-wrap gap-4">
+                <a
+                  href="/platform"
+                  className="inline-flex items-center gap-2 px-8 py-4 bg-gradient-to-r from-glow-green to-tech-cyan-500 text-white font-semibold rounded-lg hover:shadow-xl hover:shadow-glow-green/30 hover:-translate-y-1 transition-all"
+                >
+                  查看平台介绍
+                  <ChevronRight size={20} />
+                </a>
+                <a
+                  href="/about"
+                  className="inline-flex items-center gap-2 px-8 py-4 border border-white/30 text-white font-semibold rounded-lg hover:bg-white/10 hover:-translate-y-1 transition-all"
+                >
+                  了解更多
+                </a>
+              </div>
+            </div>
+            <div className="relative">
+              <div className="aspect-video rounded-xl bg-deep-space-800 flex items-center justify-center overflow-hidden">
+                <div className="absolute inset-0 flex items-center justify-center">
+                  <div className="text-center">
+                    <div className="w-20 h-20 bg-glow-green/20 rounded-full flex items-center justify-center mx-auto mb-4">
+                      <Cpu size={40} className="text-glow-green" />
+                    </div>
+                    <p className="text-white/60">平台架构示意图</p>
+                  </div>
+                </div>
+                <div className="absolute top-4 left-4 w-8 h-8 bg-glow-green rounded-full animate-ping" />
+                <div className="absolute top-1/2 right-4 w-6 h-6 bg-tech-cyan-500 rounded-full animate-ping" style={{ animationDelay: '1s' }} />
+                <div className="absolute bottom-4 left-1/3 w-4 h-4 bg-glow-purple rounded-full animate-ping" style={{ animationDelay: '2s' }} />
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+};

+ 90 - 0
src/components/home/RndAdvantage.tsx

@@ -0,0 +1,90 @@
+import { Cpu, Building2, ArrowRight, Sparkles } from 'lucide-react';
+
+export function RndAdvantage() {
+  return (
+    <section className="py-20 bg-white">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+            技术创新
+          </span>
+          <h2 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+            自主研发核心技术
+          </h2>
+        </div>
+
+        <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
+          <div className="relative">
+            <div className="bg-gradient-to-br from-deep-space-900 to-deep-space-800 rounded-3xl p-8 text-white">
+              <div className="grid grid-cols-2 gap-6">
+                <div className="bg-white/10 rounded-xl p-6 text-center">
+                  <Cpu className="w-10 h-10 text-glow-green mx-auto mb-4" />
+                  <h3 className="font-semibold mb-2">核心技术研发</h3>
+                  <p className="text-sm text-white/80">大模型算法、MCP协议迭代、前沿技术研究</p>
+                </div>
+                <div className="bg-white/10 rounded-xl p-6 text-center">
+                  <Building2 className="w-10 h-10 text-tech-cyan-400 mx-auto mb-4" />
+                  <h3 className="font-semibold mb-2">企业场景落地</h3>
+                  <p className="text-sm text-white/80">场景打磨、产品迭代、项目交付、客户需求优化</p>
+                </div>
+              </div>
+              <div className="flex items-center justify-center mt-8">
+                <ArrowRight className="w-6 h-6 text-glow-green mx-4" />
+              </div>
+              <div className="bg-gradient-to-r from-glow-green/20 to-tech-cyan-500/20 rounded-xl p-6 mt-6">
+                <div className="flex items-center gap-3 mb-3">
+                  <Sparkles className="w-5 h-5 text-glow-green" />
+                  <h3 className="font-semibold">技术优势</h3>
+                </div>
+                <ul className="space-y-2 text-sm text-white/80">
+                  <li>• 技术领先:自主研发核心技术,持续保持技术前沿</li>
+                  <li>• 落地性强:紧密结合企业真实场景,确保技术可落地</li>
+                  <li>• 可持续迭代:快速响应市场需求,保障平台持续进化</li>
+                </ul>
+              </div>
+            </div>
+          </div>
+
+          <div>
+            <h3 className="text-2xl font-bold text-deep-space-900 mb-6">
+              持续创新研发机制
+            </h3>
+            <p className="text-deep-space-600 leading-relaxed mb-8">
+              灵慧云智专注于企业级AI应用技术研发,建立完善的自主创新研发机制。公司核心团队深耕AI领域多年,具备深厚的技术积累与丰富的行业经验,致力于将前沿AI技术转化为可落地的企业级解决方案。
+            </p>
+            
+            <div className="space-y-6">
+              <div className="flex items-start gap-4">
+                <div className="w-10 h-10 bg-glow-green/10 rounded-lg flex items-center justify-center text-glow-green flex-shrink-0">
+                  <span className="font-bold">1</span>
+                </div>
+                <div>
+                  <h4 className="font-semibold text-deep-space-900 mb-1">技术研发</h4>
+                  <p className="text-sm text-deep-space-500">持续投入核心技术研发,保持产品技术领先优势</p>
+                </div>
+              </div>
+              <div className="flex items-start gap-4">
+                <div className="w-10 h-10 bg-glow-green/10 rounded-lg flex items-center justify-center text-glow-green flex-shrink-0">
+                  <span className="font-bold">2</span>
+                </div>
+                <div>
+                  <h4 className="font-semibold text-deep-space-900 mb-1">产品迭代</h4>
+                  <p className="text-sm text-deep-space-500">快速响应市场需求,持续优化产品功能与性能</p>
+                </div>
+              </div>
+              <div className="flex items-start gap-4">
+                <div className="w-10 h-10 bg-glow-green/10 rounded-lg flex items-center justify-center text-glow-green flex-shrink-0">
+                  <span className="font-bold">3</span>
+                </div>
+                <div>
+                  <h4 className="font-semibold text-deep-space-900 mb-1">场景创新</h4>
+                  <p className="text-sm text-deep-space-500">深入挖掘行业场景,创新AI应用落地模式</p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 86 - 0
src/components/home/WhatWeDo.tsx

@@ -0,0 +1,86 @@
+import React, { useState } from 'react';
+import { Network, Zap, Brain, Plug, Check } from 'lucide-react';
+import { whatWeDoData } from '@/data/mockData';
+import { SectionHeader } from '@/components/common/SectionHeader';
+
+const iconMap = {
+  Network,
+  Zap,
+  Brain,
+  Plug,
+};
+
+export const WhatWeDo: React.FC = () => {
+  const [activeTab, setActiveTab] = useState(0);
+
+  return (
+    <section className="py-24 bg-deep-space-950">
+      <div className="container mx-auto px-6">
+        <SectionHeader
+          title="我们能做什么"
+          subtitle="解决企业AI应用痛点"
+          description="聚焦解决企业AI应用浮于表面、数据孤岛、自动化程度低等行业痛点,为企业提供切实可行的智能化解决方案"
+          dark={true}
+        />
+
+        <div className="flex flex-wrap justify-center gap-4 mb-12">
+          {whatWeDoData.map((item, index) => (
+            <button
+              key={item.id}
+              onClick={() => setActiveTab(index)}
+              className={`px-6 py-3 rounded-lg font-medium transition-all ${
+                activeTab === index
+                  ? 'bg-gradient-to-r from-glow-green to-tech-cyan-500 text-white'
+                  : 'bg-deep-space-900 text-white/60 hover:bg-deep-space-800'
+              }`}
+            >
+              {item.title}
+            </button>
+          ))}
+        </div>
+
+        <div className="grid lg:grid-cols-2 gap-12 items-center">
+          <div className="relative">
+            <div className="aspect-square rounded-2xl bg-gradient-to-br from-glow-green/10 to-glow-purple/10 flex items-center justify-center">
+              <div className="w-3/4 h-3/4 rounded-xl bg-deep-space-900 flex items-center justify-center">
+                <div className="text-center">
+                  {(() => {
+                    const IconComponent = iconMap[whatWeDoData[activeTab].icon as keyof typeof iconMap];
+                    return <IconComponent size={80} className="text-glow-green mx-auto mb-4" />;
+                  })()}
+                  <h3 className="text-2xl font-bold text-white mb-2">
+                    {whatWeDoData[activeTab].title}
+                  </h3>
+                </div>
+              </div>
+            </div>
+            <div className="absolute -top-4 -right-4 w-24 h-24 bg-glow-green/20 rounded-full blur-2xl" />
+            <div className="absolute -bottom-4 -left-4 w-24 h-24 bg-glow-purple/20 rounded-full blur-2xl" />
+          </div>
+
+          <div>
+            <h3 className="text-3xl font-bold text-white mb-6">
+              {whatWeDoData[activeTab].title}
+            </h3>
+            <p className="text-lg text-white/70 mb-8 leading-relaxed">
+              {whatWeDoData[activeTab].description}
+            </p>
+            <div className="grid grid-cols-2 gap-4">
+              {whatWeDoData[activeTab].features.map((feature, index) => (
+                <div
+                  key={index}
+                  className="flex items-center gap-3 bg-deep-space-900 rounded-lg p-4"
+                >
+                  <div className="w-8 h-8 bg-glow-green/20 rounded-lg flex items-center justify-center flex-shrink-0">
+                    <Check size={16} className="text-glow-green" />
+                  </div>
+                  <span className="text-white font-medium">{feature}</span>
+                </div>
+              ))}
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+};

+ 61 - 0
src/components/layout/FloatingConsult.tsx

@@ -0,0 +1,61 @@
+import { useState } from 'react';
+import { MessageCircle, X, Phone } from 'lucide-react';
+import { contactInfo } from '@/data/mockData';
+
+export function FloatingConsult() {
+  const [isOpen, setIsOpen] = useState(false);
+
+  return (
+    <>
+      {/*<button*/}
+      {/*  onClick={() => setIsOpen(true)}*/}
+      {/*  className="fixed right-6 bottom-6 z-50 w-14 h-14 bg-gradient-to-r from-glow-green to-tech-cyan-500 rounded-full shadow-lg shadow-glow-green/30 flex items-center justify-center text-white hover:scale-110 transition-transform"*/}
+      {/*>*/}
+      {/*  <MessageCircle className="w-6 h-6" />*/}
+      {/*</button>*/}
+
+      {isOpen && (
+        <div className="fixed inset-0 z-40">
+          <div className="absolute inset-0 bg-black/50" onClick={() => setIsOpen(false)} />
+          <div className="absolute right-6 bottom-24 w-80 bg-white rounded-2xl shadow-2xl overflow-hidden">
+            <div className="bg-gradient-to-r from-glow-green to-tech-cyan-500 p-4 flex items-center justify-between">
+              <h3 className="text-white font-semibold">商务咨询</h3>
+              <button
+                onClick={() => setIsOpen(false)}
+                className="text-white/80 hover:text-white transition-colors"
+              >
+                <X className="w-5 h-5" />
+              </button>
+            </div>
+            <div className="p-4 space-y-4">
+              <div className="flex items-center gap-3">
+                <div className="w-10 h-10 bg-glow-green/10 rounded-full flex items-center justify-center">
+                  <Phone className="w-5 h-5 text-glow-green" />
+                </div>
+                <div>
+                  <p className="text-sm text-deep-space-500">商务联系人</p>
+                  <p className="font-semibold text-deep-space-900">{contactInfo.name}</p>
+                </div>
+              </div>
+              <div className="flex items-center gap-3">
+                <div className="w-10 h-10 bg-tech-cyan-500/10 rounded-full flex items-center justify-center">
+                  <Phone className="w-5 h-5 text-tech-cyan-500" />
+                </div>
+                <div>
+                  <p className="text-sm text-deep-space-500">联系电话</p>
+                  <p className="font-semibold text-deep-space-900">{contactInfo.phone}</p>
+                </div>
+              </div>
+              <a
+                href="/about"
+                className="block w-full py-3 text-center font-semibold text-white bg-gradient-to-r from-glow-green to-tech-cyan-500 rounded-lg hover:shadow-lg transition-shadow"
+              >
+                在线咨询
+              </a>
+            </div>
+          </div>
+        </div>
+      )}
+    </>
+  );
+}

+ 105 - 0
src/components/layout/Footer.tsx

@@ -0,0 +1,105 @@
+import { Link } from 'react-router-dom';
+import { navLinks, contactInfo } from '@/data/mockData';
+import { Phone, MapPin } from 'lucide-react';
+
+export function Footer() {
+  return (
+    <footer className="bg-deep-space-900 text-white">
+      <div className="container mx-auto px-4 md:px-6 py-16">
+        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12">
+          <div>
+            <div className="flex items-center gap-3 mb-6">
+              <img src="/logo.svg" alt="灵慧云智" className="h-8 w-auto object-contain" />
+            </div>
+            <p className="text-white/60 text-sm leading-relaxed">
+              哈灵企业级大模型应用开发平台,致力于为企业提供0基础、低成本、可复用的AI解决方案。
+            </p>
+          </div>
+
+          <div>
+            <h3 className="text-lg font-semibold mb-6">网站导航</h3>
+            <ul className="space-y-3">
+              {navLinks.map((link) => (
+                <li key={link.name}>
+                  <Link
+                    to={link.href}
+                    className="text-white/60 text-sm hover:text-glow-green transition-colors"
+                  >
+                    {link.name}
+                  </Link>
+                </li>
+              ))}
+            </ul>
+          </div>
+
+          <div>
+            <h3 className="text-lg font-semibold mb-6">关于我们</h3>
+            <ul className="space-y-3">
+              <li>
+                <Link
+                  to="/about"
+                  className="text-white/60 text-sm hover:text-glow-green transition-colors"
+                >
+                  企业简介
+                </Link>
+              </li>
+              <li>
+                <Link
+                  to="/about"
+                  className="text-white/60 text-sm hover:text-glow-green transition-colors"
+                >
+                  发展历程
+                </Link>
+              </li>
+              <li>
+                <Link
+                  to="/about"
+                  className="text-white/60 text-sm hover:text-glow-green transition-colors"
+                >
+                  联系方式
+                </Link>
+              </li>
+            </ul>
+          </div>
+
+          <div>
+            <h3 className="text-lg font-semibold mb-6">联系方式</h3>
+            <ul className="space-y-4">
+              <li className="flex items-center gap-3 text-white/60 text-sm">
+                <Phone className="w-4 h-4 text-glow-green" />
+                <span>{contactInfo.phone}</span>
+              </li>
+              <li className="flex items-start gap-3 text-white/60 text-sm">
+                <MapPin className="w-4 h-4 text-glow-green mt-1 flex-shrink-0" />
+                <span>{contactInfo.address}</span>
+              </li>
+            </ul>
+          </div>
+        </div>
+
+        <div className="border-t border-white/10 mt-12 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
+          <p className="text-white/40 text-sm">
+            ©2026 北京灵慧云智科技有限公司 版权所有 |
+            <a
+                href="https://beian.miit.gov.cn/"
+                target="_blank"
+                rel="noopener noreferrer"
+                className="hover:text-white/60 transition-colors"
+            >
+              京ICP备2026042465号-1
+            </a>
+            |
+            <a
+                href="https://www.beian.gov.cn/"
+                target="_blank"
+                rel="noopener noreferrer"
+                className="hover:text-white/60 transition-colors"
+            >
+              公安网备XXX号
+            </a>
+          </p>
+        </div>
+      </div>
+    </footer>
+  );
+}

+ 114 - 0
src/components/layout/Header.tsx

@@ -0,0 +1,114 @@
+import { useState, useEffect } from 'react';
+import { Link, useLocation } from 'react-router-dom';
+import { Menu, X, Search } from 'lucide-react';
+import { navLinks } from '@/data/mockData';
+
+export function Header() {
+  const [isScrolled, setIsScrolled] = useState(false);
+  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
+  const location = useLocation();
+
+  useEffect(() => {
+    const handleScroll = () => {
+      setIsScrolled(window.scrollY > 100);
+    };
+    window.addEventListener('scroll', handleScroll);
+    return () => window.removeEventListener('scroll', handleScroll);
+  }, []);
+
+  const isActive = (href: string) => location.pathname === href;
+
+  return (
+    <>
+      <header
+        className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
+          isScrolled
+            ? 'bg-white/90 backdrop-blur-lg shadow-md'
+            : 'bg-white'
+        }`}
+      >
+        <div className="container mx-auto px-4 md:px-6">
+          <div className="flex items-center justify-between h-16 md:h-20">
+            <Link to="/" className="flex items-center gap-3">
+              <div className="flex items-center">
+                <img src="/logo.svg" alt="灵慧云智" className="h-8 md:h-10 w-auto object-contain" />
+              </div>
+              <div className="hidden sm:block">
+                <p className="text-xs text-deep-space-400">企业级AI大模型应用开发平台</p>
+              </div>
+            </Link>
+
+            <nav className="hidden lg:flex items-center gap-8">
+              {navLinks.map((link) => (
+                <Link
+                  key={link.name}
+                  to={link.href}
+                  className={`text-sm font-medium transition-colors ${
+                    isActive(link.href)
+                      ? 'text-glow-green'
+                      : 'text-deep-space-600 hover:text-glow-green'
+                  }`}
+                >
+                  {link.name}
+                </Link>
+              ))}
+            </nav>
+
+            <div className="hidden lg:flex items-center gap-4">
+              <button className="p-2 text-deep-space-500 hover:text-deep-space-700 transition-colors">
+                <Search className="w-5 h-5" />
+              </button>
+              <Link
+                to="/about"
+                className="px-6 py-2.5 bg-gradient-to-r from-glow-green to-tech-cyan-500 text-white text-sm font-medium rounded-lg hover:shadow-lg hover:shadow-glow-green/30 transition-all"
+              >
+                联系我们
+              </Link>
+            </div>
+
+            <button
+              className="lg:hidden p-2 text-deep-space-600"
+              onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
+            >
+              {isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
+            </button>
+          </div>
+        </div>
+      </header>
+
+      {isMobileMenuOpen && (
+        <div className="fixed inset-0 z-40 lg:hidden">
+          <div className="absolute inset-0 bg-black/50" onClick={() => setIsMobileMenuOpen(false)} />
+          <div className="absolute right-0 top-0 bottom-0 w-80 bg-white shadow-2xl p-6">
+            <div className="flex items-center gap-3 mb-8">
+              <img src="/logo.svg" alt="灵慧云智" className="h-8 w-auto object-contain" />
+            </div>
+            <nav className="flex flex-col gap-4">
+              {navLinks.map((link) => (
+                <Link
+                  key={link.name}
+                  to={link.href}
+                  onClick={() => setIsMobileMenuOpen(false)}
+                  className={`text-lg font-medium transition-colors ${
+                    isActive(link.href)
+                      ? 'text-glow-green'
+                      : 'text-deep-space-600 hover:text-glow-green'
+                  }`}
+                >
+                  {link.name}
+                </Link>
+              ))}
+              <Link
+                to="/about"
+                onClick={() => setIsMobileMenuOpen(false)}
+                className="mt-4 px-6 py-3 bg-gradient-to-r from-glow-green to-tech-cyan-500 text-white text-center font-medium rounded-lg"
+              >
+                联系我们
+              </Link>
+            </nav>
+          </div>
+        </div>
+      )}
+    </>
+  );
+}

+ 107 - 0
src/components/layout/HeroCarousel.tsx

@@ -0,0 +1,107 @@
+import { useState, useEffect } from 'react';
+import { ChevronLeft, ChevronRight } from 'lucide-react';
+import { carouselData } from '@/data/mockData';
+
+export function HeroCarousel() {
+  const [currentIndex, setCurrentIndex] = useState(0);
+  const [isHovering, setIsHovering] = useState(false);
+
+  useEffect(() => {
+    if (isHovering) return;
+    const interval = setInterval(() => {
+      setCurrentIndex((prev) => (prev + 1) % carouselData.length);
+    }, 5000);
+    return () => clearInterval(interval);
+  }, [isHovering]);
+
+  const goToSlide = (index: number) => {
+    setCurrentIndex(index);
+  };
+
+  const prevSlide = () => {
+    setCurrentIndex((prev) => (prev - 1 + carouselData.length) % carouselData.length);
+  };
+
+  const nextSlide = () => {
+    setCurrentIndex((prev) => (prev + 1) % carouselData.length);
+  };
+
+  return (
+    <div
+      className="relative w-full overflow-hidden"
+      style={{ height: '85vh', minHeight: '600px' }}
+      onMouseEnter={() => setIsHovering(true)}
+      onMouseLeave={() => setIsHovering(false)}
+    >
+      <div className="absolute inset-0 overflow-hidden">
+        {carouselData.map((slide, index) => (
+          <div
+            key={slide.id}
+            className={`absolute inset-0 bg-gradient-to-br ${slide.bgGradient} transition-opacity duration-1000 ${
+              index === currentIndex ? 'opacity-100 z-10' : 'opacity-0 z-0'
+            }`}
+          >
+            <div className="absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg%20width%3D%2260%22%20height%3D%2260%22%20viewBox%3D%220%200%2060%2060%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Cg%20fill%3D%22%23ffffff%22%20fill-opacity%3D%220.03%22%3E%3Cpath%20d%3D%22M36%2034v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6%2034v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6%204V0H4v4H0v2h4v4h2V6h4V4H6z%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E')] opacity-50" />
+            
+            <div className="absolute top-1/4 left-1/4 w-96 h-96 bg-glow-green/10 rounded-full blur-3xl animate-pulse" />
+            <div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-tech-cyan-500/10 rounded-full blur-3xl animate-pulse" style={{ animationDelay: '1s' }} />
+            
+            <div className="absolute inset-0">
+              {[...Array(30)].map((_, i) => (
+                <div
+                  key={i}
+                  className="absolute w-1 h-1 bg-glow-green/30 rounded-full animate-float"
+                  style={{
+                    left: `${Math.random() * 100}%`,
+                    top: `${Math.random() * 100}%`,
+                    animationDelay: `${Math.random() * 5}s`,
+                    animationDuration: `${5 + Math.random() * 5}s`,
+                  }}
+                />
+              ))}
+            </div>
+          </div>
+        ))}
+      </div>
+
+      <div className="relative z-20 h-full flex flex-col items-center justify-center px-4">
+        <div className="text-center">
+          <h1 className="text-3xl md:text-4xl lg:text-5xl font-bold text-white mb-6 leading-tight transition-all duration-1000">
+            {carouselData[currentIndex].title}
+          </h1>
+          <p className="text-base md:text-lg lg:text-xl text-white/90 mb-6 max-w-4xl mx-auto leading-relaxed transition-all duration-1000">
+            {carouselData[currentIndex].subtitle}
+          </p>
+          <p className="text-sm md:text-base lg:text-lg text-white/70 max-w-2xl mx-auto transition-all duration-1000">
+            {carouselData[currentIndex].testimonial}
+          </p>
+        </div>
+      </div>
+
+      <button
+        onClick={prevSlide}
+        className="absolute left-4 md:left-8 top-1/2 -translate-y-1/2 z-30 w-12 h-12 bg-white/10 backdrop-blur-sm rounded-full flex items-center justify-center text-white hover:bg-white/20 transition-all"
+      >
+        <ChevronLeft className="w-6 h-6" />
+      </button>
+      <button
+        onClick={nextSlide}
+        className="absolute right-4 md:right-8 top-1/2 -translate-y-1/2 z-30 w-12 h-12 bg-white/10 backdrop-blur-sm rounded-full flex items-center justify-center text-white hover:bg-white/20 transition-all"
+      >
+        <ChevronRight className="w-6 h-6" />
+      </button>
+
+      <div className="absolute bottom-12 left-1/2 -translate-x-1/2 z-30 flex gap-3">
+        {carouselData.map((_, index) => (
+          <button
+            key={index}
+            onClick={() => goToSlide(index)}
+            className={`w-3 h-3 rounded-full transition-all ${
+              index === currentIndex ? 'bg-glow-green w-8' : 'bg-white/30 hover:bg-white/50'
+            }`}
+          />
+        ))}
+      </div>
+    </div>
+  );
+}

+ 166 - 0
src/components/layout/Sidebar.tsx

@@ -0,0 +1,166 @@
+import { useState } from 'react';
+import { useNavigate, useLocation } from 'react-router-dom';
+import { useAuthStore } from '@/store/authStore';
+import { 
+  LayoutDashboard, 
+  Settings, 
+  Database, 
+  Users, 
+  Key, 
+  FileText, 
+  BarChart3, 
+  Wallet,
+  ChevronDown,
+  ChevronRight,
+  Sparkles,
+  Building2,
+  Menu
+} from 'lucide-react';
+import type { Role } from '@/types';
+
+const menuItems: Record<Role, { icon: typeof LayoutDashboard; label: string; path: string; children?: { label: string; path: string }[] }[]> = {
+  super_admin: [
+    { icon: LayoutDashboard, label: '数据看板', path: '/dashboard' },
+    { icon: Sparkles, label: '渠道管理', path: '/channels' },
+    { icon: Database, label: '模型管理', path: '/models' },
+    { icon: Building2, label: '企业管理', path: '/enterprises' },
+    { icon: Users, label: '管理员管理', path: '/admins' },
+    { icon: Users, label: '用户管理', path: '/users' },
+    { icon: Key, label: 'API密钥', path: '/api-keys' },
+    { icon: FileText, label: '调用日志', path: '/call-logs' },
+    { icon: BarChart3, label: 'Token统计', path: '/token-stats' },
+    { icon: Wallet, label: '费用统计', path: '/cost-stats' },
+    { icon: Settings, label: '系统设置', path: '/settings' },
+  ],
+  platform_operator: [
+    { icon: LayoutDashboard, label: '数据看板', path: '/dashboard' },
+    { icon: FileText, label: '调用日志', path: '/call-logs' },
+    { icon: BarChart3, label: 'Token统计', path: '/token-stats' },
+    { icon: Wallet, label: '费用统计', path: '/cost-stats' },
+  ],
+  enterprise_admin: [
+    { icon: LayoutDashboard, label: '数据看板', path: '/dashboard' },
+    { icon: Building2, label: '部门管理', path: '/departments' },
+    { icon: Users, label: '用户管理', path: '/users' },
+    { icon: Key, label: 'API密钥', path: '/api-keys' },
+    { icon: FileText, label: '调用日志', path: '/call-logs' },
+    { icon: BarChart3, label: 'Token统计', path: '/token-stats' },
+    { icon: Wallet, label: '费用统计', path: '/cost-stats' },
+  ],
+  employee: [
+    { icon: LayoutDashboard, label: '数据看板', path: '/dashboard' },
+    { icon: Key, label: 'API密钥', path: '/api-keys' },
+    { icon: FileText, label: '使用日志', path: '/call-logs' },
+  ],
+};
+
+export function Sidebar() {
+  const navigate = useNavigate();
+  const location = useLocation();
+  const { user, logout } = useAuthStore();
+  const [collapsed, setCollapsed] = useState(false);
+  const [expandedMenus, setExpandedMenus] = useState<string[]>([]);
+
+  if (!user) return null;
+
+  const items = menuItems[user.role];
+
+  const toggleMenu = (label: string) => {
+    setExpandedMenus(prev => 
+      prev.includes(label) ? prev.filter(l => l !== label) : [...prev, label]
+    );
+  };
+
+  const handleLogout = () => {
+    logout();
+    navigate('/login');
+  };
+
+  return (
+    <aside 
+      className={`fixed left-0 top-0 h-full bg-slate-900 text-white transition-all duration-300 z-50 ${collapsed ? 'w-20' : 'w-64'}`}
+    >
+      <div className="flex items-center h-16 px-4 border-b border-slate-700">
+        <div className="flex items-center gap-3 flex-1">
+          <div className="w-8 h-8 bg-gradient-to-br from-cyan-400 to-blue-600 rounded-lg flex items-center justify-center">
+            <Sparkles className="w-5 h-5" />
+          </div>
+          {!collapsed && <span className="font-bold text-lg">Token运营平台</span>}
+        </div>
+        <button 
+          onClick={() => setCollapsed(!collapsed)}
+          className="p-2 hover:bg-slate-800 rounded-lg"
+        >
+          <Menu className="w-5 h-5" />
+        </button>
+      </div>
+
+      <nav className="p-4 space-y-1">
+        {items.map((item) => {
+          const hasChildren = item.children && item.children.length > 0;
+          const isActive = location.pathname === item.path || 
+            (item.children?.some(c => location.pathname === c.path) ?? false);
+
+          return (
+            <div key={item.label}>
+              <button
+                onClick={() => {
+                  if (hasChildren) {
+                    toggleMenu(item.label);
+                  } else {
+                    navigate(item.path);
+                  }
+                }}
+                className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors ${
+                  isActive ? 'bg-slate-700 text-cyan-400' : 'hover:bg-slate-800 text-slate-300'
+                }`}
+              >
+                <item.icon className="w-5 h-5 flex-shrink-0" />
+                {!collapsed && (
+                  <>
+                    <span className="flex-1 text-left">{item.label}</span>
+                    {hasChildren && (
+                      expandedMenus.includes(item.label) 
+                        ? <ChevronDown className="w-4 h-4" /> 
+                        : <ChevronRight className="w-4 h-4" />
+                    )}
+                  </>
+                )}
+              </button>
+
+              {hasChildren && !collapsed && expandedMenus.includes(item.label) && (
+                <div className="ml-4 mt-1 space-y-1">
+                  {item.children.map((child) => (
+                    <button
+                      key={child.label}
+                      onClick={() => navigate(child.path)}
+                      className={`w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-colors ${
+                        location.pathname === child.path 
+                          ? 'bg-slate-700 text-cyan-400' 
+                          : 'hover:bg-slate-800 text-slate-300'
+                      }`}
+                    >
+                      <ChevronRight className="w-4 h-4 flex-shrink-0" />
+                      <span className="text-sm">{child.label}</span>
+                    </button>
+                  ))}
+                </div>
+              )}
+            </div>
+          );
+        })}
+      </nav>
+
+      <div className="absolute bottom-0 left-0 right-0 p-4 border-t border-slate-700">
+        <button
+          onClick={handleLogout}
+          className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-slate-800 text-slate-300 transition-colors ${
+            collapsed ? 'justify-center' : ''
+          }`}
+        >
+          <span className="text-slate-400">退出登录</span>
+        </button>
+      </div>
+    </aside>
+  );
+}

+ 116 - 0
src/components/product/PlatformArchitecture.tsx

@@ -0,0 +1,116 @@
+import { Layers, Database, Cloud, Shield, Cpu, Network } from 'lucide-react';
+
+export function PlatformArchitecture() {
+  const architectureLayers = [
+    {
+      name: '应用层',
+      icon: <Layers className="w-8 h-8" />,
+      description: '面向业务场景的智能体应用',
+      items: ['智能知识库', '智能客服', '数据分析助手', '巡检辅助'],
+      image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=enterprise%20application%20layer%20AI%20intelligent%20agent%20business%20interface%20dashboard%20futuristic%20tech&image_size=landscape_4_3',
+    },
+    {
+      name: '智能体编排层',
+      icon: <Cpu className="w-8 h-8" />,
+      description: '可视化拖拽编排引擎',
+      items: ['流程编排', '逻辑配置', '在线调试', '版本管理'],
+      image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=visual%20workflow%20orchestration%20drag%20drop%20AI%20agent%20editor%20modern%20interface%20technology&image_size=landscape_4_3',
+    },
+    {
+      name: '技能组件层',
+      icon: <Network className="w-8 h-8" />,
+      description: '可复用技能组件库',
+      items: ['文档处理', '数据分析', '故障研判', '报表生成'],
+      image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=reusable%20skill%20components%20library%20AI%20modules%20toolbox%20software%20architecture%20technology&image_size=landscape_4_3',
+    },
+    {
+      name: '协议调度层',
+      icon: <Network className="w-8 h-8" />,
+      description: 'MCP核心协议',
+      items: ['智能体通信', '数据互通', '系统对接', '设备连接'],
+      image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=MCP%20protocol%20network%20communication%20data%20flow%20system%20integration%20enterprise%20architecture&image_size=landscape_4_3',
+    },
+    {
+      name: '数据层',
+      icon: <Database className="w-8 h-8" />,
+      description: '多源异构数据接入',
+      items: ['文档数据', '业务数据', '设备数据', '台账数据'],
+      image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=enterprise%20database%20data%20layer%20storage%20infrastructure%20big%20data%20architecture%20technology&image_size=landscape_4_3',
+    },
+    {
+      name: '基础设施层',
+      icon: <Cloud className="w-8 h-8" />,
+      description: '企业级安全保障',
+      items: ['私有化部署', '权限管理', '日志溯源', '等保合规'],
+      image: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=cloud%20infrastructure%20security%20enterprise%20IT%20architecture%20data%20center%20protection%20technology&image_size=landscape_4_3',
+    },
+  ];
+
+  return (
+    <section className="py-20 bg-white">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-3 tracking-wider uppercase">
+            平台架构
+          </span>
+          <h2 className="text-3xl md:text-4xl font-bold text-deep-space-900 mb-4">
+            六层技术架构
+          </h2>
+          <p className="text-deep-space-500 text-lg max-w-3xl mx-auto">
+            平台采用分层架构设计,从基础设施到应用层,各层职责清晰、协同工作,保障企业AI应用的稳定运行与灵活扩展。
+          </p>
+        </div>
+
+        <div className="max-w-6xl mx-auto">
+          <div className="relative">
+            <div className="absolute left-1/2 top-0 bottom-0 w-1 bg-gradient-to-b from-glow-green to-tech-cyan-500 -translate-x-1/2 hidden lg:block" />
+            
+            <div className="space-y-8">
+              {architectureLayers.map((layer, index) => (
+                <div
+                  key={layer.name}
+                  className={`relative grid grid-cols-1 lg:grid-cols-2 gap-8 items-center ${
+                    index % 2 === 0 ? 'lg:text-right' : 'lg:text-left'
+                  }`}
+                >
+                  <div className={`${index % 2 === 1 ? 'lg:order-2' : ''}`}>
+                    <div className="bg-deep-space-50 rounded-xl p-6 hover:shadow-lg transition-shadow">
+                      <div className={`flex items-center gap-4 mb-4 ${index % 2 === 0 ? 'lg:justify-end' : ''}`}>
+                        <div className="w-14 h-14 bg-gradient-to-br from-glow-green/10 to-tech-cyan-500/10 rounded-xl flex items-center justify-center text-glow-green">
+                          {layer.icon}
+                        </div>
+                        <div>
+                          <h3 className="text-xl font-bold text-deep-space-900">{layer.name}</h3>
+                          <p className="text-sm text-deep-space-500">{layer.description}</p>
+                        </div>
+                      </div>
+                      <img
+                        src={layer.image}
+                        alt={layer.name}
+                        className="w-full h-32 object-cover rounded-lg mb-4"
+                      />
+                      <ul className={`flex flex-wrap gap-2 ${index % 2 === 0 ? 'lg:justify-end' : ''}`}>
+                        {layer.items.map((item) => (
+                          <li
+                            key={item}
+                            className="px-3 py-1 text-xs font-medium text-deep-space-600 bg-white rounded-full border border-deep-space-200"
+                          >
+                            {item}
+                          </li>
+                        ))}
+                      </ul>
+                    </div>
+                  </div>
+                  
+                  <div className={`hidden lg:flex items-center justify-center ${index % 2 === 1 ? 'lg:order-1' : ''}`}>
+                    <div className="w-8 h-8 bg-white border-4 border-glow-green rounded-full" />
+                  </div>
+                </div>
+              ))}
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 117 - 0
src/components/solutions/PowerSolution.tsx

@@ -0,0 +1,117 @@
+import { Zap, Shield, LineChart, FileText } from 'lucide-react';
+
+export function PowerSolution() {
+  return (
+    <section className="pt-32 pb-20 bg-gradient-to-br from-deep-space-900 via-deep-space-800 to-deep-space-900">
+      <div className="container mx-auto px-4 md:px-6">
+        <div className="text-center mb-16">
+          <span className="inline-block text-glow-green text-sm font-medium mb-4 tracking-wider uppercase">
+            标杆解决方案
+          </span>
+          <h1 className="text-4xl md:text-5xl font-bold text-white mb-6">
+            电力配网运维AI解决方案
+          </h1>
+          <p className="text-xl text-deep-space-200 max-w-3xl mx-auto">
+            基于哈灵平台,为电力行业提供智能化运维解决方案,实现配网运维自动化、故障智能研判、隐患智能排查。
+          </p>
+        </div>
+
+        <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
+          <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10">
+            <div className="w-14 h-14 bg-glow-green/20 rounded-xl flex items-center justify-center text-glow-green mb-6">
+              <Zap className="w-7 h-7" />
+            </div>
+            <h3 className="text-xl font-bold text-white mb-4">故障智能研判</h3>
+            <p className="text-deep-space-300 mb-6">
+              基于大模型的故障诊断能力,自动分析设备数据与运行状态,智能识别故障类型与原因,输出精准维修建议。
+            </p>
+            <ul className="space-y-3">
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-glow-green rounded-full" />
+                实时监控设备状态
+              </li>
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-glow-green rounded-full" />
+                智能故障定位与分类
+              </li>
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-glow-green rounded-full" />
+                自动生成维修工单
+              </li>
+            </ul>
+          </div>
+
+          <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10">
+            <div className="w-14 h-14 bg-tech-cyan-500/20 rounded-xl flex items-center justify-center text-tech-cyan-400 mb-6">
+              <LineChart className="w-7 h-7" />
+            </div>
+            <h3 className="text-xl font-bold text-white mb-4">智能巡检辅助</h3>
+            <p className="text-deep-space-300 mb-6">
+              分析设备日志与巡检数据,识别异常隐患、预判风险、输出维修运维建议,提升巡检效率与质量。
+            </p>
+            <ul className="space-y-3">
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-tech-cyan-400 rounded-full" />
+                自动化数据采集与分析
+              </li>
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-tech-cyan-400 rounded-full" />
+                隐患智能识别与预警
+              </li>
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-tech-cyan-400 rounded-full" />
+                巡检路线优化建议
+              </li>
+            </ul>
+          </div>
+
+          <div className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10">
+            <div className="w-14 h-14 bg-purple-500/20 rounded-xl flex items-center justify-center text-purple-400 mb-6">
+              <FileText className="w-7 h-7" />
+            </div>
+            <h3 className="text-xl font-bold text-white mb-4">知识库管理</h3>
+            <p className="text-deep-space-300 mb-6">
+              构建电力行业专属知识库,实现运维规程、设备资料、故障案例的智能管理与快速检索。
+            </p>
+            <ul className="space-y-3">
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-purple-400 rounded-full" />
+                文档智能入库与分类
+              </li>
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-purple-400 rounded-full" />
+                知识切片与向量检索
+              </li>
+              <li className="flex items-center gap-3 text-deep-space-200">
+                <div className="w-2 h-2 bg-purple-400 rounded-full" />
+                智能问答与知识沉淀
+              </li>
+            </ul>
+          </div>
+        </div>
+
+        <div className="mt-16 bg-white/5 backdrop-blur-sm rounded-2xl p-8 border border-white/10">
+          <h3 className="text-2xl font-bold text-white mb-6">落地成果</h3>
+          <div className="grid grid-cols-2 md:grid-cols-4 gap-8">
+            <div className="text-center">
+              <div className="text-4xl font-bold text-glow-green mb-2">40%</div>
+              <div className="text-deep-space-300">运维效率提升</div>
+            </div>
+            <div className="text-center">
+              <div className="text-4xl font-bold text-tech-cyan-400 mb-2">60%</div>
+              <div className="text-deep-space-300">故障处理时间缩短</div>
+            </div>
+            <div className="text-center">
+              <div className="text-4xl font-bold text-purple-400 mb-2">80%</div>
+              <div className="text-deep-space-300">知识检索效率提升</div>
+            </div>
+            <div className="text-center">
+              <div className="text-4xl font-bold text-white mb-2">0</div>
+              <div className="text-deep-space-300">重大事故发生</div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+  );
+}

+ 203 - 0
src/data/mockData.ts

@@ -0,0 +1,203 @@
+export const carouselData = [
+  {
+    id: 1,
+    title: '哈灵企业级大模型应用开发平台',
+    subtitle: '0基础可视化编排 · 全行业极速AI落地 · 技术自主创新',
+    testimonial: '零代码搭建企业智能体,让AI深度落地业务',
+    bgGradient: 'from-deep-space-950 via-deep-space-900 to-deep-space-800',
+  },
+  {
+    id: 2,
+    title: '硬核产学研技术壁垒 · 工业级AI智能体平台',
+    subtitle: '可视化智能体编排|MCP核心协议|Skills可复用技能库|企业级安全合规',
+    testimonial: '解决企业AI浅层应用、数据孤岛、自动化低效痛点',
+    bgGradient: 'from-deep-space-900 via-slate-900 to-deep-space-800',
+  },
+  {
+    id: 3,
+    title: '实战落地验证 · 政企级AI解决方案服务商',
+    subtitle: '电力标杆落地、全行业赋能升级,服务国网电科院、大型信通企业',
+    testimonial: '双标杆案例真实可验证,落地能力可信',
+    bgGradient: 'from-deep-space-800 via-deep-space-900 to-deep-space-950',
+  },
+];
+
+export const brandStats = [
+  { value: '自研', label: '企业级平台' },
+  { value: '核心技术', label: '多维度能力' },
+  { value: '全行业', label: '场景覆盖' },
+  { value: '标杆', label: '项目验证' },
+];
+
+export const coreCapabilities = [
+  {
+    id: 1,
+    title: '可视化智能体编排',
+    subtitle: '0基础核心优势',
+    description: '零代码拖拽操作,支持业务流程编排、逻辑配置、在线调试、版本管理、一键部署,非技术人员可独立完成智能体从0到1搭建,彻底打破AI开发技术壁垒。',
+    icon: 'Blocks',
+  },
+  {
+    id: 2,
+    title: 'MCP核心协议',
+    subtitle: '企业级调度通信',
+    description: '自研企业级智能体调度通信协议,打通大模型、多智能体、业务系统、设备终端、数据库数据壁垒,实现安全、稳定、高效互联互通,解决企业数据割裂、调度混乱问题。',
+    icon: 'Network',
+  },
+  {
+    id: 3,
+    title: 'Skills可复用技能库',
+    subtitle: '标准化能力组件',
+    description: '丰富的标准化Skills组件库,涵盖知识问答、数据处理、流程自动化等高频场景,支持一键复用、自定义开发、版本管理,大幅降低企业AI开发成本与周期。',
+    icon: 'Wrench',
+  },
+  {
+    id: 4,
+    title: '企业级安全适配',
+    subtitle: '合规可信保障',
+    description: '满足等保三级、数据安全法等合规要求,支持私有化部署、数据脱敏、访问控制、审计日志等安全能力,确保企业数据安全与业务合规。',
+    icon: 'Shield',
+  },
+];
+
+export const painPointSolutions = [
+  {
+    id: 1,
+    title: '解决AI应用浮于表面',
+    icon: 'Target',
+    description: '通过可视化编排与业务流程深度融合,将AI能力嵌入业务场景核心环节,实现从"锦上添花"到"不可或缺"的转变,让AI真正创造业务价值。',
+  },
+  {
+    id: 2,
+    title: '解决数据孤岛问题',
+    icon: 'Database',
+    description: '自研MCP协议打通企业内部各系统数据壁垒,实现跨系统数据共享与协同,让AI模型能够获取全面、准确的数据支撑,提升决策质量。',
+  },
+  {
+    id: 3,
+    title: '解决自动化程度低',
+    icon: 'Zap',
+    description: '通过智能体自动化执行流程,减少人工干预,实现7x24小时不间断服务,大幅提升业务处理效率,降低人力成本。',
+  },
+  {
+    id: 4,
+    title: '解决落地成本高、复用差',
+    icon: 'TrendingDown',
+    description: '标准化Skills组件库支持跨项目复用,0基础可视化编排降低技术门槛,一键部署简化上线流程,让企业以最低成本实现AI深度落地。',
+  },
+];
+
+export const applicationScenarios = [
+  {
+    id: 1,
+    name: '智能问答助手',
+    icon: 'Headphones',
+    description: '基于企业知识库的智能问答系统,支持多轮对话、意图识别、知识检索,为员工和客户提供7x24小时智能服务。',
+    industries: ['电力', '政务', '金融'],
+  },
+  {
+    id: 2,
+    name: '文档智能处理',
+    icon: 'FileText',
+    description: '自动解析、提取、分类企业文档,支持合同审核、报告生成、数据录入等场景,大幅提升文档处理效率。',
+    industries: ['制造', '金融', '办公'],
+  },
+  {
+    id: 3,
+    name: '业务流程自动化',
+    icon: 'Zap',
+    description: '基于可视化编排的业务流程自动化,支持审批流程、数据同步、任务调度等场景,实现端到端自动化。',
+    industries: ['电力', '政务', '制造'],
+  },
+  {
+    id: 4,
+    name: '数据分析与决策',
+    icon: 'BarChart3',
+    description: '自动分析业务数据,生成可视化报表与洞察,辅助管理层做出科学决策,提升企业运营效率。',
+    industries: ['金融', '制造', '电力'],
+  },
+  {
+    id: 5,
+    name: '智能客服系统',
+    icon: 'Headphones',
+    description: '全渠道智能客服解决方案,支持文本、语音、视频等多种交互方式,提升客户服务体验与效率。',
+    industries: ['金融', '零售', '电信'],
+  },
+  {
+    id: 6,
+    name: '知识管理平台',
+    icon: 'BookOpen',
+    description: '企业知识资产的智能管理与应用平台,支持知识沉淀、检索、分享、推荐,打造学习型组织。',
+    industries: ['政务', '教育', '企业'],
+  },
+];
+
+export const caseStudies = [
+  {
+    id: 1,
+    title: '某电科院智能运维项目',
+    icon: 'Zap',
+    year: '2025年',
+    background: '某电科院作为国家级电力科研机构,面临海量设备数据处理、故障预警滞后、运维效率低下等痛点,亟需智能化解决方案提升运维水平。',
+    solution: '基于哈灵平台搭建智能运维智能体,整合设备监测数据、历史故障数据、专家经验知识,实现设备故障智能预警、运维工单自动生成、知识库智能问答等核心功能。',
+    results: ['故障预警准确率提升95%', '运维响应时间缩短60%', '年运维成本降低30%', '知识复用率提升70%'],
+    highlight: '项目已通过国家电网公司验收,成为电力行业AI运维标杆案例。',
+  },
+  {
+    id: 2,
+    title: '某大型信通企业智能客服项目',
+    icon: 'Database',
+    year: '2025年',
+    background: '某大型信通企业服务千万级用户,传统客服模式面临人力成本高、响应速度慢、服务质量不稳定等问题,亟需智能化升级。',
+    solution: '基于哈灵平台搭建全渠道智能客服系统,支持文本、语音、视频多模态交互,整合企业业务系统数据,实现智能问答、工单流转、用户画像等核心功能。',
+    results: ['客服效率提升400%', '用户满意度提升35%', '人力成本降低50%', '响应时间缩短80%'],
+    highlight: '系统上线后日均处理量突破万次,成为信通行业智能客服标杆案例。',
+  },
+];
+
+export const whatWeDoData = [
+  {
+    id: 1,
+    title: '智能体平台搭建',
+    icon: 'Network',
+    description: '为企业提供从0到1的智能体平台搭建服务,包括需求分析、架构设计、功能开发、部署上线全流程支持。',
+    features: ['需求定制', '架构设计', '功能开发', '部署上线'],
+  },
+  {
+    id: 2,
+    title: '行业解决方案',
+    icon: 'Zap',
+    description: '针对电力、政务、金融、制造等行业特点,提供深度定制的AI解决方案,助力行业数字化转型。',
+    features: ['电力AI', '政务AI', '金融AI', '制造AI'],
+  },
+  {
+    id: 3,
+    title: '私有化部署',
+    icon: 'Brain',
+    description: '支持本地化私有化部署,确保企业数据安全与业务合规,提供从部署到运维的全生命周期服务。',
+    features: ['本地部署', '数据安全', '运维支持', '版本升级'],
+  },
+  {
+    id: 4,
+    title: '技术共建',
+    icon: 'Plug',
+    description: '与企业建立长期技术合作关系,共同开展AI技术研发与创新,推动行业技术进步。',
+    features: ['联合研发', '技术咨询', '人才培养', '成果转化'],
+  },
+];
+
+export const cooperationTypes = ['全行业定制', '私有化部署', '渠道合作', '技术共建'];
+
+export const contactInfo = {
+  name: '张经理',
+  phone: '132 6923 0676',
+  address: '北京市朝阳区',
+};
+
+export const navLinks = [
+  { name: '首页', href: '/' },
+  { name: '产品', href: '/product' },
+  { name: '解决方案', href: '/solutions' },
+  { name: '案例', href: '/cases' },
+  { name: '关于我们', href: '/about' },
+];

+ 29 - 0
src/hooks/useTheme.ts

@@ -0,0 +1,29 @@
+import { useState, useEffect } from 'react';
+
+type Theme = 'light' | 'dark';
+
+export function useTheme() {
+  const [theme, setTheme] = useState<Theme>(() => {
+    const savedTheme = localStorage.getItem('theme') as Theme;
+    if (savedTheme) {
+      return savedTheme;
+    }
+    return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+  });
+
+  useEffect(() => {
+    document.documentElement.classList.remove('light', 'dark');
+    document.documentElement.classList.add(theme);
+    localStorage.setItem('theme', theme);
+  }, [theme]);
+
+  const toggleTheme = () => {
+    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
+  };
+
+  return {
+    theme,
+    toggleTheme,
+    isDark: theme === 'dark'
+  };
+} 

+ 187 - 0
src/index.css

@@ -0,0 +1,187 @@
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+  --color-primary: #00D4AA;
+  --color-primary-dark: #0891b2;
+  --color-secondary: #6366F1;
+  --color-bg-dark: #0a1628;
+  --color-bg-card: #102a43;
+  --color-bg-light: #f5f7fa;
+  --color-text-primary: #ffffff;
+  --color-text-secondary: #9fb3c8;
+  --color-text-dark: #102a43;
+  --color-border: rgba(255, 255, 255, 0.1);
+  --color-border-light: rgba(16, 42, 67, 0.1);
+  
+  font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
+  line-height: 1.6;
+  font-weight: 400;
+  
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+  margin: 0;
+  min-height: 100vh;
+  background-color: var(--color-bg-light);
+  color: var(--color-text-dark);
+}
+
+html {
+  scroll-behavior: smooth;
+}
+
+::-webkit-scrollbar {
+  width: 8px;
+}
+
+::-webkit-scrollbar-track {
+  background: rgba(0, 0, 0, 0.05);
+}
+
+::-webkit-scrollbar-thumb {
+  background: rgba(0, 212, 170, 0.5);
+  border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+  background: rgba(0, 212, 170, 0.7);
+}
+
+@layer components {
+  .gradient-text {
+    background: linear-gradient(135deg, #00D4AA 0%, #6366F1 100%);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+  }
+  
+  .gradient-border {
+    border: 1px solid transparent;
+    background: linear-gradient(var(--color-bg-card), var(--color-bg-card)) padding-box,
+                linear-gradient(135deg, rgba(0, 212, 170, 0.3), rgba(99, 102, 241, 0.3)) border-box;
+  }
+  
+  .glass-effect {
+    background: rgba(16, 42, 67, 0.8);
+    backdrop-filter: blur(10px);
+    -webkit-backdrop-filter: blur(10px);
+  }
+  
+  .glow-effect {
+    box-shadow: 0 0 30px rgba(0, 212, 170, 0.3);
+  }
+  
+  .card-hover {
+    transition: all 0.3s ease;
+  }
+  
+  .card-hover:hover {
+    transform: translateY(-5px);
+    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+  }
+  
+  .btn-primary {
+    background: linear-gradient(135deg, #00D4AA 0%, #06b6d4 100%);
+    color: #ffffff;
+    font-weight: 600;
+    padding: 12px 28px;
+    border-radius: 8px;
+    border: none;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    display: inline-flex;
+    align-items: center;
+    gap: 8px;
+  }
+  
+  .btn-primary:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 10px 30px rgba(0, 212, 170, 0.4);
+  }
+  
+  .btn-secondary {
+    background: transparent;
+    color: #00D4AA;
+    font-weight: 600;
+    padding: 12px 28px;
+    border-radius: 8px;
+    border: 1px solid #00D4AA;
+    cursor: pointer;
+    transition: all 0.3s ease;
+    display: inline-flex;
+    align-items: center;
+    gap: 8px;
+  }
+  
+  .btn-secondary:hover {
+    background: rgba(0, 212, 170, 0.1);
+    transform: translateY(-2px);
+  }
+  
+  .section-padding {
+    padding: 80px 0;
+  }
+  
+  .text-gradient-dark {
+    background: linear-gradient(135deg, #0a1628 0%, #102a43 100%);
+    -webkit-background-clip: text;
+    -webkit-text-fill-color: transparent;
+    background-clip: text;
+  }
+  
+  .animate-float {
+    animation: float 6s ease-in-out infinite;
+  }
+  
+  @keyframes float {
+    0%, 100% {
+      transform: translateY(0);
+    }
+    50% {
+      transform: translateY(-20px);
+    }
+  }
+  
+  .animate-pulse-glow {
+    animation: pulseGlow 2s ease-in-out infinite;
+  }
+  
+  @keyframes pulseGlow {
+    0%, 100% {
+      box-shadow: 0 0 20px rgba(0, 212, 170, 0.3);
+    }
+    50% {
+      box-shadow: 0 0 40px rgba(0, 212, 170, 0.6);
+    }
+  }
+}
+
+input:focus, textarea:focus, select:focus {
+  outline: none;
+}
+
+a {
+  color: inherit;
+  text-decoration: none;
+}
+
+a:hover {
+  text-decoration: none;
+}
+
+button:focus {
+  outline: none;
+}
+
+img {
+  max-width: 100%;
+  height: auto;
+}

+ 6 - 0
src/lib/utils.ts

@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+  return twMerge(clsx(inputs))
+}

+ 10 - 0
src/main.tsx

@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App'
+import './index.css'
+
+createRoot(document.getElementById('root')!).render(
+  <StrictMode>
+    <App />
+  </StrictMode>,
+)

+ 365 - 0
src/pages/APIKeys.tsx

@@ -0,0 +1,365 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { useAuthStore } from '@/store/authStore';
+import { Plus, Edit, Trash2, CheckCircle, XCircle, Copy, Check, Key, Eye } from 'lucide-react';
+import type { APIKey, KeyStatus } from '@/types';
+
+export function APIKeys() {
+  const [apiKeys, setApiKeys] = useState<APIKey[]>([]);
+  const [models, setModels] = useState<{ id: string; name: string }[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [showModal, setShowModal] = useState(false);
+  const [editingKey, setEditingKey] = useState<APIKey | null>(null);
+  const [copiedId, setCopiedId] = useState<string | null>(null);
+  const [form, setForm] = useState({
+    name: '',
+    quota: 100000,
+    allowedModels: [] as string[],
+    expiresAt: '',
+    status: 'active' as KeyStatus,
+  });
+  const { user } = useAuthStore();
+
+  useEffect(() => {
+    fetchData();
+  }, []);
+
+  const fetchData = async () => {
+    setLoading(true);
+    try {
+      const [keysData, modelsData] = await Promise.all([
+        api.apiKeys.list(user?.role === 'employee' ? user?.id : undefined),
+        api.models.list(),
+      ]);
+      setApiKeys(keysData);
+      setModels(modelsData.map(m => ({ id: m.id, name: m.name })));
+    } catch (err) {
+      console.error('Failed to fetch data:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleOpenModal = (apiKey?: APIKey) => {
+    if (apiKey) {
+      setEditingKey(apiKey);
+      setForm({
+        name: apiKey.name,
+        quota: apiKey.quota,
+        allowedModels: [...apiKey.allowedModels],
+        expiresAt: apiKey.expiresAt,
+        status: apiKey.status,
+      });
+    } else {
+      setEditingKey(null);
+      const tomorrow = new Date();
+      tomorrow.setFullYear(tomorrow.getFullYear() + 1);
+      setForm({
+        name: '',
+        quota: 100000,
+        allowedModels: [],
+        expiresAt: tomorrow.toISOString().split('T')[0],
+        status: 'active',
+      });
+    }
+    setShowModal(true);
+  };
+
+  const handleCloseModal = () => {
+    setShowModal(false);
+    setEditingKey(null);
+  };
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    try {
+      if (editingKey) {
+        await api.apiKeys.update(editingKey.id, form);
+      } else {
+        const newKey = await api.apiKeys.create({
+          ...form,
+          key: `sk-${user?.id}-${Math.random().toString(36).substring(2, 18)}`,
+          userId: user?.id || '',
+          usedQuota: 0,
+        });
+      }
+      handleCloseModal();
+      fetchData();
+    } catch (err) {
+      console.error('Failed to save API key:', err);
+    }
+  };
+
+  const handleDelete = async (id: string) => {
+    if (confirm('确定要删除该API密钥吗?')) {
+      try {
+        await api.apiKeys.delete(id);
+        fetchData();
+      } catch (err) {
+        console.error('Failed to delete API key:', err);
+      }
+    }
+  };
+
+  const handleCopy = (text: string, id: string) => {
+    navigator.clipboard.writeText(text);
+    setCopiedId(id);
+    setTimeout(() => setCopiedId(null), 2000);
+  };
+
+  const toggleModel = (modelId: string) => {
+    setForm(prev => ({
+      ...prev,
+      allowedModels: prev.allowedModels.includes(modelId)
+        ? prev.allowedModels.filter(id => id !== modelId)
+        : [...prev.allowedModels, modelId],
+    }));
+  };
+
+  const statusConfig: Record<KeyStatus, { label: string; color: string; icon: typeof CheckCircle }> = {
+    active: { label: '活跃', color: 'bg-green-100 text-green-700', icon: CheckCircle },
+    inactive: { label: '停用', color: 'bg-slate-100 text-slate-600', icon: XCircle },
+    expired: { label: '已过期', color: 'bg-red-100 text-red-700', icon: XCircle },
+  };
+
+  const canEdit = user?.role !== 'employee' || true;
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">API密钥管理</h1>
+          <p className="text-slate-500 mt-1">管理模型调用凭证</p>
+        </div>
+        {canEdit && (
+          <button
+            onClick={() => handleOpenModal()}
+            className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+          >
+            <Plus className="w-5 h-5" />
+            新建密钥
+          </button>
+        )}
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[900px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">密钥名称</th>
+                <th className="w-64 px-6 py-4 text-left text-sm font-semibold text-slate-600">密钥值</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">配额</th>
+                <th className="w-44 px-6 py-4 text-left text-sm font-semibold text-slate-600">允许模型</th>
+                <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">过期时间</th>
+                <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">状态</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {apiKeys.map((apiKey) => {
+              const status = statusConfig[apiKey.status];
+              const StatusIcon = status.icon;
+              const usedPercent = (apiKey.usedQuota / apiKey.quota) * 100;
+              const allowedModelNames = apiKey.allowedModels.map(id => models.find(m => m.id === id)?.name).filter(Boolean);
+              
+              return (
+                <tr key={apiKey.id} className="hover:bg-slate-50 transition-colors">
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-3">
+                      <div className="w-8 h-8 rounded-lg bg-cyan-100 text-cyan-600 flex items-center justify-center">
+                        <Key className="w-4 h-4" />
+                      </div>
+                      <span className="font-medium text-slate-800">{apiKey.name}</span>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-2">
+                      <code className="text-sm text-slate-600 font-mono">{apiKey.key}</code>
+                      <button
+                        onClick={() => handleCopy(apiKey.key, apiKey.id)}
+                        className="p-1.5 hover:bg-slate-100 rounded transition-colors"
+                      >
+                        {copiedId === apiKey.id ? (
+                          <Check className="w-4 h-4 text-green-600" />
+                        ) : (
+                          <Copy className="w-4 h-4 text-slate-400" />
+                        )}
+                      </button>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex flex-col gap-1">
+                      <div className="flex items-center justify-between text-xs">
+                        <span className="text-slate-600">{apiKey.quota.toLocaleString()}</span>
+                        <span className="text-slate-500">已用: {apiKey.usedQuota.toLocaleString()}</span>
+                      </div>
+                      <div className="h-1.5 bg-slate-100 rounded-full overflow-hidden">
+                        <div 
+                          className={`h-full rounded-full transition-all ${usedPercent > 80 ? 'bg-red-500' : usedPercent > 50 ? 'bg-orange-500' : 'bg-cyan-500'}`}
+                          style={{ width: `${Math.min(usedPercent, 100)}%` }}
+                        ></div>
+                      </div>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex flex-wrap gap-1">
+                      {allowedModelNames.length > 0 ? (
+                        allowedModelNames.map((name, index) => (
+                          <span key={index} className="px-2 py-0.5 bg-slate-100 text-slate-600 text-xs rounded">
+                            {name}
+                          </span>
+                        ))
+                      ) : (
+                        <span className="text-xs text-slate-400">全部模型</span>
+                      )}
+                    </div>
+                  </td>
+                  <td className="px-6 py-4 text-slate-600">{apiKey.expiresAt}</td>
+                  <td className="px-6 py-4">
+                    <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${status.color}`}>
+                      <StatusIcon className="w-3 h-3" />
+                      {status.label}
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-2">
+                      {canEdit && (
+                        <>
+                          <button
+                            onClick={() => handleOpenModal(apiKey)}
+                            className="p-2 text-slate-500 hover:text-cyan-600 hover:bg-cyan-50 rounded-lg transition-colors"
+                          >
+                            <Edit className="w-4 h-4" />
+                          </button>
+                          <button
+                            onClick={() => handleDelete(apiKey.id)}
+                            className="p-2 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
+                          >
+                            <Trash2 className="w-4 h-4" />
+                          </button>
+                        </>
+                      )}
+                      {!canEdit && (
+                        <button className="p-2 text-slate-500 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors">
+                          <Eye className="w-4 h-4" />
+                        </button>
+                      )}
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+          </table>
+        </div>
+      </div>
+
+      {showModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg max-h-[90vh] overflow-y-auto">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingKey ? '编辑API密钥' : '新建API密钥'}</h2>
+              <button onClick={handleCloseModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleSubmit} className="p-6 space-y-4">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">密钥名称</label>
+                <input
+                  type="text"
+                  value={form.name}
+                  onChange={(e) => setForm({ ...form, name: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">月度Token配额</label>
+                <input
+                  type="number"
+                  value={form.quota}
+                  onChange={(e) => setForm({ ...form, quota: parseInt(e.target.value) || 0 })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  min="0"
+                />
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">允许调用的模型(不选则允许全部)</label>
+                <div className="flex flex-wrap gap-2">
+                  {models.map(model => (
+                    <button
+                      key={model.id}
+                      type="button"
+                      onClick={() => toggleModel(model.id)}
+                      className={`px-3 py-1.5 rounded-lg text-sm transition-all ${
+                        form.allowedModels.includes(model.id)
+                          ? 'bg-cyan-500 text-white'
+                          : 'bg-slate-100 text-slate-600 hover:bg-slate-200'
+                      }`}
+                    >
+                      {model.name}
+                    </button>
+                  ))}
+                </div>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">过期时间</label>
+                <input
+                  type="date"
+                  value={form.expiresAt}
+                  onChange={(e) => setForm({ ...form, expiresAt: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                />
+              </div>
+
+              {editingKey && (
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">状态</label>
+                  <select
+                    value={form.status}
+                    onChange={(e) => setForm({ ...form, status: e.target.value as KeyStatus })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  >
+                    <option value="active">活跃</option>
+                    <option value="inactive">停用</option>
+                    <option value="expired">已过期</option>
+                  </select>
+                </div>
+              )}
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingKey ? '保存修改' : '创建密钥'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}

+ 15 - 0
src/pages/About.tsx

@@ -0,0 +1,15 @@
+import { CompanyIntro } from '@/components/about/CompanyIntro';
+import { Vision } from '@/components/about/Vision';
+import { CompanyMap } from '@/components/about/CompanyMap';
+import { FloatingConsult } from '@/components/layout/FloatingConsult';
+
+export function About() {
+  return (
+    <main>
+      <CompanyIntro />
+      <Vision />
+      <CompanyMap />
+      <FloatingConsult />
+    </main>
+  );
+}

+ 337 - 0
src/pages/Admins.tsx

@@ -0,0 +1,337 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { Plus, Edit, Trash2, CheckCircle, XCircle, Users as UsersIcon } from 'lucide-react';
+import type { User, UserStatus, Role } from '@/types';
+
+export function Admins() {
+  const [users, setUsers] = useState<User[]>([]);
+  const [enterprises, setEnterprises] = useState<{ id: string; name: string }[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [showModal, setShowModal] = useState(false);
+  const [editingUser, setEditingUser] = useState<User | null>(null);
+  const [form, setForm] = useState({
+    username: '',
+    email: '',
+    phone: '',
+    role: 'platform_operator' as Role,
+    status: 'enabled' as UserStatus,
+    enterpriseId: '',
+    departmentId: '',
+    quota: -1,
+  });
+
+  useEffect(() => {
+    fetchData();
+  }, []);
+
+  const fetchData = async () => {
+    setLoading(true);
+    try {
+      const [usersData, enterprisesData] = await Promise.all([
+        api.users.list(),
+        api.enterprises.list(),
+      ]);
+      setUsers(usersData.filter(u => u.role !== 'employee'));
+      setEnterprises(enterprisesData.map(e => ({ id: e.id, name: e.name })));
+    } catch (err) {
+      console.error('Failed to fetch data:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleOpenModal = (user?: User) => {
+    if (user) {
+      setEditingUser(user);
+      setForm({
+        username: user.username,
+        email: user.email,
+        phone: user.phone,
+        role: user.role,
+        status: user.status,
+        enterpriseId: user.enterpriseId,
+        departmentId: user.departmentId || '',
+        quota: user.quota,
+      });
+    } else {
+      setEditingUser(null);
+      setForm({
+        username: '',
+        email: '',
+        phone: '',
+        role: 'platform_operator',
+        status: 'enabled',
+        enterpriseId: '',
+        departmentId: '',
+        quota: -1,
+      });
+    }
+    setShowModal(true);
+  };
+
+  const handleCloseModal = () => {
+    setShowModal(false);
+    setEditingUser(null);
+  };
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    try {
+      if (editingUser) {
+        await api.users.update(editingUser.id, form);
+      } else {
+        await api.users.create({
+          ...form,
+          usedQuota: 0,
+        });
+      }
+      handleCloseModal();
+      fetchData();
+    } catch (err) {
+      console.error('Failed to save admin:', err);
+    }
+  };
+
+  const handleDelete = async (id: string) => {
+    if (confirm('确定要删除该管理员吗?')) {
+      try {
+        await api.users.delete(id);
+        fetchData();
+      } catch (err) {
+        console.error('Failed to delete admin:', err);
+      }
+    }
+  };
+
+  const statusConfig: Record<UserStatus, { label: string; color: string; icon: typeof CheckCircle }> = {
+    enabled: { label: '启用', color: 'bg-green-100 text-green-700', icon: CheckCircle },
+    disabled: { label: '禁用', color: 'bg-slate-100 text-slate-600', icon: XCircle },
+    frozen: { label: '冻结', color: 'bg-orange-100 text-orange-700', icon: XCircle },
+  };
+
+  const roleLabels: Record<Role, string> = {
+    super_admin: '平台超级管理员',
+    platform_operator: '平台运营管理员',
+    enterprise_admin: '企业租户管理员',
+    employee: '普通员工',
+  };
+
+  const roleColors: Record<Role, string> = {
+    super_admin: 'bg-red-100 text-red-700',
+    platform_operator: 'bg-purple-100 text-purple-700',
+    enterprise_admin: 'bg-blue-100 text-blue-700',
+    employee: 'bg-green-100 text-green-700',
+  };
+
+  const getEnterpriseName = (id: string) => enterprises.find(e => e.id === id)?.name || '-';
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">管理员管理</h1>
+          <p className="text-slate-500 mt-1">管理平台及企业管理员账号</p>
+        </div>
+        <button
+          onClick={() => handleOpenModal()}
+          className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+        >
+          <Plus className="w-5 h-5" />
+          新增管理员
+        </button>
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[800px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">用户名</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">所属企业</th>
+                <th className="w-48 px-6 py-4 text-left text-sm font-semibold text-slate-600">联系方式</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">角色</th>
+                <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">状态</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {users.map((user) => {
+              const status = statusConfig[user.status];
+              const StatusIcon = status.icon;
+              
+              return (
+                <tr key={user.id} className="hover:bg-slate-50 transition-colors">
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-3">
+                      <div className="w-8 h-8 rounded-full bg-gradient-to-br from-cyan-400 to-blue-600 flex items-center justify-center">
+                        <UsersIcon className="w-4 h-4 text-white" />
+                      </div>
+                      <div>
+                        <p className="font-medium text-slate-800">{user.username}</p>
+                        <p className="text-xs text-slate-500">ID: {user.id}</p>
+                      </div>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4 text-slate-600">{getEnterpriseName(user.enterpriseId)}</td>
+                  <td className="px-6 py-4">
+                    <div className="flex flex-col gap-1">
+                      <span className="text-sm text-slate-600">{user.email}</span>
+                      <span className="text-xs text-slate-500">{user.phone}</span>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${roleColors[user.role]}`}>
+                      {roleLabels[user.role]}
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${status.color}`}>
+                      <StatusIcon className="w-3 h-3" />
+                      {status.label}
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-2">
+                      <button
+                        onClick={() => handleOpenModal(user)}
+                        className="p-2 text-slate-500 hover:text-cyan-600 hover:bg-cyan-50 rounded-lg transition-colors"
+                      >
+                        <Edit className="w-4 h-4" />
+                      </button>
+                      <button
+                        onClick={() => handleDelete(user.id)}
+                        className="p-2 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
+                      >
+                        <Trash2 className="w-4 h-4" />
+                      </button>
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+          </table>
+        </div>
+      </div>
+
+      {showModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingUser ? '编辑管理员' : '新增管理员'}</h2>
+              <button onClick={handleCloseModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleSubmit} className="p-6 space-y-4">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">用户名</label>
+                <input
+                  type="text"
+                  value={form.username}
+                  onChange={(e) => setForm({ ...form, username: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">邮箱</label>
+                  <input
+                    type="email"
+                    value={form.email}
+                    onChange={(e) => setForm({ ...form, email: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">手机号</label>
+                  <input
+                    type="text"
+                    value={form.phone}
+                    onChange={(e) => setForm({ ...form, phone: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">角色</label>
+                <select
+                  value={form.role}
+                  onChange={(e) => {
+                    const role = e.target.value as Role;
+                    setForm({ 
+                      ...form, 
+                      role,
+                      enterpriseId: role === 'enterprise_admin' ? form.enterpriseId || enterprises[0]?.id || '' : ''
+                    });
+                  }}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="super_admin">平台超级管理员</option>
+                  <option value="platform_operator">平台运营管理员</option>
+                  <option value="enterprise_admin">企业租户管理员</option>
+                </select>
+              </div>
+
+              {form.role === 'enterprise_admin' && (
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">所属企业</label>
+                  <select
+                    value={form.enterpriseId}
+                    onChange={(e) => setForm({ ...form, enterpriseId: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  >
+                    {enterprises.map(enterprise => (
+                      <option key={enterprise.id} value={enterprise.id}>{enterprise.name}</option>
+                    ))}
+                  </select>
+                </div>
+              )}
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">状态</label>
+                <select
+                  value={form.status}
+                  onChange={(e) => setForm({ ...form, status: e.target.value as UserStatus })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="enabled">启用</option>
+                  <option value="disabled">禁用</option>
+                  <option value="frozen">冻结</option>
+                </select>
+              </div>
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingUser ? '保存修改' : '创建管理员'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}

+ 196 - 0
src/pages/CallLogs.tsx

@@ -0,0 +1,196 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { useAuthStore } from '@/store/authStore';
+import { Download, Filter, Search, CheckCircle, XCircle, Clock } from 'lucide-react';
+import type { CallLog } from '@/types';
+
+export function CallLogs() {
+  const [logs, setLogs] = useState<CallLog[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [searchTerm, setSearchTerm] = useState('');
+  const [filterModel, setFilterModel] = useState('');
+  const [filterStatus, setFilterStatus] = useState<'all' | 'success' | 'failed'>('all');
+  const { user } = useAuthStore();
+
+  useEffect(() => {
+    fetchLogs();
+  }, []);
+
+  const fetchLogs = async () => {
+    setLoading(true);
+    try {
+      const data = await api.callLogs.list(
+        user?.role === 'employee' ? user?.id : undefined,
+        user?.role === 'enterprise_admin' ? user?.enterpriseId : undefined
+      );
+      setLogs(data);
+    } catch (err) {
+      console.error('Failed to fetch call logs:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleExport = () => {
+    const headers = ['ID', '用户', '模型', '输入Token', '输出Token', '总Token', '状态', '响应时间', '时间'];
+    const csvContent = [
+      headers.join(','),
+      ...logs.map(log => [
+        log.id,
+        log.userName,
+        log.modelName,
+        log.inputTokens,
+        log.outputTokens,
+        log.totalTokens,
+        log.status,
+        log.responseTime,
+        log.createdAt,
+      ].join(','))
+    ].join('\n');
+
+    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+    const link = document.createElement('a');
+    link.href = URL.createObjectURL(blob);
+    link.download = `call-logs-${new Date().toISOString().split('T')[0]}.csv`;
+    link.click();
+  };
+
+  const uniqueModels = [...new Set(logs.map(log => log.modelName))];
+
+  const filteredLogs = logs.filter(log => {
+    const matchesSearch = log.userName.toLowerCase().includes(searchTerm.toLowerCase()) ||
+      log.modelName.toLowerCase().includes(searchTerm.toLowerCase());
+    const matchesModel = !filterModel || log.modelName === filterModel;
+    const matchesStatus = filterStatus === 'all' || log.status === filterStatus;
+    return matchesSearch && matchesModel && matchesStatus;
+  });
+
+  const formatTime = (ms: number) => {
+    if (ms < 1000) return `${ms}ms`;
+    if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
+    return `${(ms / 60000).toFixed(1)}min`;
+  };
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">{user?.role === 'employee' ? '使用日志' : '调用日志'}</h1>
+          <p className="text-slate-500 mt-1">{user?.role === 'employee' ? '查看个人模型调用记录' : '查看全量模型调用记录'}</p>
+        </div>
+        <button
+          onClick={handleExport}
+          className="flex items-center gap-2 px-4 py-2 bg-white border border-slate-200 text-slate-600 font-medium rounded-lg hover:bg-slate-50 transition-all"
+        >
+          <Download className="w-5 h-5" />
+          导出日志
+        </button>
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 p-4">
+        <div className="flex items-center gap-4">
+          <div className="relative flex-1">
+            <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
+            <input
+              type="text"
+              value={searchTerm}
+              onChange={(e) => setSearchTerm(e.target.value)}
+              placeholder="搜索用户名或模型名称..."
+              className="w-full pl-10 pr-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+            />
+          </div>
+          <div className="flex items-center gap-2">
+            <Filter className="w-4 h-4 text-slate-400" />
+            <select
+              value={filterModel}
+              onChange={(e) => setFilterModel(e.target.value)}
+              className="px-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+            >
+              <option value="">全部模型</option>
+              {uniqueModels.map(model => (
+                <option key={model} value={model}>{model}</option>
+              ))}
+            </select>
+            <select
+              value={filterStatus}
+              onChange={(e) => setFilterStatus(e.target.value as typeof filterStatus)}
+              className="px-4 py-2 bg-slate-50 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+            >
+              <option value="all">全部状态</option>
+              <option value="success">成功</option>
+              <option value="failed">失败</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[800px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">时间</th>
+                <th className="w-28 px-6 py-4 text-left text-sm font-semibold text-slate-600">用户</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">模型</th>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">Token消耗</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">响应时间</th>
+                <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">状态</th>
+                <th className="w-48 px-6 py-4 text-left text-sm font-semibold text-slate-600">错误信息</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {filteredLogs.map((log) => (
+              <tr key={log.id} className="hover:bg-slate-50 transition-colors">
+                <td className="px-6 py-4">
+                  <div className="flex items-center gap-2">
+                    <Clock className="w-4 h-4 text-slate-400" />
+                    <span className="text-sm text-slate-600">{log.createdAt}</span>
+                  </div>
+                </td>
+                <td className="px-6 py-4 text-slate-700 font-medium">{log.userName}</td>
+                <td className="px-6 py-4 text-slate-600">{log.modelName}</td>
+                <td className="px-6 py-4">
+                  <div className="flex flex-col gap-1">
+                    <span className="text-sm text-slate-700">输入: {log.inputTokens}</span>
+                    <span className="text-sm text-slate-700">输出: {log.outputTokens}</span>
+                    <span className="text-xs text-slate-500">总计: {log.totalTokens}</span>
+                  </div>
+                </td>
+                <td className="px-6 py-4 text-slate-600">{formatTime(log.responseTime)}</td>
+                <td className="px-6 py-4">
+                  <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${
+                    log.status === 'success' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'
+                  }`}>
+                    {log.status === 'success' ? (
+                      <><CheckCircle className="w-3 h-3" /> 成功</>
+                    ) : (
+                      <><XCircle className="w-3 h-3" /> 失败</>
+                    )}
+                  </span>
+                </td>
+                <td className="px-6 py-4">
+                  <span className="text-sm text-slate-500 max-w-xs truncate">{log.errorMessage || '-'}</span>
+                </td>
+              </tr>
+            ))}
+          </tbody>
+          </table>
+        </div>
+
+        {filteredLogs.length === 0 && (
+          <div className="py-12 text-center">
+            <p className="text-slate-500">暂无调用记录</p>
+          </div>
+        )}
+      </div>
+    </div>
+  );
+}

+ 29 - 0
src/pages/Cases.tsx

@@ -0,0 +1,29 @@
+import { HeroCarousel } from '@/components/layout/HeroCarousel';
+import { CaseStudies } from '@/components/home/CaseStudies';
+import { CaseDetail } from '@/components/cases/CaseDetail';
+import { FloatingConsult } from '@/components/layout/FloatingConsult';
+import { caseStudies } from '@/data/mockData';
+
+export function Cases() {
+  return (
+    <main>
+      <HeroCarousel />
+      <CaseStudies />
+      {caseStudies.map((caseItem) => (
+        <CaseDetail
+          key={caseItem.id}
+          title={caseItem.title}
+          year={caseItem.year}
+          client="灵慧云智"
+          industry="电力/信通"
+          background={caseItem.background}
+          solution={caseItem.solution}
+          results={caseItem.results}
+          highlight={caseItem.highlight}
+          icon={caseItem.icon as 'Zap' | 'Database'}
+        />
+      ))}
+      <FloatingConsult />
+    </main>
+  );
+}

+ 360 - 0
src/pages/Channels.tsx

@@ -0,0 +1,360 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { useAuthStore } from '@/store/authStore';
+import { Plus, Edit, Trash2, CheckCircle, XCircle, AlertCircle, Eye, EyeOff } from 'lucide-react';
+import type { Channel, ChannelStatus } from '@/types';
+
+export function Channels() {
+  const [channels, setChannels] = useState<Channel[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [showModal, setShowModal] = useState(false);
+  const [editingChannel, setEditingChannel] = useState<Channel | null>(null);
+  const [form, setForm] = useState({
+    name: '',
+    provider: 'deepseek' as Channel['provider'],
+    apiKeys: [''],
+    weight: 100,
+    priority: 1,
+    status: 'enabled' as ChannelStatus,
+    baseUrl: '',
+  });
+  const { user } = useAuthStore();
+
+  useEffect(() => {
+    fetchChannels();
+  }, []);
+
+  const fetchChannels = async () => {
+    setLoading(true);
+    try {
+      const data = await api.channels.list();
+      setChannels(data);
+    } catch (err) {
+      console.error('Failed to fetch channels:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleOpenModal = (channel?: Channel) => {
+    if (channel) {
+      setEditingChannel(channel);
+      setForm({
+        name: channel.name,
+        provider: channel.provider,
+        apiKeys: [...channel.apiKeys],
+        weight: channel.weight,
+        priority: channel.priority,
+        status: channel.status,
+        baseUrl: channel.baseUrl,
+      });
+    } else {
+      setEditingChannel(null);
+      setForm({
+        name: '',
+        provider: 'deepseek',
+        apiKeys: [''],
+        weight: 100,
+        priority: channels.length + 1,
+        status: 'enabled',
+        baseUrl: '',
+      });
+    }
+    setShowModal(true);
+  };
+
+  const handleCloseModal = () => {
+    setShowModal(false);
+    setEditingChannel(null);
+  };
+
+  const handleAddApiKey = () => {
+    setForm(prev => ({ ...prev, apiKeys: [...prev.apiKeys, ''] }));
+  };
+
+  const handleRemoveApiKey = (index: number) => {
+    setForm(prev => ({ ...prev, apiKeys: prev.apiKeys.filter((_, i) => i !== index) }));
+  };
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    try {
+      if (editingChannel) {
+        await api.channels.update(editingChannel.id, form);
+      } else {
+        await api.channels.create(form);
+      }
+      handleCloseModal();
+      fetchChannels();
+    } catch (err) {
+      console.error('Failed to save channel:', err);
+    }
+  };
+
+  const handleDelete = async (id: string) => {
+    if (confirm('确定要删除该渠道吗?')) {
+      try {
+        await api.channels.delete(id);
+        fetchChannels();
+      } catch (err) {
+        console.error('Failed to delete channel:', err);
+      }
+    }
+  };
+
+  const statusConfig: Record<ChannelStatus, { label: string; color: string; icon: typeof CheckCircle }> = {
+    enabled: { label: '启用', color: 'bg-green-100 text-green-700', icon: CheckCircle },
+    disabled: { label: '禁用', color: 'bg-slate-100 text-slate-600', icon: XCircle },
+    error: { label: '异常', color: 'bg-red-100 text-red-700', icon: AlertCircle },
+  };
+
+  const providerLabels: Record<Channel['provider'], string> = {
+    deepseek: 'DeepSeek',
+    tongyi: '通义千问',
+    zhipu: '智谱AI',
+    wenxin: '文心一言',
+    custom: '自定义',
+  };
+
+  const canEdit = user?.role === 'super_admin';
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">渠道管理</h1>
+          <p className="text-slate-500 mt-1">管理全平台大模型调用通道</p>
+        </div>
+        {canEdit && (
+          <button
+            onClick={() => handleOpenModal()}
+            className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+          >
+            <Plus className="w-5 h-5" />
+            新增渠道
+          </button>
+        )}
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[800px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">名称</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">厂商</th>
+                <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">API密钥数量</th>
+                <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">权重</th>
+                <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">优先级</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">状态</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {channels.map((channel) => {
+              const status = statusConfig[channel.status];
+              const StatusIcon = status.icon;
+              return (
+                <tr key={channel.id} className="hover:bg-slate-50 transition-colors">
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-3">
+                      <div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
+                        channel.provider === 'deepseek' ? 'bg-blue-100 text-blue-600' :
+                        channel.provider === 'tongyi' ? 'bg-orange-100 text-orange-600' :
+                        channel.provider === 'zhipu' ? 'bg-green-100 text-green-600' :
+                        channel.provider === 'wenxin' ? 'bg-red-100 text-red-600' :
+                        'bg-purple-100 text-purple-600'
+                      }`}>
+                        {providerLabels[channel.provider][0]}
+                      </div>
+                      <div>
+                        <p className="font-medium text-slate-800">{channel.name}</p>
+                        <p className="text-xs text-slate-500">{channel.baseUrl}</p>
+                      </div>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4 text-slate-600">{providerLabels[channel.provider]}</td>
+                  <td className="px-6 py-4 text-slate-600">{channel.apiKeys.length} 个</td>
+                  <td className="px-6 py-4 text-slate-600">{channel.weight}</td>
+                  <td className="px-6 py-4 text-slate-600">{channel.priority}</td>
+                  <td className="px-6 py-4">
+                    <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${status.color}`}>
+                      <StatusIcon className="w-3 h-3" />
+                      {status.label}
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-2">
+                      {canEdit && (
+                        <>
+                          <button
+                            onClick={() => handleOpenModal(channel)}
+                            className="p-2 text-slate-500 hover:text-cyan-600 hover:bg-cyan-50 rounded-lg transition-colors"
+                          >
+                            <Edit className="w-4 h-4" />
+                          </button>
+                          <button
+                            onClick={() => handleDelete(channel.id)}
+                            className="p-2 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
+                          >
+                            <Trash2 className="w-4 h-4" />
+                          </button>
+                        </>
+                      )}
+                      {!canEdit && (
+                        <button className="p-2 text-slate-500 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors">
+                          <Eye className="w-4 h-4" />
+                        </button>
+                      )}
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+          </table>
+        </div>
+      </div>
+
+      {showModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg max-h-[90vh] overflow-y-auto">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingChannel ? '编辑渠道' : '新增渠道'}</h2>
+              <button onClick={handleCloseModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleSubmit} className="p-6 space-y-5">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">渠道名称</label>
+                <input
+                  type="text"
+                  value={form.name}
+                  onChange={(e) => setForm({ ...form, name: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">厂商</label>
+                <select
+                  value={form.provider}
+                  onChange={(e) => setForm({ ...form, provider: e.target.value as Channel['provider'] })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  {Object.entries(providerLabels).map(([value, label]) => (
+                    <option key={value} value={value}>{label}</option>
+                  ))}
+                </select>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">API密钥</label>
+                <div className="space-y-2">
+                  {form.apiKeys.map((key, index) => (
+                    <div key={index} className="flex items-center gap-2">
+                      <input
+                        type="text"
+                        value={key}
+                        onChange={(e) => {
+                          const newKeys = [...form.apiKeys];
+                          newKeys[index] = e.target.value;
+                          setForm({ ...form, apiKeys: newKeys });
+                        }}
+                        className="flex-1 px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500 font-mono text-sm"
+                        placeholder="sk-xxxxxxxxxxxxxxxx"
+                      />
+                      {form.apiKeys.length > 1 && (
+                        <button type="button" onClick={() => handleRemoveApiKey(index)} className="p-2.5 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg">
+                          <XCircle className="w-4 h-4" />
+                        </button>
+                      )}
+                    </div>
+                  ))}
+                </div>
+                <button type="button" onClick={handleAddApiKey} className="mt-2 flex items-center gap-1 text-sm text-cyan-600 hover:text-cyan-700">
+                  <Plus className="w-4 h-4" />
+                  添加密钥
+                </button>
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">权重</label>
+                  <input
+                    type="number"
+                    value={form.weight}
+                    onChange={(e) => setForm({ ...form, weight: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                    min="0"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">优先级</label>
+                  <input
+                    type="number"
+                    value={form.priority}
+                    onChange={(e) => setForm({ ...form, priority: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                    min="0"
+                  />
+                </div>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">基础URL</label>
+                <input
+                  type="text"
+                  value={form.baseUrl}
+                  onChange={(e) => setForm({ ...form, baseUrl: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  placeholder="https://api.example.com"
+                />
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">状态</label>
+                <select
+                  value={form.status}
+                  onChange={(e) => setForm({ ...form, status: e.target.value as ChannelStatus })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="enabled">启用</option>
+                  <option value="disabled">禁用</option>
+                  <option value="error">异常</option>
+                </select>
+              </div>
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingChannel ? '保存修改' : '创建渠道'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}

+ 250 - 0
src/pages/CostStats.tsx

@@ -0,0 +1,250 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { Download, Calendar, TrendingUp, TrendingDown } from 'lucide-react';
+import type { CostStats as CostStatsType } from '@/types';
+
+export function CostStats() {
+  const [stats, setStats] = useState<CostStatsType[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [startDate, setStartDate] = useState('');
+  const [endDate, setEndDate] = useState('');
+  const [filterType, setFilterType] = useState<'model' | 'enterprise' | 'user'>('model');
+
+  useEffect(() => {
+    fetchStats();
+  }, []);
+
+  const fetchStats = async () => {
+    setLoading(true);
+    try {
+      const data = await api.stats.costs();
+      setStats(data);
+    } catch (err) {
+      console.error('Failed to fetch cost stats:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleExport = () => {
+    const headers = ['日期', '费用(元)'];
+    const csvContent = [
+      headers.join(','),
+      ...stats.map(stat => [stat.date, stat.amount].join(','))
+    ].join('\n');
+
+    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+    const link = document.createElement('a');
+    link.href = URL.createObjectURL(blob);
+    link.download = `cost-stats-${new Date().toISOString().split('T')[0]}.csv`;
+    link.click();
+  };
+
+  const totalCost = stats.reduce((sum, stat) => sum + stat.amount, 0);
+  const avgDailyCost = totalCost / stats.length;
+  const lastWeekCost = stats.slice(-7).reduce((sum, stat) => sum + stat.amount, 0);
+  const prevWeekCost = stats.slice(-14, -7).reduce((sum, stat) => sum + stat.amount, 0);
+  const weekChange = prevWeekCost > 0 ? ((lastWeekCost - prevWeekCost) / prevWeekCost * 100).toFixed(1) : '0';
+
+  const mockModelCosts = [
+    { name: 'deepseek-chat', cost: 4250, percentage: 22.7 },
+    { name: 'glm-4', cost: 3890, percentage: 20.8 },
+    { name: 'qwen-2', cost: 3120, percentage: 16.7 },
+    { name: 'deepseek-r1', cost: 2480, percentage: 13.2 },
+    { name: 'glm-4-flash', cost: 1890, percentage: 10.1 },
+    { name: 'qwen-2-plus', cost: 1560, percentage: 8.3 },
+    { name: '其他', cost: 1460, percentage: 7.8 },
+  ];
+
+  const mockEnterpriseCosts = [
+    { name: '哈工大计算学部', cost: 8250, percentage: 44.0 },
+    { name: '灵慧云智科技', cost: 6890, percentage: 37.0 },
+    { name: '湖北电力公司', cost: 3610, percentage: 19.0 },
+  ];
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">费用统计</h1>
+          <p className="text-slate-500 mt-1">按模型、用户、企业、时间区间进行费用汇总</p>
+        </div>
+        <div className="flex items-center gap-3">
+          <div className="flex items-center gap-2">
+            <Calendar className="w-4 h-4 text-slate-400" />
+            <input
+              type="date"
+              value={startDate}
+              onChange={(e) => setStartDate(e.target.value)}
+              className="px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+            />
+            <span className="text-slate-400">至</span>
+            <input
+              type="date"
+              value={endDate}
+              onChange={(e) => setEndDate(e.target.value)}
+              className="px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+            />
+          </div>
+          <button
+            onClick={handleExport}
+            className="flex items-center gap-2 px-4 py-2 bg-white border border-slate-200 text-slate-600 font-medium rounded-lg hover:bg-slate-50 transition-all"
+          >
+            <Download className="w-5 h-5" />
+            导出账单
+          </button>
+        </div>
+      </div>
+
+      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">累计费用</p>
+              <p className="text-2xl font-bold text-slate-800 mt-1">¥{totalCost.toLocaleString()}</p>
+            </div>
+            <div className="w-10 h-10 rounded-lg bg-gradient-to-br from-cyan-400 to-blue-600 flex items-center justify-center">
+              <span className="text-white font-bold">¥</span>
+            </div>
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">日均费用</p>
+              <p className="text-2xl font-bold text-slate-800 mt-1">¥{avgDailyCost.toFixed(0)}</p>
+            </div>
+            <div className="w-10 h-10 rounded-lg bg-green-100 text-green-600 flex items-center justify-center">
+              <span className="text-lg">D</span>
+            </div>
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">本周费用</p>
+              <p className="text-2xl font-bold text-slate-800 mt-1">¥{lastWeekCost.toFixed(0)}</p>
+            </div>
+            <div className="w-10 h-10 rounded-lg bg-purple-100 text-purple-600 flex items-center justify-center">
+              <span className="text-lg">W</span>
+            </div>
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">周环比</p>
+              <p className={`text-2xl font-bold mt-1 ${parseFloat(weekChange) >= 0 ? 'text-red-600' : 'text-green-600'}`}>
+                {parseFloat(weekChange) >= 0 ? '+' : ''}{weekChange}%
+              </p>
+            </div>
+            <div className={`w-10 h-10 rounded-lg flex items-center justify-center ${parseFloat(weekChange) >= 0 ? 'bg-red-100 text-red-600' : 'bg-green-100 text-green-600'}`}>
+              {parseFloat(weekChange) >= 0 ? <TrendingUp className="w-5 h-5" /> : <TrendingDown className="w-5 h-5" />}
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
+        <div className="bg-white rounded-xl border border-slate-100 p-6">
+          <h2 className="text-lg font-semibold text-slate-800 mb-4">费用趋势</h2>
+          <div className="h-64 flex items-end gap-4">
+            {stats.map((stat, index) => {
+              const maxAmount = Math.max(...stats.map(s => s.amount));
+              const height = (stat.amount / maxAmount) * 100;
+              return (
+                <div key={index} className="flex-1 flex flex-col items-center gap-2">
+                  <div className="w-full bg-gradient-to-t from-purple-500 to-indigo-500 rounded-t-lg transition-all hover:from-purple-600 hover:to-indigo-600" style={{ height: `${height}%`, minHeight: '16px' }}></div>
+                  <span className="text-xs text-slate-500">{stat.date.slice(5)}</span>
+                  <span className="text-xs text-purple-600">¥{stat.amount}</span>
+                </div>
+              );
+            })}
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl border border-slate-100 p-6">
+          <div className="flex items-center justify-between mb-4">
+            <h2 className="text-lg font-semibold text-slate-800">费用分布</h2>
+            <select
+              value={filterType}
+              onChange={(e) => setFilterType(e.target.value as typeof filterType)}
+              className="px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500 text-sm"
+            >
+              <option value="model">按模型</option>
+              <option value="enterprise">按企业</option>
+              <option value="user">按用户</option>
+            </select>
+          </div>
+          <div className="space-y-3">
+            {(filterType === 'model' ? mockModelCosts : mockEnterpriseCosts).map((item, index) => (
+              <div key={index}>
+                <div className="flex items-center justify-between mb-1">
+                  <span className="text-sm text-slate-700">{item.name}</span>
+                  <span className="text-sm text-slate-600">¥{item.cost.toLocaleString()} ({item.percentage}%)</span>
+                </div>
+                <div className="h-2 bg-slate-100 rounded-full overflow-hidden">
+                  <div 
+                    className={`h-full rounded-full transition-all ${
+                      index === 0 ? 'bg-gradient-to-r from-cyan-400 to-blue-500' :
+                      index === 1 ? 'bg-gradient-to-r from-purple-400 to-indigo-500' :
+                      index === 2 ? 'bg-gradient-to-r from-orange-400 to-red-500' :
+                      'bg-gradient-to-r from-slate-400 to-slate-500'
+                    }`}
+                    style={{ width: `${item.percentage}%` }}
+                  ></div>
+                </div>
+              </div>
+            ))}
+          </div>
+        </div>
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[500px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">日期</th>
+                <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">费用(元)</th>
+                <th className="w-28 px-6 py-4 text-left text-sm font-semibold text-slate-600">环比</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {stats.map((stat, index) => {
+              const prevAmount = index > 0 ? stats[index - 1].amount : stat.amount;
+              const change = prevAmount > 0 ? ((stat.amount - prevAmount) / prevAmount * 100).toFixed(1) : '0';
+              return (
+                <tr key={stat.date} className="hover:bg-slate-50 transition-colors">
+                  <td className="px-6 py-4 font-medium text-slate-800">{stat.date}</td>
+                  <td className="px-6 py-4 text-slate-600">¥{stat.amount.toLocaleString()}</td>
+                  <td className="px-6 py-4">
+                    <span className={`text-sm ${parseFloat(change) >= 0 ? 'text-red-600' : 'text-green-600'}`}>
+                      {parseFloat(change) >= 0 ? '+' : ''}{change}%
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <button className="text-sm text-cyan-600 hover:text-cyan-700">查看详情</button>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 174 - 0
src/pages/Dashboard.tsx

@@ -0,0 +1,174 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { useAuthStore } from '@/store/authStore';
+import { TrendingUp, Users, Zap, AlertTriangle, ArrowUpRight, ArrowDownRight } from 'lucide-react';
+import type { DashboardData } from '@/types';
+
+export function Dashboard() {
+  const [data, setData] = useState<DashboardData | null>(null);
+  const [loading, setLoading] = useState(true);
+  const { user } = useAuthStore();
+
+  useEffect(() => {
+    const fetchData = async () => {
+      setLoading(true);
+      try {
+        const dashboardData = await api.stats.dashboard();
+        setData(dashboardData);
+      } catch (err) {
+        console.error('Failed to fetch dashboard data:', err);
+      } finally {
+        setLoading(false);
+      }
+    };
+    fetchData();
+  }, []);
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  if (!data) return null;
+
+  const stats = [
+    { label: '总调用量', value: data.totalCalls.toLocaleString(), icon: Zap, trend: '+12.5%', trendUp: true },
+    { label: 'Token消耗', value: (data.totalTokens / 1000000).toFixed(2) + 'M', icon: TrendingUp, trend: '+8.3%', trendUp: true },
+    { label: '费用总计', value: '¥' + data.totalCost.toLocaleString(), icon: Users, trend: '-2.1%', trendUp: false },
+    { label: '活跃用户', value: data.activeUsers.toString(), icon: Users, trend: '+5.7%', trendUp: true },
+  ];
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">数据看板</h1>
+          <p className="text-slate-500 mt-1">{user?.role === 'super_admin' ? '平台全局数据' : user?.role === 'platform_operator' ? '平台运营数据' : user?.role === 'enterprise_admin' ? '企业数据' : '个人数据'}</p>
+        </div>
+        <div className="flex items-center gap-2">
+          <span className="px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm">实时更新</span>
+        </div>
+      </div>
+
+      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
+        {stats.map((stat, index) => (
+          <div key={index} className="bg-white rounded-xl p-5 border border-slate-100 hover:shadow-lg transition-shadow">
+            <div className="flex items-center justify-between">
+              <div>
+                <p className="text-slate-500 text-sm">{stat.label}</p>
+                <p className="text-2xl font-bold text-slate-800 mt-1">{stat.value}</p>
+              </div>
+              <div className={`w-10 h-10 rounded-lg flex items-center justify-center ${stat.trendUp ? 'bg-green-100' : 'bg-red-100'}`}>
+                <stat.icon className={`w-5 h-5 ${stat.trendUp ? 'text-green-600' : 'text-red-600'}`} />
+              </div>
+            </div>
+            <div className={`flex items-center gap-1 mt-3 text-sm ${stat.trendUp ? 'text-green-600' : 'text-red-600'}`}>
+              {stat.trendUp ? <ArrowUpRight className="w-4 h-4" /> : <ArrowDownRight className="w-4 h-4" />}
+              <span>{stat.trend} 较上周</span>
+            </div>
+          </div>
+        ))}
+      </div>
+
+      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
+        <div className="lg:col-span-2 bg-white rounded-xl p-5 border border-slate-100">
+          <h2 className="text-lg font-semibold text-slate-800 mb-4">Token消耗趋势</h2>
+          <div className="h-64 flex items-end gap-4">
+            {data.tokenTrend.map((item, index) => {
+              const maxTokens = Math.max(...data.tokenTrend.map(t => t.totalTokens));
+              const height = (item.totalTokens / maxTokens) * 100;
+              return (
+                <div key={index} className="flex-1 flex flex-col items-center gap-2">
+                  <div className="w-full bg-gradient-to-t from-cyan-500 to-blue-500 rounded-t-lg transition-all hover:from-cyan-600 hover:to-blue-600" style={{ height: `${height}%`, minHeight: '20px' }}></div>
+                  <span className="text-xs text-slate-500">{item.date.slice(5)}</span>
+                </div>
+              );
+            })}
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between mb-4">
+            <h2 className="text-lg font-semibold text-slate-800">热门模型</h2>
+            <span className="text-xs text-slate-500">调用次数</span>
+          </div>
+          <div className="space-y-3">
+            {data.hotModels.map((model, index) => {
+              const maxCount = Math.max(...data.hotModels.map(m => m.count));
+              const width = (model.count / maxCount) * 100;
+              return (
+                <div key={index} className="flex items-center gap-3">
+                  <span className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium ${
+                    index === 0 ? 'bg-yellow-100 text-yellow-700' :
+                    index === 1 ? 'bg-slate-100 text-slate-600' :
+                    index === 2 ? 'bg-orange-100 text-orange-700' :
+                    'bg-slate-50 text-slate-500'
+                  }`}>
+                    {index + 1}
+                  </span>
+                  <div className="flex-1">
+                    <div className="flex items-center justify-between mb-1">
+                      <span className="text-sm text-slate-700">{model.name}</span>
+                      <span className="text-sm text-slate-500">{model.count.toLocaleString()}</span>
+                    </div>
+                    <div className="h-1.5 bg-slate-100 rounded-full overflow-hidden">
+                      <div className="h-full bg-gradient-to-r from-cyan-400 to-blue-500 rounded-full transition-all" style={{ width: `${width}%` }}></div>
+                    </div>
+                  </div>
+                </div>
+              );
+            })}
+          </div>
+        </div>
+      </div>
+
+      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <h2 className="text-lg font-semibold text-slate-800 mb-4">费用趋势</h2>
+          <div className="h-48 flex items-end gap-4">
+            {data.costTrend.map((item, index) => {
+              const maxAmount = Math.max(...data.costTrend.map(c => c.amount));
+              const height = (item.amount / maxAmount) * 100;
+              return (
+                <div key={index} className="flex-1 flex flex-col items-center gap-2">
+                  <div className="w-full bg-gradient-to-t from-purple-500 to-indigo-500 rounded-t-lg transition-all" style={{ height: `${height}%`, minHeight: '16px' }}></div>
+                  <span className="text-xs text-slate-500">{item.date.slice(5)}</span>
+                </div>
+              );
+            })}
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center gap-2 mb-4">
+            <AlertTriangle className="w-5 h-5 text-orange-500" />
+            <h2 className="text-lg font-semibold text-slate-800">异常调用告警</h2>
+          </div>
+          <div className="grid grid-cols-2 gap-4">
+            <div className="bg-orange-50 rounded-lg p-4 text-center">
+              <p className="text-3xl font-bold text-orange-600">{data.abnormalCalls}</p>
+              <p className="text-sm text-orange-500 mt-1">今日异常</p>
+            </div>
+            <div className="bg-red-50 rounded-lg p-4 text-center">
+              <p className="text-3xl font-bold text-red-600">5</p>
+              <p className="text-sm text-red-500 mt-1">待处理</p>
+            </div>
+          </div>
+          <div className="mt-4 space-y-2">
+            <div className="flex items-center justify-between text-sm">
+              <span className="text-slate-600">qwen-2</span>
+              <span className="text-red-500">超时 3次</span>
+            </div>
+            <div className="flex items-center justify-between text-sm">
+              <span className="text-slate-600">ernie-4</span>
+              <span className="text-red-500">服务不可用 2次</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 281 - 0
src/pages/Departments.tsx

@@ -0,0 +1,281 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { useAuthStore } from '@/store/authStore';
+import { Plus, Edit, Trash2, XCircle, Folder, Building2 } from 'lucide-react';
+import type { Department } from '@/types';
+
+export function Departments() {
+  const [departments, setDepartments] = useState<Department[]>([]);
+  const [enterprises, setEnterprises] = useState<{ id: string; name: string }[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [showModal, setShowModal] = useState(false);
+  const [editingDepartment, setEditingDepartment] = useState<Department | null>(null);
+  const [form, setForm] = useState({
+    name: '',
+    enterpriseId: '',
+    parentId: '',
+  });
+  const { user } = useAuthStore();
+
+  useEffect(() => {
+    fetchData();
+  }, []);
+
+  const fetchData = async () => {
+    setLoading(true);
+    try {
+      const [departmentsData, enterprisesData] = await Promise.all([
+        api.departments.list(user?.role === 'enterprise_admin' ? user?.enterpriseId : undefined),
+        api.enterprises.list(),
+      ]);
+      setDepartments(departmentsData);
+      setEnterprises(enterprisesData.map(e => ({ id: e.id, name: e.name })));
+    } catch (err) {
+      console.error('Failed to fetch data:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleOpenModal = (department?: Department) => {
+    if (department) {
+      setEditingDepartment(department);
+      setForm({
+        name: department.name,
+        enterpriseId: department.enterpriseId,
+        parentId: department.parentId || '',
+      });
+    } else {
+      setEditingDepartment(null);
+      setForm({
+        name: '',
+        enterpriseId: user?.role === 'enterprise_admin' ? (user?.enterpriseId || '') : (enterprises[0]?.id || ''),
+        parentId: '',
+      });
+    }
+    setShowModal(true);
+  };
+
+  const handleCloseModal = () => {
+    setShowModal(false);
+    setEditingDepartment(null);
+  };
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    try {
+      if (editingDepartment) {
+        await api.departments.update(editingDepartment.id, form);
+      } else {
+        await api.departments.create(form);
+      }
+      handleCloseModal();
+      fetchData();
+    } catch (err) {
+      console.error('Failed to save department:', err);
+    }
+  };
+
+  const handleDelete = async (id: string) => {
+    if (confirm('确定要删除该部门吗?')) {
+      try {
+        await api.departments.delete(id);
+        fetchData();
+      } catch (err) {
+        console.error('Failed to delete department:', err);
+      }
+    }
+  };
+
+  const getEnterpriseName = (id: string) => enterprises.find(e => e.id === id)?.name || '未知企业';
+  const getParentName = (id: string | null) => id ? departments.find(d => d.id === id)?.name : '无';
+
+  const canEdit = user?.role === 'super_admin' || user?.role === 'enterprise_admin';
+
+  const rootDepartments = departments.filter(d => !d.parentId);
+
+  const renderTree = (departments: Department[], parentId: string | null) => {
+    const children = departments.filter(d => d.parentId === parentId);
+    if (children.length === 0) return null;
+    
+    return (
+      <ul className="ml-4 mt-1 space-y-1">
+        {children.map((department) => (
+          <li key={department.id}>
+            <div className="flex items-center justify-between py-2 px-2 hover:bg-slate-50 rounded-lg">
+              <div className="flex items-center gap-2">
+                <Folder className="w-4 h-4 text-cyan-500" />
+                <span className="text-sm text-slate-700">{department.name}</span>
+                {canEdit && (
+                  <div className="flex items-center gap-1 ml-2">
+                    <button
+                      onClick={() => handleOpenModal(department)}
+                      className="p-1 text-slate-400 hover:text-cyan-600 hover:bg-cyan-50 rounded transition-colors"
+                    >
+                      <Edit className="w-3 h-3" />
+                    </button>
+                    <button
+                      onClick={() => handleDelete(department.id)}
+                      className="p-1 text-slate-400 hover:text-red-600 hover:bg-red-50 rounded transition-colors"
+                    >
+                      <Trash2 className="w-3 h-3" />
+                    </button>
+                  </div>
+                )}
+              </div>
+            </div>
+            {renderTree(departments, department.id)}
+          </li>
+        ))}
+      </ul>
+    );
+  };
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">部门管理</h1>
+          <p className="text-slate-500 mt-1">管理企业内部部门结构</p>
+        </div>
+        {canEdit && (
+          <button
+            onClick={() => handleOpenModal()}
+            className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+          >
+            <Plus className="w-5 h-5" />
+            新增部门
+          </button>
+        )}
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="p-6">
+          {enterprises.map((enterprise) => {
+            const enterpriseDepartments = departments.filter(d => d.enterpriseId === enterprise.id);
+            const roots = enterpriseDepartments.filter(d => !d.parentId);
+            return (
+              <div key={enterprise.id} className="mb-6">
+                <div className="flex items-center gap-2 mb-3">
+                  <Building2 className="w-5 h-5 text-blue-500" />
+                  <span className="font-semibold text-slate-800">{enterprise.name}</span>
+                </div>
+                <ul className="space-y-1">
+                  {roots.map((department) => (
+                    <li key={department.id}>
+                      <div className="flex items-center justify-between py-2 px-2 hover:bg-slate-50 rounded-lg">
+                        <div className="flex items-center gap-2">
+                          <Folder className="w-5 h-5 text-cyan-500" />
+                          <span className="font-medium text-slate-800">{department.name}</span>
+                          {canEdit && (
+                            <div className="flex items-center gap-1 ml-2">
+                              <button
+                                onClick={() => handleOpenModal(department)}
+                                className="p-1 text-slate-400 hover:text-cyan-600 hover:bg-cyan-50 rounded transition-colors"
+                              >
+                                <Edit className="w-4 h-4" />
+                              </button>
+                              <button
+                                onClick={() => handleDelete(department.id)}
+                                className="p-1 text-slate-400 hover:text-red-600 hover:bg-red-50 rounded transition-colors"
+                              >
+                                <Trash2 className="w-4 h-4" />
+                              </button>
+                            </div>
+                          )}
+                        </div>
+                      </div>
+                      {renderTree(enterpriseDepartments, department.id)}
+                    </li>
+                  ))}
+                </ul>
+              </div>
+            );
+          })}
+        </div>
+      </div>
+
+      {showModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingDepartment ? '编辑部门' : '新增部门'}</h2>
+              <button onClick={handleCloseModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleSubmit} className="p-6 space-y-4">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">部门名称</label>
+                <input
+                  type="text"
+                  value={form.name}
+                  onChange={(e) => setForm({ ...form, name: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              {user?.role !== 'enterprise_admin' && (
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">所属企业</label>
+                  <select
+                    value={form.enterpriseId}
+                    onChange={(e) => setForm({ ...form, enterpriseId: e.target.value, parentId: '' })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  >
+                    {enterprises.map(enterprise => (
+                      <option key={enterprise.id} value={enterprise.id}>{enterprise.name}</option>
+                    ))}
+                  </select>
+                </div>
+              )}
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">上级部门</label>
+                <select
+                  value={form.parentId}
+                  onChange={(e) => setForm({ ...form, parentId: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="">无(顶级部门)</option>
+                  {departments
+                    .filter(d => d.enterpriseId === form.enterpriseId)
+                    .filter(d => !editingDepartment || d.id !== editingDepartment.id)
+                    .map(department => (
+                      <option key={department.id} value={department.id}>{department.name}</option>
+                    ))}
+                </select>
+              </div>
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingDepartment ? '保存修改' : '创建部门'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}

+ 605 - 0
src/pages/Enterprises.tsx

@@ -0,0 +1,605 @@
+import {useState, useEffect, Fragment} from 'react';
+import { api } from '@/api/mockApi';
+import { Plus, Edit, Trash2, CheckCircle, XCircle, AlertCircle, Building2, Users, Folder, ChevronRight } from 'lucide-react';
+import type { Enterprise, Department } from '@/types';
+import React from 'react';
+
+type EnterpriseStatus = Enterprise['status'];
+
+export function Enterprises() {
+  const [enterprises, setEnterprises] = useState<Enterprise[]>([]);
+  const [departments, setDepartments] = useState<Department[]>([]);
+  const [selectedEnterprise, setSelectedEnterprise] = useState<Enterprise | null>(null);
+  const [activeTab, setActiveTab] = useState<'enterprises' | 'departments'>('enterprises');
+  const [loading, setLoading] = useState(true);
+  const [showModal, setShowModal] = useState(false);
+  const [showDeptModal, setShowDeptModal] = useState(false);
+  const [editingEnterprise, setEditingEnterprise] = useState<Enterprise | null>(null);
+  const [editingDepartment, setEditingDepartment] = useState<Department | null>(null);
+  const [form, setForm] = useState({
+    name: '',
+    contact: '',
+    phone: '',
+    email: '',
+    status: 'active' as EnterpriseStatus,
+  });
+  const [deptForm, setDeptForm] = useState({
+    name: '',
+    parentId: '',
+  });
+
+  useEffect(() => {
+    fetchEnterprises();
+  }, []);
+
+  useEffect(() => {
+    if (selectedEnterprise) {
+      fetchDepartments(selectedEnterprise.id);
+    }
+  }, [selectedEnterprise]);
+
+  const fetchEnterprises = async () => {
+    setLoading(true);
+    try {
+      const data = await api.enterprises.list();
+      setEnterprises(data);
+    } catch (err) {
+      console.error('Failed to fetch enterprises:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const fetchDepartments = async (enterpriseId: string) => {
+    try {
+      const data = await api.departments.list(enterpriseId);
+      setDepartments(data);
+    } catch (err) {
+      console.error('Failed to fetch departments:', err);
+    }
+  };
+
+  const handleOpenModal = (enterprise?: Enterprise) => {
+    if (enterprise) {
+      setEditingEnterprise(enterprise);
+      setForm({
+        name: enterprise.name,
+        contact: enterprise.contact,
+        phone: enterprise.phone,
+        email: enterprise.email,
+        status: enterprise.status,
+      });
+    } else {
+      setEditingEnterprise(null);
+      setForm({
+        name: '',
+        contact: '',
+        phone: '',
+        email: '',
+        status: 'active',
+      });
+    }
+    setShowModal(true);
+  };
+
+  const handleCloseModal = () => {
+    setShowModal(false);
+    setEditingEnterprise(null);
+  };
+
+  const handleOpenDeptModal = (department?: Department) => {
+    if (department) {
+      setEditingDepartment(department);
+      setDeptForm({
+        name: department.name,
+        parentId: department.parentId || '',
+      });
+    } else {
+      setEditingDepartment(null);
+      setDeptForm({
+        name: '',
+        parentId: '',
+      });
+    }
+    setShowDeptModal(true);
+  };
+
+  const handleCloseDeptModal = () => {
+    setShowDeptModal(false);
+    setEditingDepartment(null);
+  };
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    try {
+      if (editingEnterprise) {
+        await api.enterprises.update(editingEnterprise.id, form);
+      } else {
+        await api.enterprises.create(form);
+      }
+      handleCloseModal();
+      fetchEnterprises();
+    } catch (err) {
+      console.error('Failed to save enterprise:', err);
+    }
+  };
+
+  const handleDeptSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    if (!selectedEnterprise) return;
+    try {
+      if (editingDepartment) {
+        await api.departments.update(editingDepartment.id, deptForm);
+      } else {
+        await api.departments.create({
+          ...deptForm,
+          enterpriseId: selectedEnterprise.id,
+        });
+      }
+      handleCloseDeptModal();
+      fetchDepartments(selectedEnterprise.id);
+    } catch (err) {
+      console.error('Failed to save department:', err);
+    }
+  };
+
+  const handleDelete = async (id: string) => {
+    if (confirm('确定要注销该企业吗?')) {
+      try {
+        await api.enterprises.update(id, { status: 'deleted' });
+        fetchEnterprises();
+        if (selectedEnterprise?.id === id) {
+          setSelectedEnterprise(null);
+          setDepartments([]);
+        }
+      } catch (err) {
+        console.error('Failed to delete enterprise:', err);
+      }
+    }
+  };
+
+  const handleDeptDelete = async (id: string) => {
+    if (!selectedEnterprise) return;
+    if (confirm('确定要删除该部门吗?')) {
+      try {
+        await api.departments.delete(id);
+        fetchDepartments(selectedEnterprise.id);
+      } catch (err) {
+        console.error('Failed to delete department:', err);
+      }
+    }
+  };
+
+  const handleToggleStatus = async (id: string, currentStatus: EnterpriseStatus) => {
+    try {
+      const newStatus: EnterpriseStatus = currentStatus === 'active' ? 'frozen' : 'active';
+      await api.enterprises.update(id, { status: newStatus });
+      fetchEnterprises();
+    } catch (err) {
+      console.error('Failed to update status:', err);
+    }
+  };
+
+  const statusConfig: Record<EnterpriseStatus, { label: string; color: string; icon: typeof CheckCircle }> = {
+    active: { label: '正常', color: 'bg-green-100 text-green-700', icon: CheckCircle },
+    frozen: { label: '冻结', color: 'bg-orange-100 text-orange-700', icon: AlertCircle },
+    deleted: { label: '已注销', color: 'bg-slate-100 text-slate-600', icon: XCircle },
+  };
+
+  const getParentName = (parentId: string | null) => {
+    if (!parentId) return '顶级部门';
+    return departments.find(d => d.id === parentId)?.name || '-';
+  };
+
+  const buildTree = (items: Department[], parentId: string | null = null): Department[] => {
+    return items
+      .filter(item => item.parentId === parentId)
+      .map(item => ({
+        ...item,
+        children: buildTree(items, item.id),
+      }));
+  };
+
+  const renderTree = (nodes: Department[], level = 0) => {
+    return nodes.map((dept) => (
+      <Fragment key={dept.id}>
+        <tr className="hover:bg-slate-50 transition-colors">
+          <td className="px-6 py-4" style={{ paddingLeft: `${24 + level * 20}px` }}>
+            <div className="flex items-center gap-2">
+              {dept.children?.length > 0 ? (
+                <ChevronRight className="w-4 h-4 text-slate-400" />
+              ) : (
+                <span className="w-4" />
+              )}
+              <Folder className="w-4 h-4 text-cyan-500" />
+              <span className="font-medium text-slate-800">{dept.name}</span>
+            </div>
+          </td>
+          <td className="px-6 py-4 text-slate-600 text-sm">{getParentName(dept.parentId)}</td>
+          <td className="px-6 py-4 text-slate-600 text-sm">{dept.createdAt}</td>
+          <td className="px-6 py-4">
+            <div className="flex items-center gap-2">
+              <button
+                onClick={() => handleOpenDeptModal(dept)}
+                className="p-2 text-slate-500 hover:text-cyan-600 hover:bg-cyan-50 rounded-lg transition-colors"
+              >
+                <Edit className="w-4 h-4" />
+              </button>
+              <button
+                onClick={() => handleDeptDelete(dept.id)}
+                className="p-2 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
+              >
+                <Trash2 className="w-4 h-4" />
+              </button>
+            </div>
+          </td>
+        </tr>
+        {dept.children?.length > 0 && renderTree(dept.children, level + 1)}
+      </Fragment>
+    ));
+  };
+
+  const treeData = buildTree(departments);
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">企业管理</h1>
+          <p className="text-slate-500 mt-1">管理企业信息及部门信息</p>
+        </div>
+        {activeTab === 'enterprises' && (
+          <button
+            onClick={() => handleOpenModal()}
+            className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+          >
+            <Plus className="w-5 h-5" />
+            新增企业
+          </button>
+        )}
+        {activeTab === 'departments' && selectedEnterprise && (
+          <button
+            onClick={() => handleOpenDeptModal()}
+            className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+          >
+            <Plus className="w-5 h-5" />
+            新增部门
+          </button>
+        )}
+      </div>
+
+      <div className="flex gap-4 border-b border-slate-200">
+        <button
+          onClick={() => {
+            setActiveTab('enterprises');
+            setSelectedEnterprise(null);
+            setDepartments([]);
+          }}
+          className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
+            activeTab === 'enterprises'
+              ? 'border-cyan-500 text-cyan-600'
+              : 'border-transparent text-slate-500 hover:text-slate-700'
+          }`}
+        >
+          <span className="flex items-center gap-2">
+            <Building2 className="w-4 h-4" />
+            企业列表
+          </span>
+        </button>
+        <button
+          onClick={() => setActiveTab('departments')}
+          className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
+            activeTab === 'departments'
+              ? 'border-cyan-500 text-cyan-600'
+              : 'border-transparent text-slate-500 hover:text-slate-700'
+          }`}
+        >
+          <span className="flex items-center gap-2">
+            <Users className="w-4 h-4" />
+            部门管理
+          </span>
+        </button>
+      </div>
+
+      {activeTab === 'enterprises' && (
+        <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+          <div className="overflow-x-auto">
+            <table className="w-full min-w-[700px] table-fixed">
+              <thead className="bg-slate-50">
+                <tr>
+                  <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">企业名称</th>
+                  <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">联系人</th>
+                  <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">联系电话</th>
+                  <th className="w-48 px-6 py-4 text-left text-sm font-semibold text-slate-600">邮箱</th>
+                  <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">状态</th>
+                  <th className="w-48 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+                </tr>
+              </thead>
+              <tbody className="divide-y divide-slate-100">
+                {enterprises.map((enterprise) => {
+                  const status = statusConfig[enterprise.status];
+                  const StatusIcon = status.icon;
+                  return (
+                    <tr key={enterprise.id} className="hover:bg-slate-50 transition-colors">
+                      <td className="px-6 py-4">
+                        <div className="flex items-center gap-3">
+                          <div className="w-10 h-10 rounded-lg bg-blue-100 text-blue-600 flex items-center justify-center">
+                            <Building2 className="w-5 h-5" />
+                          </div>
+                          <div>
+                            <p className="font-medium text-slate-800">{enterprise.name}</p>
+                            <p className="text-xs text-slate-500">ID: {enterprise.id}</p>
+                          </div>
+                        </div>
+                      </td>
+                      <td className="px-6 py-4 text-slate-600">{enterprise.contact}</td>
+                      <td className="px-6 py-4 text-slate-600">{enterprise.phone}</td>
+                      <td className="px-6 py-4 text-slate-600">{enterprise.email}</td>
+                      <td className="px-6 py-4">
+                        <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${status.color}`}>
+                          <StatusIcon className="w-3 h-3" />
+                          {status.label}
+                        </span>
+                      </td>
+                      <td className="px-6 py-4">
+                        <div className="flex items-center gap-2">
+                          <button
+                            onClick={() => handleOpenModal(enterprise)}
+                            className="p-2 text-slate-500 hover:text-cyan-600 hover:bg-cyan-50 rounded-lg transition-colors"
+                          >
+                            <Edit className="w-4 h-4" />
+                          </button>
+                          <button
+                            onClick={() => {
+                              setSelectedEnterprise(enterprise);
+                              setActiveTab('departments');
+                            }}
+                            className="p-2 text-slate-500 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
+                          >
+                            <Users className="w-4 h-4" />
+                          </button>
+                          {enterprise.status !== 'deleted' && (
+                            <button
+                              onClick={() => handleToggleStatus(enterprise.id, enterprise.status)}
+                              className={`p-2 rounded-lg transition-colors ${
+                                enterprise.status === 'active' ? 'text-slate-500 hover:text-orange-600 hover:bg-orange-50' : 'text-slate-500 hover:text-green-600 hover:bg-green-50'
+                              }`}
+                            >
+                              {enterprise.status === 'active' ? <AlertCircle className="w-4 h-4" /> : <CheckCircle className="w-4 h-4" />}
+                            </button>
+                          )}
+                          <button
+                            onClick={() => handleDelete(enterprise.id)}
+                            className="p-2 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
+                          >
+                            <Trash2 className="w-4 h-4" />
+                          </button>
+                        </div>
+                      </td>
+                    </tr>
+                  );
+                })}
+              </tbody>
+            </table>
+          </div>
+        </div>
+      )}
+
+      {activeTab === 'departments' && (
+        <div className="space-y-4">
+          {!selectedEnterprise ? (
+            <div className="bg-white rounded-xl border border-slate-100 p-12 text-center">
+              <div className="w-16 h-16 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-4">
+                <Users className="w-8 h-8 text-slate-400" />
+              </div>
+              <h3 className="text-lg font-medium text-slate-700 mb-2">请选择企业</h3>
+              <p className="text-slate-500">从左侧企业列表中选择一个企业,即可管理其部门</p>
+              <button
+                onClick={() => setActiveTab('enterprises')}
+                className="mt-4 px-4 py-2 bg-cyan-500 text-white font-medium rounded-lg hover:bg-cyan-600 transition-colors"
+              >
+                选择企业
+              </button>
+            </div>
+          ) : (
+            <>
+              <div className="bg-cyan-50 border border-cyan-100 rounded-lg px-4 py-3">
+                <div className="flex items-center gap-2">
+                  <Building2 className="w-5 h-5 text-cyan-600" />
+                  <span className="font-medium text-cyan-700">当前企业:{selectedEnterprise.name}</span>
+                  <button
+                    onClick={() => {
+                      setSelectedEnterprise(null);
+                      setDepartments([]);
+                    }}
+                    className="ml-auto text-sm text-cyan-600 hover:text-cyan-700"
+                  >
+                    更换企业
+                  </button>
+                </div>
+              </div>
+
+              <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+                <div className="overflow-x-auto">
+                  <table className="w-full min-w-[700px] table-fixed">
+                    <thead className="bg-slate-50">
+                      <tr>
+                        <th className="w-64 px-6 py-4 text-left text-sm font-semibold text-slate-600">部门名称</th>
+                        <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">上级部门</th>
+                        <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">创建时间</th>
+                        <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+                      </tr>
+                    </thead>
+                    <tbody className="divide-y divide-slate-100">
+                      {treeData.length > 0 ? (
+                        renderTree(treeData)
+                      ) : (
+                        <tr>
+                          <td colSpan={4} className="px-6 py-12 text-center text-slate-500">
+                            <Folder className="w-12 h-12 text-slate-300 mx-auto mb-3" />
+                            <p>暂无部门数据</p>
+                          </td>
+                        </tr>
+                      )}
+                    </tbody>
+                  </table>
+                </div>
+              </div>
+            </>
+          )}
+        </div>
+      )}
+
+      {showModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingEnterprise ? '编辑企业' : '新增企业'}</h2>
+              <button onClick={handleCloseModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleSubmit} className="p-6 space-y-4">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">企业名称</label>
+                <input
+                  type="text"
+                  value={form.name}
+                  onChange={(e) => setForm({ ...form, name: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">联系人</label>
+                <input
+                  type="text"
+                  value={form.contact}
+                  onChange={(e) => setForm({ ...form, contact: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">联系电话</label>
+                  <input
+                    type="text"
+                    value={form.phone}
+                    onChange={(e) => setForm({ ...form, phone: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">邮箱</label>
+                  <input
+                    type="email"
+                    value={form.email}
+                    onChange={(e) => setForm({ ...form, email: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">状态</label>
+                <select
+                  value={form.status}
+                  onChange={(e) => setForm({ ...form, status: e.target.value as EnterpriseStatus })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="active">正常</option>
+                  <option value="frozen">冻结</option>
+                  <option value="deleted">已注销</option>
+                </select>
+              </div>
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingEnterprise ? '保存修改' : '创建企业'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+
+      {showDeptModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingDepartment ? '编辑部门' : '新增部门'}</h2>
+              <button onClick={handleCloseDeptModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleDeptSubmit} className="p-6 space-y-4">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">部门名称</label>
+                <input
+                  type="text"
+                  value={deptForm.name}
+                  onChange={(e) => setDeptForm({ ...deptForm, name: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">上级部门</label>
+                <select
+                  value={deptForm.parentId}
+                  onChange={(e) => setDeptForm({ ...deptForm, parentId: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="">顶级部门</option>
+                  {departments.filter(d => d.id !== editingDepartment?.id).map(dept => (
+                    <option key={dept.id} value={dept.id}>{dept.name}</option>
+                  ))}
+                </select>
+              </div>
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseDeptModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingDepartment ? '保存修改' : '创建部门'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}

+ 25 - 0
src/pages/Home.tsx

@@ -0,0 +1,25 @@
+import { HeroCarousel } from '@/components/layout/HeroCarousel';
+import { BrandEndorsement } from '@/components/home/BrandEndorsement';
+import { CoreCapabilities } from '@/components/home/CoreCapabilities';
+import { PainPointSolutions } from '@/components/home/PainPointSolutions';
+import { ApplicationScenarios } from '@/components/home/ApplicationScenarios';
+import { CaseStudies } from '@/components/home/CaseStudies';
+import { RndAdvantage } from '@/components/home/RndAdvantage';
+import { BusinessConversion } from '@/components/home/BusinessConversion';
+import { FloatingConsult } from '@/components/layout/FloatingConsult';
+
+export function Home() {
+  return (
+    <main>
+      <HeroCarousel />
+      <BrandEndorsement />
+      <CoreCapabilities />
+      <PainPointSolutions />
+      <ApplicationScenarios />
+      <CaseStudies />
+      <RndAdvantage />
+      <BusinessConversion />
+      <FloatingConsult />
+    </main>
+  );
+}

+ 105 - 0
src/pages/Login.tsx

@@ -0,0 +1,105 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useAuthStore } from '@/store/authStore';
+import { Sparkles, User, Lock, AlertCircle } from 'lucide-react';
+
+export function Login() {
+  const navigate = useNavigate();
+  const { login } = useAuthStore();
+  const [form, setForm] = useState({ username: '', password: '' });
+  const [error, setError] = useState('');
+  const [loading, setLoading] = useState(false);
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    setError('');
+    setLoading(true);
+
+    try {
+      await login(form);
+      navigate('/dashboard');
+    } catch (err) {
+      setError(err instanceof Error ? err.message : '登录失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center p-4">
+      <div className="w-full max-w-md">
+        <div className="text-center mb-8">
+          <div className="w-16 h-16 bg-gradient-to-br from-cyan-400 to-blue-600 rounded-2xl flex items-center justify-center mx-auto mb-4">
+            <Sparkles className="w-8 h-8 text-white" />
+          </div>
+          <h1 className="text-2xl font-bold text-white mb-2">Token运营平台</h1>
+          <p className="text-slate-400">企业级大模型API统一管理平台</p>
+        </div>
+
+        <div className="bg-slate-800/50 backdrop-blur-sm rounded-2xl p-6 border border-slate-700">
+          {error && (
+            <div className="flex items-center gap-2 bg-red-500/10 border border-red-500/30 rounded-lg px-4 py-3 mb-4">
+              <AlertCircle className="w-4 h-4 text-red-500" />
+              <span className="text-red-400 text-sm">{error}</span>
+            </div>
+          )}
+
+          <form onSubmit={handleSubmit} className="space-y-4">
+            <div>
+              <label className="block text-sm font-medium text-slate-300 mb-2">用户名</label>
+              <div className="relative">
+                <User className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-500" />
+                <input
+                  type="text"
+                  value={form.username}
+                  onChange={(e) => setForm({ ...form, username: e.target.value })}
+                  placeholder="请输入用户名"
+                  className="w-full pl-10 pr-4 py-3 bg-slate-700/50 border border-slate-600 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:border-transparent"
+                />
+              </div>
+            </div>
+
+            <div>
+              <label className="block text-sm font-medium text-slate-300 mb-2">密码</label>
+              <div className="relative">
+                <Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-500" />
+                <input
+                  type="password"
+                  value={form.password}
+                  onChange={(e) => setForm({ ...form, password: e.target.value })}
+                  placeholder="请输入密码"
+                  className="w-full pl-10 pr-4 py-3 bg-slate-700/50 border border-slate-600 rounded-lg text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500 focus:border-transparent"
+                />
+              </div>
+            </div>
+
+            <button
+              type="submit"
+              disabled={loading}
+              className="w-full py-3 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
+            >
+              {loading ? (
+                <>
+                  <div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin"></div>
+                  登录中...
+                </>
+              ) : (
+                '登录'
+              )}
+            </button>
+          </form>
+
+          <div className="mt-6 pt-4 border-t border-slate-700">
+            <p className="text-center text-slate-500 text-sm">
+              测试账号:
+              <span className="text-cyan-400 mx-2">super_admin/admin123</span>
+              <span className="text-cyan-400 mx-2">platform_operator/operator123</span>
+              <span className="text-cyan-400 mx-2">enterprise_admin/enterprise123</span>
+              <span className="text-cyan-400 mx-2">employee/employee123</span>
+            </p>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 376 - 0
src/pages/Models.tsx

@@ -0,0 +1,376 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { useAuthStore } from '@/store/authStore';
+import { Plus, Edit, Trash2, CheckCircle, XCircle, Eye, EyeOff } from 'lucide-react';
+import type { Model, ModelStatus } from '@/types';
+
+export function Models() {
+  const [models, setModels] = useState<Model[]>([]);
+  const [channels, setChannels] = useState<{ id: string; name: string }[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [showModal, setShowModal] = useState(false);
+  const [editingModel, setEditingModel] = useState<Model | null>(null);
+  const [form, setForm] = useState({
+    name: '',
+    provider: '',
+    type: 'chat',
+    version: 'v1',
+    contextWindow: 128000,
+    inputLimit: 64000,
+    outputLimit: 64000,
+    inputPrice: 0.0015,
+    outputPrice: 0.0015,
+    status: 'enabled' as ModelStatus,
+    channelId: '',
+  });
+  const { user } = useAuthStore();
+
+  useEffect(() => {
+    fetchData();
+  }, []);
+
+  const fetchData = async () => {
+    setLoading(true);
+    try {
+      const [modelsData, channelsData] = await Promise.all([
+        api.models.list(),
+        api.channels.list(),
+      ]);
+      setModels(modelsData);
+      setChannels(channelsData.map(c => ({ id: c.id, name: c.name })));
+    } catch (err) {
+      console.error('Failed to fetch data:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleOpenModal = (model?: Model) => {
+    if (model) {
+      setEditingModel(model);
+      setForm({
+        name: model.name,
+        provider: model.provider,
+        type: model.type,
+        version: model.version,
+        contextWindow: model.contextWindow,
+        inputLimit: model.inputLimit,
+        outputLimit: model.outputLimit,
+        inputPrice: model.inputPrice,
+        outputPrice: model.outputPrice,
+        status: model.status,
+        channelId: model.channelId,
+      });
+    } else {
+      setEditingModel(null);
+      setForm({
+        name: '',
+        provider: '',
+        type: 'chat',
+        version: 'v1',
+        contextWindow: 128000,
+        inputLimit: 64000,
+        outputLimit: 64000,
+        inputPrice: 0.0015,
+        outputPrice: 0.0015,
+        status: 'enabled',
+        channelId: channels[0]?.id || '',
+      });
+    }
+    setShowModal(true);
+  };
+
+  const handleCloseModal = () => {
+    setShowModal(false);
+    setEditingModel(null);
+  };
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    try {
+      if (editingModel) {
+        await api.models.update(editingModel.id, form);
+      } else {
+        await api.models.create(form);
+      }
+      handleCloseModal();
+      fetchData();
+    } catch (err) {
+      console.error('Failed to save model:', err);
+    }
+  };
+
+  const handleDelete = async (id: string) => {
+    if (confirm('确定要删除该模型吗?')) {
+      try {
+        await api.models.delete(id);
+        fetchData();
+      } catch (err) {
+        console.error('Failed to delete model:', err);
+      }
+    }
+  };
+
+  const statusConfig: Record<ModelStatus, { label: string; color: string; icon: typeof CheckCircle }> = {
+    enabled: { label: '启用', color: 'bg-green-100 text-green-700', icon: CheckCircle },
+    disabled: { label: '下线', color: 'bg-red-100 text-red-700', icon: XCircle },
+  };
+
+  const canEdit = user?.role === 'super_admin';
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">模型管理</h1>
+          <p className="text-slate-500 mt-1">管理平台所有模型的元数据和定价</p>
+        </div>
+        {canEdit && (
+          <button
+            onClick={() => handleOpenModal()}
+            className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+          >
+            <Plus className="w-5 h-5" />
+            新增模型
+          </button>
+        )}
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[900px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">模型名称</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">厂商</th>
+                <th className="w-28 px-6 py-4 text-left text-sm font-semibold text-slate-600">类型/版本</th>
+                <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">上下文窗口</th>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">输入/输出Token单价</th>
+                <th className="w-28 px-6 py-4 text-left text-sm font-semibold text-slate-600">关联渠道</th>
+                <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">状态</th>
+                <th className="w-24 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {models.map((model) => {
+              const status = statusConfig[model.status];
+              const StatusIcon = status.icon;
+              const channel = channels.find(c => c.id === model.channelId);
+              return (
+                <tr key={model.id} className="hover:bg-slate-50 transition-colors">
+                  <td className="px-6 py-4 font-medium text-slate-800">{model.name}</td>
+                  <td className="px-6 py-4 text-slate-600">{model.provider}</td>
+                  <td className="px-6 py-4 text-slate-600">{model.type} / {model.version}</td>
+                  <td className="px-6 py-4 text-slate-600">{(model.contextWindow / 1000).toFixed(0)}K</td>
+                  <td className="px-6 py-4">
+                    <div className="flex flex-col gap-1">
+                      <span className="text-xs text-slate-600">输入: ¥{model.inputPrice}/K</span>
+                      <span className="text-xs text-slate-600">输出: ¥{model.outputPrice}/K</span>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4 text-slate-600">{channel?.name || '未关联'}</td>
+                  <td className="px-6 py-4">
+                    <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${status.color}`}>
+                      <StatusIcon className="w-3 h-3" />
+                      {status.label}
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-2">
+                      {canEdit && (
+                        <>
+                          <button
+                            onClick={() => handleOpenModal(model)}
+                            className="p-2 text-slate-500 hover:text-cyan-600 hover:bg-cyan-50 rounded-lg transition-colors"
+                          >
+                            <Edit className="w-4 h-4" />
+                          </button>
+                          <button
+                            onClick={() => handleDelete(model.id)}
+                            className="p-2 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
+                          >
+                            <Trash2 className="w-4 h-4" />
+                          </button>
+                        </>
+                      )}
+                      {!canEdit && (
+                        <button className="p-2 text-slate-500 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors">
+                          <Eye className="w-4 h-4" />
+                        </button>
+                      )}
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+          </table>
+        </div>
+      </div>
+
+      {showModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg max-h-[90vh] overflow-y-auto">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingModel ? '编辑模型' : '新增模型'}</h2>
+              <button onClick={handleCloseModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleSubmit} className="p-6 space-y-4">
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">模型名称</label>
+                  <input
+                    type="text"
+                    value={form.name}
+                    onChange={(e) => setForm({ ...form, name: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                    required
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">厂商</label>
+                  <input
+                    type="text"
+                    value={form.provider}
+                    onChange={(e) => setForm({ ...form, provider: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                    required
+                  />
+                </div>
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">类型</label>
+                  <select
+                    value={form.type}
+                    onChange={(e) => setForm({ ...form, type: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  >
+                    <option value="chat">对话</option>
+                    <option value="completion">补全</option>
+                    <option value="embedding">嵌入</option>
+                  </select>
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">版本</label>
+                  <input
+                    type="text"
+                    value={form.version}
+                    onChange={(e) => setForm({ ...form, version: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              <div className="grid grid-cols-3 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">上下文窗口</label>
+                  <input
+                    type="number"
+                    value={form.contextWindow}
+                    onChange={(e) => setForm({ ...form, contextWindow: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">输入限制</label>
+                  <input
+                    type="number"
+                    value={form.inputLimit}
+                    onChange={(e) => setForm({ ...form, inputLimit: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">输出限制</label>
+                  <input
+                    type="number"
+                    value={form.outputLimit}
+                    onChange={(e) => setForm({ ...form, outputLimit: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">输入Token单价 (¥/K)</label>
+                  <input
+                    type="number"
+                    step="0.0001"
+                    value={form.inputPrice}
+                    onChange={(e) => setForm({ ...form, inputPrice: parseFloat(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">输出Token单价 (¥/K)</label>
+                  <input
+                    type="number"
+                    step="0.0001"
+                    value={form.outputPrice}
+                    onChange={(e) => setForm({ ...form, outputPrice: parseFloat(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">关联渠道</label>
+                <select
+                  value={form.channelId}
+                  onChange={(e) => setForm({ ...form, channelId: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  {channels.map(channel => (
+                    <option key={channel.id} value={channel.id}>{channel.name}</option>
+                  ))}
+                </select>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">状态</label>
+                <select
+                  value={form.status}
+                  onChange={(e) => setForm({ ...form, status: e.target.value as ModelStatus })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="enabled">启用</option>
+                  <option value="disabled">下线</option>
+                </select>
+              </div>
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingModel ? '保存修改' : '创建模型'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}

+ 17 - 0
src/pages/Product.tsx

@@ -0,0 +1,17 @@
+import { HeroCarousel } from '@/components/layout/HeroCarousel';
+import { PlatformArchitecture } from '@/components/product/PlatformArchitecture';
+import { CoreCapabilities } from '@/components/home/CoreCapabilities';
+import { PainPointSolutions } from '@/components/home/PainPointSolutions';
+import { FloatingConsult } from '@/components/layout/FloatingConsult';
+
+export function Product() {
+  return (
+    <main>
+      <HeroCarousel />
+      <CoreCapabilities />
+      <PlatformArchitecture />
+      <PainPointSolutions />
+      <FloatingConsult />
+    </main>
+  );
+}

+ 257 - 0
src/pages/Settings.tsx

@@ -0,0 +1,257 @@
+import { useState } from 'react';
+import { Save, Shield, Bell, Database, Lock, RefreshCw } from 'lucide-react';
+
+export function Settings() {
+  const [settings, setSettings] = useState({
+    systemName: 'Token运营平台',
+    defaultQuota: 100000,
+    maxQuota: 10000000,
+    autoDisableThreshold: 90,
+    sessionTimeout: 24,
+    enableEmailNotify: true,
+    enableSmsNotify: false,
+    dataRetentionDays: 90,
+    enableDataBackflow: true,
+    backflowInterval: 'daily',
+    enableRateLimit: true,
+    maxRequestsPerMinute: 1000,
+  });
+
+  const [activeTab, setActiveTab] = useState<'basic' | 'security' | 'notify' | 'data'>('basic');
+
+  const handleSave = () => {
+    alert('设置已保存');
+  };
+
+  const tabs = [
+    { id: 'basic' as const, label: '基础设置', icon: Shield },
+    { id: 'security' as const, label: '安全设置', icon: Lock },
+    { id: 'notify' as const, label: '通知设置', icon: Bell },
+    { id: 'data' as const, label: '数据设置', icon: Database },
+  ];
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">系统设置</h1>
+          <p className="text-slate-500 mt-1">配置平台全局参数与策略</p>
+        </div>
+        <button
+          onClick={handleSave}
+          className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+        >
+          <Save className="w-5 h-5" />
+          保存设置
+        </button>
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100">
+        <div className="flex border-b border-slate-100">
+          {tabs.map((tab) => (
+            <button
+              key={tab.id}
+              onClick={() => setActiveTab(tab.id)}
+              className={`flex items-center gap-2 px-6 py-4 font-medium transition-colors ${
+                activeTab === tab.id
+                  ? 'text-cyan-600 border-b-2 border-cyan-600 bg-cyan-50'
+                  : 'text-slate-600 hover:text-slate-800 hover:bg-slate-50'
+              }`}
+            >
+              <tab.icon className="w-5 h-5" />
+              {tab.label}
+            </button>
+          ))}
+        </div>
+
+        <div className="p-6">
+          {activeTab === 'basic' && (
+            <div className="space-y-6">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">平台名称</label>
+                <input
+                  type="text"
+                  value={settings.systemName}
+                  onChange={(e) => setSettings({ ...settings, systemName: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                />
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">默认用户配额(Token)</label>
+                  <input
+                    type="number"
+                    value={settings.defaultQuota}
+                    onChange={(e) => setSettings({ ...settings, defaultQuota: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">最大用户配额(Token)</label>
+                  <input
+                    type="number"
+                    value={settings.maxQuota}
+                    onChange={(e) => setSettings({ ...settings, maxQuota: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">配额自动禁用阈值(%)</label>
+                <input
+                  type="range"
+                  min="50"
+                  max="100"
+                  value={settings.autoDisableThreshold}
+                  onChange={(e) => setSettings({ ...settings, autoDisableThreshold: parseInt(e.target.value) || 0 })}
+                  className="w-full"
+                />
+                <p className="text-sm text-slate-500 mt-1">当用户配额使用超过 {settings.autoDisableThreshold}% 时,自动禁用API密钥</p>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">会话超时时间(小时)</label>
+                <input
+                  type="number"
+                  value={settings.sessionTimeout}
+                  onChange={(e) => setSettings({ ...settings, sessionTimeout: parseInt(e.target.value) || 0 })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  min="1"
+                  max="168"
+                />
+              </div>
+            </div>
+          )}
+
+          {activeTab === 'security' && (
+            <div className="space-y-6">
+              <div className="flex items-center justify-between">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700">启用限流</label>
+                  <p className="text-sm text-slate-500 mt-1">限制每分钟最大请求次数</p>
+                </div>
+                <button
+                  onClick={() => setSettings({ ...settings, enableRateLimit: !settings.enableRateLimit })}
+                  className={`w-12 h-6 rounded-full transition-colors ${settings.enableRateLimit ? 'bg-cyan-500' : 'bg-slate-300'}`}
+                >
+                  <div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ${settings.enableRateLimit ? 'translate-x-6' : 'translate-x-0.5'}`}></div>
+                </button>
+              </div>
+
+              {settings.enableRateLimit && (
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">每分钟最大请求次数</label>
+                  <input
+                    type="number"
+                    value={settings.maxRequestsPerMinute}
+                    onChange={(e) => setSettings({ ...settings, maxRequestsPerMinute: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                    min="100"
+                    max="10000"
+                  />
+                </div>
+              )}
+
+              <div className="bg-slate-50 rounded-lg p-4">
+                <div className="flex items-center gap-2">
+                  <RefreshCw className="w-5 h-5 text-cyan-600" />
+                  <div>
+                    <p className="text-sm font-medium text-slate-700">密码策略</p>
+                    <p className="text-xs text-slate-500">默认密码规则:至少8位,包含字母和数字</p>
+                  </div>
+                </div>
+              </div>
+
+              <div className="bg-slate-50 rounded-lg p-4">
+                <div className="flex items-center gap-2">
+                  <Lock className="w-5 h-5 text-cyan-600" />
+                  <div>
+                    <p className="text-sm font-medium text-slate-700">API密钥安全</p>
+                    <p className="text-xs text-slate-500">密钥采用SHA256加密存储,支持定期轮换</p>
+                  </div>
+                </div>
+              </div>
+            </div>
+          )}
+
+          {activeTab === 'notify' && (
+            <div className="space-y-6">
+              <div className="flex items-center justify-between">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700">启用邮件通知</label>
+                  <p className="text-sm text-slate-500 mt-1">当配额使用超过阈值时发送邮件通知</p>
+                </div>
+                <button
+                  onClick={() => setSettings({ ...settings, enableEmailNotify: !settings.enableEmailNotify })}
+                  className={`w-12 h-6 rounded-full transition-colors ${settings.enableEmailNotify ? 'bg-cyan-500' : 'bg-slate-300'}`}
+                >
+                  <div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ${settings.enableEmailNotify ? 'translate-x-6' : 'translate-x-0.5'}`}></div>
+                </button>
+              </div>
+
+              <div className="flex items-center justify-between">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700">启用短信通知</label>
+                  <p className="text-sm text-slate-500 mt-1">当配额使用超过阈值时发送短信通知</p>
+                </div>
+                <button
+                  onClick={() => setSettings({ ...settings, enableSmsNotify: !settings.enableSmsNotify })}
+                  className={`w-12 h-6 rounded-full transition-colors ${settings.enableSmsNotify ? 'bg-cyan-500' : 'bg-slate-300'}`}
+                >
+                  <div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ${settings.enableSmsNotify ? 'translate-x-6' : 'translate-x-0.5'}`}></div>
+                </button>
+              </div>
+            </div>
+          )}
+
+          {activeTab === 'data' && (
+            <div className="space-y-6">
+              <div className="flex items-center justify-between">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700">启用数据回流</label>
+                  <p className="text-sm text-slate-500 mt-1">将调用记录同步到外部数据集系统</p>
+                </div>
+                <button
+                  onClick={() => setSettings({ ...settings, enableDataBackflow: !settings.enableDataBackflow })}
+                  className={`w-12 h-6 rounded-full transition-colors ${settings.enableDataBackflow ? 'bg-cyan-500' : 'bg-slate-300'}`}
+                >
+                  <div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ${settings.enableDataBackflow ? 'translate-x-6' : 'translate-x-0.5'}`}></div>
+                </button>
+              </div>
+
+              {settings.enableDataBackflow && (
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">回流频率</label>
+                  <select
+                    value={settings.backflowInterval}
+                    onChange={(e) => setSettings({ ...settings, backflowInterval: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  >
+                    <option value="hourly">每小时</option>
+                    <option value="daily">每日</option>
+                    <option value="weekly">每周</option>
+                  </select>
+                </div>
+              )}
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">数据保留天数</label>
+                <input
+                  type="number"
+                  value={settings.dataRetentionDays}
+                  onChange={(e) => setSettings({ ...settings, dataRetentionDays: parseInt(e.target.value) || 0 })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  min="30"
+                  max="365"
+                />
+                <p className="text-sm text-slate-500 mt-1">超过保留期限的数据将被自动清理</p>
+              </div>
+            </div>
+          )}
+        </div>
+      </div>
+    </div>
+  );
+}

+ 15 - 0
src/pages/Solutions.tsx

@@ -0,0 +1,15 @@
+import { HeroCarousel } from '@/components/layout/HeroCarousel';
+import { ApplicationScenarios } from '@/components/home/ApplicationScenarios';
+import { PowerSolution } from '@/components/solutions/PowerSolution';
+import { FloatingConsult } from '@/components/layout/FloatingConsult';
+
+export function Solutions() {
+  return (
+    <main>
+      <HeroCarousel />
+      <ApplicationScenarios />
+      <PowerSolution />
+      <FloatingConsult />
+    </main>
+  );
+}

+ 213 - 0
src/pages/TokenStats.tsx

@@ -0,0 +1,213 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { Download, Calendar } from 'lucide-react';
+import type { TokenStats as TokenStatsType } from '@/types';
+
+export function TokenStats() {
+  const [stats, setStats] = useState<TokenStatsType[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [startDate, setStartDate] = useState('');
+  const [endDate, setEndDate] = useState('');
+
+  useEffect(() => {
+    fetchStats();
+  }, []);
+
+  const fetchStats = async () => {
+    setLoading(true);
+    try {
+      const data = await api.stats.tokens();
+      setStats(data);
+    } catch (err) {
+      console.error('Failed to fetch token stats:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleExport = () => {
+    const headers = ['日期', '输入Token', '输出Token', '总Token', '调用次数'];
+    const csvContent = [
+      headers.join(','),
+      ...stats.map(stat => [
+        stat.date,
+        stat.inputTokens,
+        stat.outputTokens,
+        stat.totalTokens,
+        stat.callCount,
+      ].join(','))
+    ].join('\n');
+
+    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+    const link = document.createElement('a');
+    link.href = URL.createObjectURL(blob);
+    link.download = `token-stats-${new Date().toISOString().split('T')[0]}.csv`;
+    link.click();
+  };
+
+  const totalInputTokens = stats.reduce((sum, stat) => sum + stat.inputTokens, 0);
+  const totalOutputTokens = stats.reduce((sum, stat) => sum + stat.outputTokens, 0);
+  const totalTokens = stats.reduce((sum, stat) => sum + stat.totalTokens, 0);
+  const totalCalls = stats.reduce((sum, stat) => sum + stat.callCount, 0);
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">Token统计</h1>
+          <p className="text-slate-500 mt-1">统计输入/输出/总Token用量及调用次数</p>
+        </div>
+        <div className="flex items-center gap-3">
+          <div className="flex items-center gap-2">
+            <Calendar className="w-4 h-4 text-slate-400" />
+            <input
+              type="date"
+              value={startDate}
+              onChange={(e) => setStartDate(e.target.value)}
+              className="px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+            />
+            <span className="text-slate-400">至</span>
+            <input
+              type="date"
+              value={endDate}
+              onChange={(e) => setEndDate(e.target.value)}
+              className="px-3 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+            />
+          </div>
+          <button
+            onClick={handleExport}
+            className="flex items-center gap-2 px-4 py-2 bg-white border border-slate-200 text-slate-600 font-medium rounded-lg hover:bg-slate-50 transition-all"
+          >
+            <Download className="w-5 h-5" />
+            导出数据
+          </button>
+        </div>
+      </div>
+
+      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">输入Token</p>
+              <p className="text-2xl font-bold text-slate-800 mt-1">{(totalInputTokens / 1000000).toFixed(2)}M</p>
+            </div>
+            <div className="w-10 h-10 rounded-lg bg-cyan-100 text-cyan-600 flex items-center justify-center">
+              <span className="text-lg">IN</span>
+            </div>
+          </div>
+          <div className="mt-3 flex items-center text-sm text-green-600">
+            <span className="w-1.5 h-1.5 bg-green-500 rounded-full mr-2"></span>
+            +12.5% 较上周
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">输出Token</p>
+              <p className="text-2xl font-bold text-slate-800 mt-1">{(totalOutputTokens / 1000000).toFixed(2)}M</p>
+            </div>
+            <div className="w-10 h-10 rounded-lg bg-blue-100 text-blue-600 flex items-center justify-center">
+              <span className="text-lg">OUT</span>
+            </div>
+          </div>
+          <div className="mt-3 flex items-center text-sm text-green-600">
+            <span className="w-1.5 h-1.5 bg-green-500 rounded-full mr-2"></span>
+            +8.3% 较上周
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">总Token</p>
+              <p className="text-2xl font-bold text-slate-800 mt-1">{(totalTokens / 1000000).toFixed(2)}M</p>
+            </div>
+            <div className="w-10 h-10 rounded-lg bg-purple-100 text-purple-600 flex items-center justify-center">
+              <span className="text-lg">T</span>
+            </div>
+          </div>
+          <div className="mt-3 flex items-center text-sm text-green-600">
+            <span className="w-1.5 h-1.5 bg-green-500 rounded-full mr-2"></span>
+            +10.2% 较上周
+          </div>
+        </div>
+
+        <div className="bg-white rounded-xl p-5 border border-slate-100">
+          <div className="flex items-center justify-between">
+            <div>
+              <p className="text-slate-500 text-sm">调用次数</p>
+              <p className="text-2xl font-bold text-slate-800 mt-1">{totalCalls.toLocaleString()}</p>
+            </div>
+            <div className="w-10 h-10 rounded-lg bg-orange-100 text-orange-600 flex items-center justify-center">
+              <span className="text-lg">C</span>
+            </div>
+          </div>
+          <div className="mt-3 flex items-center text-sm text-green-600">
+            <span className="w-1.5 h-1.5 bg-green-500 rounded-full mr-2"></span>
+            +15.1% 较上周
+          </div>
+        </div>
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 p-6">
+        <h2 className="text-lg font-semibold text-slate-800 mb-4">Token消耗趋势</h2>
+        <div className="h-72 flex items-end gap-4">
+          {stats.map((stat, index) => {
+            const maxTokens = Math.max(...stats.map(s => s.totalTokens));
+            const inputHeight = (stat.inputTokens / maxTokens) * 100;
+            const outputHeight = (stat.outputTokens / maxTokens) * 100;
+            return (
+              <div key={index} className="flex-1 flex flex-col items-center gap-2">
+                <div className="w-full flex gap-1 items-end" style={{ height: '200px' }}>
+                  <div className="flex-1 bg-gradient-to-t from-cyan-400 to-cyan-300 rounded-t-lg transition-all hover:from-cyan-500 hover:to-cyan-400" style={{ height: `${inputHeight}%`, minHeight: '10px' }}></div>
+                  <div className="flex-1 bg-gradient-to-t from-blue-400 to-blue-300 rounded-t-lg transition-all hover:from-blue-500 hover:to-blue-400" style={{ height: `${outputHeight}%`, minHeight: '10px' }}></div>
+                </div>
+                <div className="flex gap-2 text-xs">
+                  <span className="text-cyan-500">IN</span>
+                  <span className="text-blue-500">OUT</span>
+                </div>
+                <span className="text-xs text-slate-500">{stat.date.slice(5)}</span>
+              </div>
+            );
+          })}
+        </div>
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[600px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">日期</th>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">输入Token</th>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">输出Token</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">总Token</th>
+                <th className="w-32 px-6 py-4 text-left text-sm font-semibold text-slate-600">调用次数</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {stats.map((stat) => (
+              <tr key={stat.date} className="hover:bg-slate-50 transition-colors">
+                <td className="px-6 py-4 font-medium text-slate-800">{stat.date}</td>
+                <td className="px-6 py-4 text-slate-600">{stat.inputTokens.toLocaleString()}</td>
+                <td className="px-6 py-4 text-slate-600">{stat.outputTokens.toLocaleString()}</td>
+                <td className="px-6 py-4 text-slate-600">{stat.totalTokens.toLocaleString()}</td>
+                <td className="px-6 py-4 text-slate-600">{stat.callCount}</td>
+              </tr>
+            ))}
+          </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  );
+}

+ 479 - 0
src/pages/Users.tsx

@@ -0,0 +1,479 @@
+import { useState, useEffect } from 'react';
+import { api } from '@/api/mockApi';
+import { useAuthStore } from '@/store/authStore';
+import { Plus, Edit, Trash2, CheckCircle, XCircle, RefreshCw, Upload, Users as UsersIcon } from 'lucide-react';
+import type { User, UserStatus, Role } from '@/types';
+
+export function Users() {
+  const [users, setUsers] = useState<User[]>([]);
+  const [enterprises, setEnterprises] = useState<{ id: string; name: string }[]>([]);
+  const [departments, setDepartments] = useState<{ id: string; name: string; enterpriseId: string }[]>([]);
+  const [loading, setLoading] = useState(true);
+  const [showModal, setShowModal] = useState(false);
+  const [editingUser, setEditingUser] = useState<User | null>(null);
+  const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
+  const [form, setForm] = useState({
+    username: '',
+    email: '',
+    phone: '',
+    role: 'employee' as Role,
+    status: 'enabled' as UserStatus,
+    enterpriseId: '',
+    departmentId: '',
+    quota: 100000,
+  });
+  const { user } = useAuthStore();
+
+  useEffect(() => {
+    fetchData();
+  }, []);
+
+  const fetchData = async () => {
+    setLoading(true);
+    try {
+      const [usersData, enterprisesData, departmentsData] = await Promise.all([
+        api.users.list(user?.role === 'enterprise_admin' ? user?.enterpriseId : undefined),
+        api.enterprises.list(),
+        api.departments.list(user?.role === 'enterprise_admin' ? user?.enterpriseId : undefined),
+      ]);
+      setUsers(usersData);
+      setEnterprises(enterprisesData.map(e => ({ id: e.id, name: e.name })));
+      setDepartments(departmentsData.map(d => ({ id: d.id, name: d.name, enterpriseId: d.enterpriseId })));
+    } catch (err) {
+      console.error('Failed to fetch data:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleOpenModal = (user?: User) => {
+    if (user) {
+      setEditingUser(user);
+      setForm({
+        username: user.username,
+        email: user.email,
+        phone: user.phone,
+        role: user.role,
+        status: user.status,
+        enterpriseId: user.enterpriseId,
+        departmentId: user.departmentId || '',
+        quota: user.quota,
+      });
+    } else {
+      setEditingUser(null);
+      setForm({
+        username: '',
+        email: '',
+        phone: '',
+        role: 'employee',
+        status: 'enabled',
+        enterpriseId: user?.role === 'enterprise_admin' ? (user?.enterpriseId || '') : (enterprises[0]?.id || ''),
+        departmentId: '',
+        quota: 100000,
+      });
+    }
+    setShowModal(true);
+  };
+
+  const handleCloseModal = () => {
+    setShowModal(false);
+    setEditingUser(null);
+  };
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    try {
+      if (editingUser) {
+        await api.users.update(editingUser.id, form);
+      } else {
+        await api.users.create({
+          ...form,
+          usedQuota: 0,
+        });
+      }
+      handleCloseModal();
+      fetchData();
+    } catch (err) {
+      console.error('Failed to save user:', err);
+    }
+  };
+
+  const handleDelete = async (id: string) => {
+    if (confirm('确定要删除该用户吗?')) {
+      try {
+        await api.users.delete(id);
+        fetchData();
+      } catch (err) {
+        console.error('Failed to delete user:', err);
+      }
+    }
+  };
+
+  const handleBatchToggleStatus = async (status: UserStatus) => {
+    if (selectedUsers.length === 0) return;
+    if (!confirm(`确定要${status === 'enabled' ? '启用' : '冻结'}选中的${selectedUsers.length}个用户吗?`)) return;
+    
+    try {
+      for (const id of selectedUsers) {
+        await api.users.update(id, { status });
+      }
+      setSelectedUsers([]);
+      fetchData();
+    } catch (err) {
+      console.error('Failed to update users:', err);
+    }
+  };
+
+  const handleResetPassword = async (id: string) => {
+    if (confirm('确定要重置该用户密码吗?初始密码将设置为123456')) {
+      alert('密码已重置为:123456');
+    }
+  };
+
+  const statusConfig: Record<UserStatus, { label: string; color: string; icon: typeof CheckCircle }> = {
+    enabled: { label: '启用', color: 'bg-green-100 text-green-700', icon: CheckCircle },
+    disabled: { label: '禁用', color: 'bg-slate-100 text-slate-600', icon: XCircle },
+    frozen: { label: '冻结', color: 'bg-orange-100 text-orange-700', icon: XCircle },
+  };
+
+  const roleLabels: Record<Role, string> = {
+    super_admin: '平台超级管理员',
+    platform_operator: '平台运营管理员',
+    enterprise_admin: '企业租户管理员',
+    employee: '普通员工',
+  };
+
+  const roleColors: Record<Role, string> = {
+    super_admin: 'bg-red-100 text-red-700',
+    platform_operator: 'bg-purple-100 text-purple-700',
+    enterprise_admin: 'bg-blue-100 text-blue-700',
+    employee: 'bg-green-100 text-green-700',
+  };
+
+  const getEnterpriseName = (id: string) => enterprises.find(e => e.id === id)?.name || '-';
+  const getDepartmentName = (id: string | null) => id ? departments.find(d => d.id === id)?.name || '-' : '-';
+
+  const canEdit = user?.role === 'super_admin' || user?.role === 'enterprise_admin';
+  const canAddAdmin = user?.role === 'super_admin';
+
+  const filteredDepartments = form.enterpriseId 
+    ? departments.filter(d => d.enterpriseId === form.enterpriseId)
+    : departments;
+
+  if (loading) {
+    return (
+      <div className="flex items-center justify-center h-64">
+        <div className="w-8 h-8 border-4 border-cyan-500 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-6 space-y-6">
+      <div className="flex items-center justify-between">
+        <div>
+          <h1 className="text-2xl font-bold text-slate-800">用户管理</h1>
+          <p className="text-slate-500 mt-1">{user?.role === 'enterprise_admin' ? '管理企业内部员工' : '管理平台所有用户'}</p>
+        </div>
+        {canEdit && (
+          <div className="flex items-center gap-3">
+            {selectedUsers.length > 0 && (
+              <>
+                <button
+                  onClick={() => handleBatchToggleStatus('enabled')}
+                  className="flex items-center gap-2 px-4 py-2 bg-green-500 text-white font-medium rounded-lg hover:bg-green-600 transition-all"
+                >
+                  <CheckCircle className="w-4 h-4" />
+                  批量启用
+                </button>
+                <button
+                  onClick={() => handleBatchToggleStatus('disabled')}
+                  className="flex items-center gap-2 px-4 py-2 bg-orange-500 text-white font-medium rounded-lg hover:bg-orange-600 transition-all"
+                >
+                  <XCircle className="w-4 h-4" />
+                  批量冻结
+                </button>
+              </>
+            )}
+            <button
+              onClick={() => handleOpenModal()}
+              className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+            >
+              <Plus className="w-5 h-5" />
+              新增用户
+            </button>
+            <button className="flex items-center gap-2 px-4 py-2 bg-white border border-slate-200 text-slate-600 font-medium rounded-lg hover:bg-slate-50 transition-all">
+              <Upload className="w-5 h-5" />
+              批量导入
+            </button>
+          </div>
+        )}
+      </div>
+
+      <div className="bg-white rounded-xl border border-slate-100 overflow-hidden">
+        <div className="overflow-x-auto">
+          <table className="w-full min-w-[900px] table-fixed">
+            <thead className="bg-slate-50">
+              <tr>
+                <th className="w-12 px-6 py-4 text-left text-sm font-semibold text-slate-600">
+                  {canEdit && (
+                    <input
+                      type="checkbox"
+                      checked={selectedUsers.length === users.length && users.length > 0}
+                      onChange={(e) => setSelectedUsers(e.target.checked ? users.map(u => u.id) : [])}
+                      className="rounded border-slate-300 text-cyan-600 focus:ring-cyan-500"
+                    />
+                  )}
+                </th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">用户名</th>
+                <th className="w-40 px-6 py-4 text-left text-sm font-semibold text-slate-600">企业/部门</th>
+                <th className="w-44 px-6 py-4 text-left text-sm font-semibold text-slate-600">联系方式</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">角色</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">配额</th>
+                <th className="w-20 px-6 py-4 text-left text-sm font-semibold text-slate-600">状态</th>
+                <th className="w-36 px-6 py-4 text-left text-sm font-semibold text-slate-600">操作</th>
+              </tr>
+            </thead>
+          <tbody className="divide-y divide-slate-100">
+            {users.map((userItem) => {
+              const status = statusConfig[userItem.status];
+              const StatusIcon = status.icon;
+              const usedPercent = userItem.quota > 0 ? (userItem.usedQuota / userItem.quota) * 100 : 0;
+              
+              return (
+                <tr key={userItem.id} className="hover:bg-slate-50 transition-colors">
+                  <td className="px-6 py-4">
+                    {canEdit && (
+                      <input
+                        type="checkbox"
+                        checked={selectedUsers.includes(userItem.id)}
+                        onChange={(e) => setSelectedUsers(
+                          e.target.checked 
+                            ? [...selectedUsers, userItem.id] 
+                            : selectedUsers.filter(id => id !== userItem.id)
+                        )}
+                        className="rounded border-slate-300 text-cyan-600 focus:ring-cyan-500"
+                      />
+                    )}
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-3">
+                      <div className="w-8 h-8 rounded-full bg-gradient-to-br from-cyan-400 to-blue-600 flex items-center justify-center">
+                        <UsersIcon className="w-4 h-4 text-white" />
+                      </div>
+                      <div>
+                        <p className="font-medium text-slate-800">{userItem.username}</p>
+                        <p className="text-xs text-slate-500">ID: {userItem.id}</p>
+                      </div>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex flex-col gap-1">
+                      <span className="text-sm text-slate-700">{getEnterpriseName(userItem.enterpriseId)}</span>
+                      <span className="text-xs text-slate-500">{getDepartmentName(userItem.departmentId)}</span>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex flex-col gap-1">
+                      <span className="text-sm text-slate-600">{userItem.email}</span>
+                      <span className="text-xs text-slate-500">{userItem.phone}</span>
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${roleColors[userItem.role]}`}>
+                      {roleLabels[userItem.role]}
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex flex-col gap-1">
+                      <div className="flex items-center justify-between text-xs">
+                        <span className="text-slate-600">{userItem.quota > 0 ? userItem.quota.toLocaleString() : '不限'}</span>
+                        <span className="text-slate-500">已用: {(userItem.usedQuota / 1000).toFixed(0)}K</span>
+                      </div>
+                      {userItem.quota > 0 && (
+                        <div className="h-1.5 bg-slate-100 rounded-full overflow-hidden">
+                          <div 
+                            className={`h-full rounded-full transition-all ${usedPercent > 80 ? 'bg-red-500' : usedPercent > 50 ? 'bg-orange-500' : 'bg-cyan-500'}`}
+                            style={{ width: `${Math.min(usedPercent, 100)}%` }}
+                          ></div>
+                        </div>
+                      )}
+                    </div>
+                  </td>
+                  <td className="px-6 py-4">
+                    <span className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium ${status.color}`}>
+                      <StatusIcon className="w-3 h-3" />
+                      {status.label}
+                    </span>
+                  </td>
+                  <td className="px-6 py-4">
+                    <div className="flex items-center gap-2">
+                      {canEdit && (
+                        <>
+                          <button
+                            onClick={() => handleOpenModal(userItem)}
+                            className="p-2 text-slate-500 hover:text-cyan-600 hover:bg-cyan-50 rounded-lg transition-colors"
+                          >
+                            <Edit className="w-4 h-4" />
+                          </button>
+                          <button
+                            onClick={() => handleResetPassword(userItem.id)}
+                            className="p-2 text-slate-500 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition-colors"
+                          >
+                            <RefreshCw className="w-4 h-4" />
+                          </button>
+                          <button
+                            onClick={() => handleDelete(userItem.id)}
+                            className="p-2 text-slate-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
+                          >
+                            <Trash2 className="w-4 h-4" />
+                          </button>
+                        </>
+                      )}
+                    </div>
+                  </td>
+                </tr>
+              );
+            })}
+          </tbody>
+          </table>
+        </div>
+      </div>
+
+      {showModal && (
+        <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
+          <div className="bg-white rounded-2xl w-full max-w-lg">
+            <div className="flex items-center justify-between p-6 border-b border-slate-100">
+              <h2 className="text-xl font-bold text-slate-800">{editingUser ? '编辑用户' : '新增用户'}</h2>
+              <button onClick={handleCloseModal} className="p-2 hover:bg-slate-100 rounded-lg">
+                <XCircle className="w-5 h-5 text-slate-500" />
+              </button>
+            </div>
+
+            <form onSubmit={handleSubmit} className="p-6 space-y-4">
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">用户名</label>
+                <input
+                  type="text"
+                  value={form.username}
+                  onChange={(e) => setForm({ ...form, username: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  required
+                />
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">邮箱</label>
+                  <input
+                    type="email"
+                    value={form.email}
+                    onChange={(e) => setForm({ ...form, email: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">手机号</label>
+                  <input
+                    type="text"
+                    value={form.phone}
+                    onChange={(e) => setForm({ ...form, phone: e.target.value })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              {user?.role !== 'enterprise_admin' && (
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">所属企业</label>
+                  <select
+                    value={form.enterpriseId}
+                    onChange={(e) => setForm({ ...form, enterpriseId: e.target.value, departmentId: '' })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  >
+                    {enterprises.map(enterprise => (
+                      <option key={enterprise.id} value={enterprise.id}>{enterprise.name}</option>
+                    ))}
+                  </select>
+                </div>
+              )}
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">所属部门</label>
+                <select
+                  value={form.departmentId}
+                  onChange={(e) => setForm({ ...form, departmentId: e.target.value })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="">无</option>
+                  {filteredDepartments.map(department => (
+                    <option key={department.id} value={department.id}>{department.name}</option>
+                  ))}
+                </select>
+              </div>
+
+              <div className="grid grid-cols-2 gap-4">
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">角色</label>
+                  <select
+                    value={form.role}
+                    onChange={(e) => setForm({ ...form, role: e.target.value as Role })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                    disabled={!canAddAdmin && form.role === 'super_admin'}
+                  >
+                    {canAddAdmin ? (
+                      <>
+                        <option value="super_admin">平台超级管理员</option>
+                        <option value="platform_operator">平台运营管理员</option>
+                      </>
+                    ) : null}
+                    <option value="enterprise_admin">企业租户管理员</option>
+                    <option value="employee">普通员工</option>
+                  </select>
+                </div>
+                <div>
+                  <label className="block text-sm font-medium text-slate-700 mb-2">月度Token配额</label>
+                  <input
+                    type="number"
+                    value={form.quota}
+                    onChange={(e) => setForm({ ...form, quota: parseInt(e.target.value) || 0 })}
+                    className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                  />
+                </div>
+              </div>
+
+              <div>
+                <label className="block text-sm font-medium text-slate-700 mb-2">状态</label>
+                <select
+                  value={form.status}
+                  onChange={(e) => setForm({ ...form, status: e.target.value as UserStatus })}
+                  className="w-full px-4 py-2.5 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-500"
+                >
+                  <option value="enabled">启用</option>
+                  <option value="disabled">禁用</option>
+                  <option value="frozen">冻结</option>
+                </select>
+              </div>
+
+              <div className="flex items-center justify-end gap-3 pt-4">
+                <button
+                  type="button"
+                  onClick={handleCloseModal}
+                  className="px-4 py-2 text-slate-600 font-medium hover:bg-slate-100 rounded-lg transition-colors"
+                >
+                  取消
+                </button>
+                <button
+                  type="submit"
+                  className="px-4 py-2 bg-gradient-to-r from-cyan-500 to-blue-600 text-white font-medium rounded-lg hover:from-cyan-600 hover:to-blue-700 transition-all"
+                >
+                  {editingUser ? '保存修改' : '创建用户'}
+                </button>
+              </div>
+            </form>
+          </div>
+        </div>
+      )}
+    </div>
+  );
+}

+ 141 - 0
src/store/authStore.ts

@@ -0,0 +1,141 @@
+import { create } from 'zustand';
+import type { User, LoginForm, LoginResponse } from '@/types';
+
+interface AuthState {
+  user: User | null;
+  token: string | null;
+  isLoggedIn: boolean;
+  login: (form: LoginForm) => Promise<LoginResponse>;
+  logout: () => void;
+  setUser: (user: User) => void;
+  setToken: (token: string) => void;
+  setIsLoggedIn: (isLoggedIn: boolean) => void;
+}
+
+const mockUsers: Record<string, { password: string; user: User }> = {
+  'super_admin': {
+    password: 'admin123',
+    user: {
+      id: 'u1',
+      username: 'super_admin',
+      email: 'super@token.com',
+      phone: '13800000001',
+      role: 'super_admin',
+      status: 'enabled',
+      enterpriseId: '',
+      departmentId: null,
+      quota: -1,
+      usedQuota: 0,
+      createdAt: '2024-01-01',
+      updatedAt: '2024-01-01',
+    },
+  },
+  'platform_operator': {
+    password: 'operator123',
+    user: {
+      id: 'u2',
+      username: 'platform_operator',
+      email: 'operator@token.com',
+      phone: '13800000002',
+      role: 'platform_operator',
+      status: 'enabled',
+      enterpriseId: '',
+      departmentId: null,
+      quota: -1,
+      usedQuota: 0,
+      createdAt: '2024-01-01',
+      updatedAt: '2024-01-01',
+    },
+  },
+  'enterprise_admin': {
+    password: 'enterprise123',
+    user: {
+      id: 'u3',
+      username: 'enterprise_admin',
+      email: 'admin@enterprise.com',
+      phone: '13800000003',
+      role: 'enterprise_admin',
+      status: 'enabled',
+      enterpriseId: 'e1',
+      departmentId: 'd1',
+      quota: 1000000,
+      usedQuota: 250000,
+      createdAt: '2024-01-01',
+      updatedAt: '2024-01-01',
+    },
+  },
+  'employee': {
+    password: 'employee123',
+    user: {
+      id: 'u4',
+      username: 'employee',
+      email: 'employee@enterprise.com',
+      phone: '13800000004',
+      role: 'employee',
+      status: 'enabled',
+      enterpriseId: 'e1',
+      departmentId: 'd2',
+      quota: 100000,
+      usedQuota: 35000,
+      createdAt: '2024-01-01',
+      updatedAt: '2024-01-01',
+    },
+  },
+};
+
+export const useAuthStore = create<AuthState>()((set) => ({
+  user: null,
+  token: null,
+  isLoggedIn: false,
+  
+  login: async (form: LoginForm) => {
+    const mockUser = mockUsers[form.username];
+    if (!mockUser || mockUser.password !== form.password) {
+      throw new Error('用户名或密码错误');
+    }
+    const response: LoginResponse = {
+      token: `token_${form.username}_${Date.now()}`,
+      user: mockUser.user,
+    };
+    set({ user: mockUser.user, token: response.token, isLoggedIn: true });
+    localStorage.setItem('token', response.token);
+    localStorage.setItem('user', JSON.stringify(mockUser.user));
+    return response;
+  },
+  
+  logout: () => {
+    set({ user: null, token: null, isLoggedIn: false });
+    localStorage.removeItem('token');
+    localStorage.removeItem('user');
+  },
+  
+  setUser: (user: User) => {
+    set({ user });
+    localStorage.setItem('user', JSON.stringify(user));
+  },
+  
+  setToken: (token: string) => {
+    set({ token });
+    localStorage.setItem('token', token);
+  },
+  
+  setIsLoggedIn: (isLoggedIn: boolean) => {
+    set({ isLoggedIn });
+  },
+}));
+
+export const initAuth = () => {
+  const token = localStorage.getItem('token');
+  const userStr = localStorage.getItem('user');
+  if (token && userStr) {
+    try {
+      const user = JSON.parse(userStr) as User;
+      useAuthStore.getState().setUser(user);
+      useAuthStore.getState().setToken(token);
+      useAuthStore.getState().setIsLoggedIn(true);
+    } catch {
+      localStorage.removeItem('token');
+      localStorage.removeItem('user');
+    }
+  }
+};

+ 142 - 0
src/types/index.ts

@@ -0,0 +1,142 @@
+export type Role = 'super_admin' | 'platform_operator' | 'enterprise_admin' | 'employee';
+
+export type ChannelStatus = 'enabled' | 'disabled' | 'error';
+
+export type ModelStatus = 'enabled' | 'disabled';
+
+export type UserStatus = 'enabled' | 'disabled' | 'frozen';
+
+export type KeyStatus = 'active' | 'inactive' | 'expired';
+
+export interface Channel {
+  id: string;
+  name: string;
+  provider: 'deepseek' | 'tongyi' | 'zhipu' | 'wenxin' | 'custom';
+  apiKeys: string[];
+  weight: number;
+  priority: number;
+  status: ChannelStatus;
+  baseUrl: string;
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface Model {
+  id: string;
+  name: string;
+  provider: string;
+  type: string;
+  version: string;
+  contextWindow: number;
+  inputLimit: number;
+  outputLimit: number;
+  inputPrice: number;
+  outputPrice: number;
+  status: ModelStatus;
+  channelId: string;
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface Enterprise {
+  id: string;
+  name: string;
+  contact: string;
+  phone: string;
+  email: string;
+  status: 'active' | 'frozen' | 'deleted';
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface Department {
+  id: string;
+  name: string;
+  enterpriseId: string;
+  parentId: string | null;
+  createdAt: string;
+  updatedAt: string;
+  children?: Department[];
+}
+
+export interface User {
+  id: string;
+  username: string;
+  email: string;
+  phone: string;
+  role: Role;
+  status: UserStatus;
+  enterpriseId: string;
+  departmentId: string | null;
+  quota: number;
+  usedQuota: number;
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface APIKey {
+  id: string;
+  key: string;
+  name: string;
+  userId: string;
+  quota: number;
+  usedQuota: number;
+  allowedModels: string[];
+  expiresAt: string;
+  status: KeyStatus;
+  createdAt: string;
+  updatedAt: string;
+}
+
+export interface CallLog {
+  id: string;
+  userId: string;
+  userName: string;
+  enterpriseId: string;
+  modelName: string;
+  inputTokens: number;
+  outputTokens: number;
+  totalTokens: number;
+  status: 'success' | 'failed';
+  errorMessage: string;
+  responseTime: number;
+  apiKeyId: string;
+  createdAt: string;
+}
+
+export interface TokenStats {
+  date: string;
+  inputTokens: number;
+  outputTokens: number;
+  totalTokens: number;
+  callCount: number;
+}
+
+export interface CostStats {
+  date: string;
+  amount: number;
+  enterpriseId?: string;
+  userId?: string;
+  modelId?: string;
+}
+
+export interface DashboardData {
+  totalCalls: number;
+  totalTokens: number;
+  totalCost: number;
+  activeUsers: number;
+  hotModels: { name: string; count: number }[];
+  abnormalCalls: number;
+  tokenTrend: TokenStats[];
+  costTrend: CostStats[];
+}
+
+export interface LoginForm {
+  username: string;
+  password: string;
+}
+
+export interface LoginResponse {
+  token: string;
+  user: User;
+}

+ 1 - 0
src/vite-env.d.ts

@@ -0,0 +1 @@
+/// <reference types="vite/client" />

+ 104 - 0
tailwind.config.js

@@ -0,0 +1,104 @@
+/** @type {import('tailwindcss').Config} */
+
+export default {
+  darkMode: "class",
+  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
+  theme: {
+    container: {
+      center: true,
+      padding: {
+        DEFAULT: '1rem',
+        sm: '2rem',
+        lg: '4rem',
+        xl: '6rem',
+      },
+    },
+    extend: {
+      colors: {
+        'deep-space': {
+          50: '#f0f4f8',
+          100: '#d9e2ec',
+          200: '#bcccdc',
+          300: '#9fb3c8',
+          400: '#829ab1',
+          500: '#627d98',
+          600: '#486581',
+          700: '#334e68',
+          800: '#243b53',
+          900: '#102a43',
+          950: '#0a1628',
+        },
+        'tech-cyan': {
+          50: '#ecfeff',
+          100: '#cffafe',
+          200: '#a5f3fc',
+          300: '#67e8f9',
+          400: '#22d3ee',
+          500: '#06b6d4',
+          600: '#0891b2',
+          700: '#0e7490',
+          800: '#155e75',
+          900: '#164e63',
+        },
+        'aurora-blue': {
+          50: '#eff6ff',
+          100: '#dbeafe',
+          200: '#bfdbfe',
+          300: '#93c5fd',
+          400: '#60a5fa',
+          500: '#3b82f6',
+          600: '#2563eb',
+          700: '#1d4ed8',
+          800: '#1e40af',
+          900: '#1e3a8a',
+        },
+        'glow-green': '#00D4AA',
+        'glow-purple': '#6366F1',
+      },
+      fontFamily: {
+        sans: ['Inter', 'PingFang SC', 'Microsoft YaHei', 'sans-serif'],
+      },
+      animation: {
+        'fade-in': 'fadeIn 0.8s ease-out forwards',
+        'slide-up': 'slideUp 0.8s ease-out forwards',
+        'slide-right': 'slideRight 0.8s ease-out forwards',
+        'pulse-glow': 'pulseGlow 2s ease-in-out infinite',
+        'float': 'float 6s ease-in-out infinite',
+        'gradient-shift': 'gradientShift 15s ease infinite',
+        'data-flow': 'dataFlow 3s linear infinite',
+      },
+      keyframes: {
+        fadeIn: {
+          '0%': { opacity: '0' },
+          '100%': { opacity: '1' },
+        },
+        slideUp: {
+          '0%': { opacity: '0', transform: 'translateY(30px)' },
+          '100%': { opacity: '1', transform: 'translateY(0)' },
+        },
+        slideRight: {
+          '0%': { opacity: '0', transform: 'translateX(-30px)' },
+          '100%': { opacity: '1', transform: 'translateX(0)' },
+        },
+        pulseGlow: {
+          '0%, 100%': { boxShadow: '0 0 20px rgba(0, 212, 170, 0.3)' },
+          '50%': { boxShadow: '0 0 40px rgba(0, 212, 170, 0.6)' },
+        },
+        float: {
+          '0%, 100%': { transform: 'translateY(0)' },
+          '50%': { transform: 'translateY(-20px)' },
+        },
+        gradientShift: {
+          '0%': { backgroundPosition: '0% 50%' },
+          '50%': { backgroundPosition: '100% 50%' },
+          '100%': { backgroundPosition: '0% 50%' },
+        },
+        dataFlow: {
+          '0%': { transform: 'translateY(-100%)' },
+          '100%': { transform: 'translateY(100%)' },
+        },
+      },
+    },
+  },
+  plugins: [],
+};

+ 36 - 0
tsconfig.json

@@ -0,0 +1,36 @@
+{
+  "compilerOptions": {
+    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "lib": [
+      "ES2020",
+      "DOM",
+      "DOM.Iterable"
+    ],
+    "module": "ESNext",
+    "skipLibCheck": true,
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "verbatimModuleSyntax": false,
+    "moduleDetection": "force",
+    "noEmit": true,
+    "jsx": "react-jsx",
+    "strict": false,
+    "noUnusedLocals": false,
+    "noUnusedParameters": false,
+    "noFallthroughCasesInSwitch": false,
+    "noUncheckedSideEffectImports": false,
+    "forceConsistentCasingInFileNames": false,
+    "baseUrl": "./",
+    "paths": {
+      "@/*": [
+        "./src/*"
+      ]
+    }
+  },
+  "include": [
+    "src",
+    "api"
+  ]
+}

+ 23 - 0
vite.config.ts

@@ -0,0 +1,23 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import tsconfigPaths from "vite-tsconfig-paths";
+
+export default defineConfig({
+  server: {
+    port: 6077,
+    strictPort: false,
+  },
+  build: {
+    sourcemap: 'hidden',
+  },
+  plugins: [
+    react({
+      babel: {
+        plugins: [
+          'react-dev-locator',
+        ],
+      },
+    }),
+    tsconfigPaths()
+  ],
+})

Некоторые файлы не были показаны из-за большого количества измененных файлов