commit faff32475f2c38870a5acc7c939399a4e999dd29 Author: siujamo Date: Thu Dec 25 16:12:01 2025 +0800 feat: 初始提交 diff --git a/.env b/.env new file mode 100644 index 0000000..e69de29 diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..e69de29 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a2f6b70 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Backend base URL +VITE_API_BASE_URL=/api +# Microsoft Entra ID Client ID +VITE_MSAL_CLIENT_ID=msal-client-id +# Microsoft Entra ID Tenant ID +VITE_MSAL_TENANT_ID=msal-tenant-id diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22f6f8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,105 @@ +### macOS +# General +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Linux +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# Metadata left by Dolphin file manager, which comes with KDE Plasma +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# Log files created by default by the nohup command +nohup.out + +### Windows + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### JetBrains IDE +# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea + +# Gradle and Maven with auto-import +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based HTTP Client +http-client.private.env.json + +# Node libraries +node_modules/ + +# Config +.env.local +.env.*.local diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000..7612d53 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,24 @@ +printWidth: 100 +tabWidth: 2 +useTabs: false +semi: false +singleQuote: false +quoteProps: as-needed +jsxSingleQuote: false +trailingComma: es5 +bracketSpacing: true +bracketSameLine: true +arrowParens: always +#rangeStart: 0 +#rangeEnd: n +#parser: +#filepath: +#requirePragma: false +#insertPragma: false +#proseWrap: preserve +htmlWhitespaceSensitivity: css +#vueIndentScriptAndStyle: false +endOfLine: lf +#embeddedLanguageFormatting: auto +# Enforce single attribute per line in HTML, Vue and JSX +#singleAttributePerLine: false \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..6f4a2f1 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,42 @@ +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" +import { globalIgnores } from "eslint/config" + +export default tseslint.config([ + globalIgnores(["dist"]), + { + files: ["**/*.{ts,tsx}"], + extends: [ + js.configs.recommended, + reactHooks.configs["recommended-latest"], + reactRefresh.configs.vite, + // Remove tseslint.configs.recommended and use this for stricter rules + ...tseslint.configs.strictTypeChecked, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + project: ["./tsconfig.node.json", "./tsconfig.app.json"], + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + "@typescript-eslint/no-unused-vars": 0, + "@typescript-eslint/no-non-null-assertion": 0, + "@typescript-eslint/restrict-template-expressions": 0, + "@typescript-eslint/no-unsafe-assignment": 0, + "@typescript-eslint/no-unsafe-call": 0, + "no-empty": 0, + "@typescript-eslint/no-unsafe-member-access": 0, + "no-empty-pattern": 0, + "@typescript-eslint/no-unnecessary-condition": 0, + "@typescript-eslint/no-confusing-void-expression": 0, + "@typescript-eslint/no-empty-object-type": 0, + "react-refresh/only-export-components": 0, + }, + }, +]) diff --git a/index.html b/index.html new file mode 100644 index 0000000..48e94cc --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Helix + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..dcbd0b8 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "helix-web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/cssinjs": "^2.0.1", + "@ant-design/icons": "^6.1.0", + "@azure/msal-browser": "^4.27.0", + "@azure/msal-react": "^3.0.23", + "@reduxjs/toolkit": "^2.11.1", + "@tailwindcss/vite": "^4.1.17", + "antd": "^6.1.0", + "axios": "^1.13.2", + "dayjs": "^1.11.19", + "libphonenumber-js": "^1.12.31", + "react": "^19.2.1", + "react-dom": "^19.2.1", + "react-redux": "^9.2.0", + "react-router": "^7.10.1", + "react-router-dom": "^7.10.1", + "redux-persist": "^6.0.0", + "tailwindcss": "^4.1.17" + }, + "devDependencies": { + "@types/node": "^20.19.26", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^4.7.0", + "globals": "^16.5.0", + "prettier": "^3.7.4", + "typescript": "^5.9.3", + "vite": "^7.2.7", + "vite-plugin-svgr": "^4.5.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..ead17ad --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3045 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@ant-design/cssinjs': + specifier: ^2.0.1 + version: 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@ant-design/icons': + specifier: ^6.1.0 + version: 6.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@azure/msal-browser': + specifier: ^4.27.0 + version: 4.27.0 + '@azure/msal-react': + specifier: ^3.0.23 + version: 3.0.23(@azure/msal-browser@4.27.0)(react@19.2.1) + '@reduxjs/toolkit': + specifier: ^2.11.1 + version: 2.11.1(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.1)(redux@5.0.1))(react@19.2.1) + '@tailwindcss/vite': + specifier: ^4.1.17 + version: 4.1.17(vite@7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)) + antd: + specifier: ^6.1.0 + version: 6.1.0(moment@2.30.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + axios: + specifier: ^1.13.2 + version: 1.13.2 + dayjs: + specifier: ^1.11.19 + version: 1.11.19 + libphonenumber-js: + specifier: ^1.12.31 + version: 1.12.31 + react: + specifier: ^19.2.1 + version: 19.2.1 + react-dom: + specifier: ^19.2.1 + version: 19.2.1(react@19.2.1) + react-redux: + specifier: ^9.2.0 + version: 9.2.0(@types/react@19.2.7)(react@19.2.1)(redux@5.0.1) + react-router: + specifier: ^7.10.1 + version: 7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react-router-dom: + specifier: ^7.10.1 + version: 7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + redux-persist: + specifier: ^6.0.0 + version: 6.0.0(react@19.2.1)(redux@5.0.1) + tailwindcss: + specifier: ^4.1.17 + version: 4.1.17 + devDependencies: + '@types/node': + specifier: ^20.19.26 + version: 20.19.26 + '@types/react': + specifier: ^19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@vitejs/plugin-react': + specifier: ^4.7.0 + version: 4.7.0(vite@7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)) + globals: + specifier: ^16.5.0 + version: 16.5.0 + prettier: + specifier: ^3.7.4 + version: 3.7.4 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^7.2.7 + version: 7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2) + vite-plugin-svgr: + specifier: ^4.5.0 + version: 4.5.0(rollup@4.53.3)(typescript@5.9.3)(vite@7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)) + +packages: + + '@ant-design/colors@8.0.0': + resolution: {integrity: sha512-6YzkKCw30EI/E9kHOIXsQDHmMvTllT8STzjMb4K2qzit33RW2pqCJP0sk+hidBntXxE+Vz4n1+RvCTfBw6OErw==} + + '@ant-design/cssinjs-utils@2.0.2': + resolution: {integrity: sha512-Mq3Hm6fJuQeFNKSp3+yT4bjuhVbdrsyXE2RyfpJFL0xiYNZdaJ6oFaE3zFrzmHbmvTd2Wp3HCbRtkD4fU+v2ZA==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + '@ant-design/cssinjs@2.0.1': + resolution: {integrity: sha512-Lw1Z4cUQxdMmTNir67gU0HCpTl5TtkKCJPZ6UBvCqzcOTl/QmMFB6qAEoj8qFl0CuZDX9qQYa3m9+rEKfaBSbA==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/fast-color@3.0.0': + resolution: {integrity: sha512-eqvpP7xEDm2S7dUzl5srEQCBTXZMmY3ekf97zI+M2DHOYyKdJGH0qua0JACHTqbkRnD/KHFQP9J1uMJ/XWVzzA==} + engines: {node: '>=8.x'} + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@ant-design/icons@6.1.0': + resolution: {integrity: sha512-KrWMu1fIg3w/1F2zfn+JlfNDU8dDqILfA5Tg85iqs1lf8ooyGlbkA+TkwfOKKgqpUmAiRY1PTFpuOU2DAIgSUg==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/react-slick@2.0.0': + resolution: {integrity: sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==} + peerDependencies: + react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@azure/msal-browser@4.27.0': + resolution: {integrity: sha512-bZ8Pta6YAbdd0o0PEaL1/geBsPrLEnyY/RDWqvF1PP9RUH8EMLvUMGoZFYS6jSlUan6KZ9IMTLCnwpWWpQRK/w==} + engines: {node: '>=0.8.0'} + + '@azure/msal-common@15.13.3': + resolution: {integrity: sha512-shSDU7Ioecya+Aob5xliW9IGq1Ui8y4EVSdWGyI1Gbm4Vg61WpP95LuzcY214/wEjSn6w4PZYD4/iVldErHayQ==} + engines: {node: '>=0.8.0'} + + '@azure/msal-react@3.0.23': + resolution: {integrity: sha512-tHvq441nwlJD9QfQP4ZStiw6xb2hQoujNHZhZb+wpUbImb3wyr2FF6/umhX/p+yzc/aq0Lee7mbdDDpzRZzxcA==} + engines: {node: '>=10'} + peerDependencies: + '@azure/msal-browser': ^4.27.0 + react: ^16.8.0 || ^17 || ^18 || ^19.2.1 + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@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] + + '@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==} + + '@rc-component/async-validator@5.0.4': + resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} + engines: {node: '>=14.x'} + + '@rc-component/cascader@1.9.0': + resolution: {integrity: sha512-2jbthe1QZrMBgtCvNKkJFjZYC3uKl4N/aYm5SsMvO3T+F+qRT1CGsSM9bXnh1rLj7jDk/GK0natShWF/jinhWQ==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/checkbox@1.0.1': + resolution: {integrity: sha512-08yTH8m+bSm8TOqbybbJ9KiAuIATti6bDs2mVeSfu4QfEnyeF6X0enHVvD1NEAyuBWEAo56QtLe++MYs2D9XiQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/collapse@1.1.2': + resolution: {integrity: sha512-ilBYk1dLLJHu5Q74dF28vwtKUYQ42ZXIIDmqTuVy4rD8JQVvkXOs+KixVNbweyuIEtJYJ7+t+9GVD9dPc6N02w==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/color-picker@3.0.3': + resolution: {integrity: sha512-V7gFF9O7o5XwIWafdbOtqI4BUUkEUkgdBwp6favy3xajMX/2dDqytFaiXlcwrpq6aRyPLp5dKLAG5RFKLXMeGA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/context@2.0.1': + resolution: {integrity: sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/dialog@1.5.1': + resolution: {integrity: sha512-by4Sf/a3azcb89WayWuwG19/Y312xtu8N81HoVQQtnsBDylfs+dog98fTAvLinnpeoWG52m/M7QLRW6fXR3l1g==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/drawer@1.3.0': + resolution: {integrity: sha512-rE+sdXEmv2W25VBQ9daGbnb4J4hBIEKmdbj0b3xpY+K7TUmLXDIlSnoXraIbFZdGyek9WxxGKK887uRnFgI+pQ==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/dropdown@1.0.2': + resolution: {integrity: sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==} + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + '@rc-component/form@1.4.0': + resolution: {integrity: sha512-C8MN/2wIaW9hSrCCtJmcgCkWTQNIspN7ARXLFA4F8PGr8Qxk39U5pS3kRK51/bUJNhb/fEtdFnaViLlISGKI2A==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/image@1.5.3': + resolution: {integrity: sha512-/NR7QW9uCN8Ugar+xsHZOPvzPySfEhcW2/vLcr7VPRM+THZMrllMRv7LAUgW7ikR+Z67Ab67cgPp5K5YftpJsQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input-number@1.6.2': + resolution: {integrity: sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input@1.1.2': + resolution: {integrity: sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/mentions@1.6.0': + resolution: {integrity: sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/menu@1.2.0': + resolution: {integrity: sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mini-decimal@1.1.0': + resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} + engines: {node: '>=8.x'} + + '@rc-component/motion@1.1.6': + resolution: {integrity: sha512-aEQobs/YA0kqRvHIPjQvOytdtdRVyhf/uXAal4chBjxDu6odHckExJzjn2D+Ju1aKK6hx3pAs6BXdV9+86xkgQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mutate-observer@2.0.1': + resolution: {integrity: sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/notification@1.2.0': + resolution: {integrity: sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/overflow@1.0.0': + resolution: {integrity: sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/pagination@1.2.0': + resolution: {integrity: sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/picker@1.8.0': + resolution: {integrity: sha512-ek4efrIy+peC8WFJg6Lg7c+WNkykr+wUGQGBNoKmlF0K752aIJuaPcBj6p8CceT9vSJ9gOeeclQCBQIFWVDk1A==} + engines: {node: '>=12.x'} + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + react: '>=16.9.0' + react-dom: '>=16.9.0' + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + '@rc-component/portal@2.0.1': + resolution: {integrity: sha512-46KYuA7Udb1LAaLIdDrfmDz3wzyeEZxIURJCn+heoQVbhtW5PQkhBSQtRus+DUdsknmTFQulxSnqrbX3CI4yXw==} + engines: {node: '>=12.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/progress@1.0.2': + resolution: {integrity: sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/qrcode@1.1.1': + resolution: {integrity: sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/rate@1.0.1': + resolution: {integrity: sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/resize-observer@1.0.1': + resolution: {integrity: sha512-r+w+Mz1EiueGk1IgjB3ptNXLYSLZ5vnEfKHH+gfgj7JMupftyzvUUl3fRcMZe5uMM04x0n8+G2o/c6nlO2+Wag==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/segmented@1.2.3': + resolution: {integrity: sha512-L7G4S6zUpqHclOXK0wKKN2/VyqHa9tfDNxkoFjWOTPtQ0ROFaBwZhbf1+9sdZfIFkxJkpcShAmDOMEIBaFFqkw==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/select@1.3.4': + resolution: {integrity: sha512-NKhzahL/lXk3aKtmeH5W/jIqaPKcx9QiFXOvJxKe8eiuusIcSCW+XvJdjY3nRvCpTZCZDp7e1RaCU95gohx6Ow==} + engines: {node: '>=8.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/slider@1.0.1': + resolution: {integrity: sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/steps@1.2.2': + resolution: {integrity: sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/switch@1.0.3': + resolution: {integrity: sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/table@1.9.0': + resolution: {integrity: sha512-cq3P9FkD+F3eglkFYhBuNlHclg+r4jY8+ZIgK7zbEFo6IwpnA77YL/Gq4ensLw9oua3zFCTA6JDu6YgBei0TxA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tabs@1.7.0': + resolution: {integrity: sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/textarea@1.1.2': + resolution: {integrity: sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tooltip@1.4.0': + resolution: {integrity: sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tour@2.2.1': + resolution: {integrity: sha512-BUCrVikGJsXli38qlJ+h2WyDD6dYxzDA9dV3o0ij6gYhAq6ooT08SUMWOikva9v4KZ2BEuluGl5bPcsjrSoBgQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tree-select@1.4.0': + resolution: {integrity: sha512-I3UAlO2hNqy9CSKc8EBaESgnmKk2QaRzuZ2XHZGFCgsSMkGl06mdF97sVfROM02YIb64ocgLKefsjE0Ch4ocwQ==} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/tree@1.1.0': + resolution: {integrity: sha512-HZs3aOlvFgQdgrmURRc/f4IujiNBf4DdEeXUlkS0lPoLlx9RoqsZcF0caXIAMVb+NaWqKtGQDnrH8hqLCN5zlA==} + engines: {node: '>=10.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/trigger@3.7.1': + resolution: {integrity: sha512-+YNP8FywxKJpdqzlAp6TN8UbSK6YsQtIs3kI13mHfm87qi3qUd5Q9AGW8Unfv76kXFUSu7U7D0FygRsGH+6MiA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/upload@1.1.0': + resolution: {integrity: sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/util@1.6.0': + resolution: {integrity: sha512-YbjuIVAm8InCnXVoA4n6G+uh31yESTxQ6fSY2frZ2/oMSvktoB+bumFUfNN7RKh7YeOkZgOvN2suGtEDhJSX0A==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/virtual-list@1.0.2': + resolution: {integrity: sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@reduxjs/toolkit@2.11.1': + resolution: {integrity: sha512-HjhlEREguAyBTGNzRlGNiDHGQ2EjLSPWwdhhpoEqHYy8hWak3Dp6/fU72OfqVsiMb8S6rbfPsWUF24fxpilrVA==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@tailwindcss/node@4.1.17': + resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} + + '@tailwindcss/oxide-android-arm64@4.1.17': + resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.17': + resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.17': + resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.17': + resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.17': + resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.17': + resolution: {integrity: sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@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.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@20.19.26': + resolution: {integrity: sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@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 + + antd@6.1.0: + resolution: {integrity: sha512-RIe4W5saaL9SWgvqCcvz6LZta/KwT50B0YF7xYiWVZh0Gqfw2rJAsOMcp202Hxgm+YiyoSp4QqqvexKhuGGarw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + baseline-browser-mapping@2.9.5: + resolution: {integrity: sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA==} + hasBin: true + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001760: + resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + 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'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + 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'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + 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'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + immer@11.0.1: + resolution: {integrity: sha512-naDCyggtcBWANtIrjQEajhhBEuL9b0Zg4zmlWK2CzS6xCWSE39/vvf4LqnMjUAWHBhot4m9MHCM/Z+mfWhUkiA==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-mobile@5.0.0: + resolution: {integrity: sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json2mq@0.2.0: + resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + libphonenumber-js@1.12.31: + resolution: {integrity: sha512-Z3IhgVgrqO1S5xPYM3K5XwbkDasU67/Vys4heW+lfSBALcUZjeIIzI8zCLifY+OCzSq+fpDdywMDa7z+4srJPQ==} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + engines: {node: '>=14'} + hasBin: true + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + react-dom@19.2.1: + resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==} + peerDependencies: + react: ^19.2.1 + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@7.10.1: + resolution: {integrity: sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.10.1: + resolution: {integrity: sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.1: + resolution: {integrity: sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==} + engines: {node: '>=0.10.0'} + + redux-persist@6.0.0: + resolution: {integrity: sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==} + peerDependencies: + react: '>=16' + redux: '>4.0.0' + peerDependenciesMeta: + react: + optional: true + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + tailwindcss@4.1.17: + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vite-plugin-svgr@4.5.0: + resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==} + peerDependencies: + vite: '>=2.6.0' + + vite@7.2.7: + resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + 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 + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + +snapshots: + + '@ant-design/colors@8.0.0': + dependencies: + '@ant-design/fast-color': 3.0.0 + + '@ant-design/cssinjs-utils@2.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@ant-design/cssinjs': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@babel/runtime': 7.28.4 + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@ant-design/cssinjs@2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + csstype: 3.2.3 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + stylis: 4.3.6 + + '@ant-design/fast-color@3.0.0': {} + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@6.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@ant-design/colors': 8.0.0 + '@ant-design/icons-svg': 4.4.2 + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@ant-design/react-slick@2.0.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@babel/runtime': 7.28.4 + clsx: 2.1.1 + json2mq: 0.2.0 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + throttle-debounce: 5.0.2 + + '@azure/msal-browser@4.27.0': + dependencies: + '@azure/msal-common': 15.13.3 + + '@azure/msal-common@15.13.3': {} + + '@azure/msal-react@3.0.23(@azure/msal-browser@4.27.0)(react@19.2.1)': + dependencies: + '@azure/msal-browser': 4.27.0 + react: 19.2.1 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@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.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@emotion/hash@0.8.0': {} + + '@emotion/unitless@0.7.5': {} + + '@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 + + '@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 + + '@rc-component/async-validator@5.0.4': + dependencies: + '@babel/runtime': 7.28.4 + + '@rc-component/cascader@1.9.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/select': 1.3.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/tree': 1.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/checkbox@1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/collapse@1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/color-picker@3.0.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@ant-design/fast-color': 3.0.0 + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/context@2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/dialog@1.5.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/portal': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/drawer@1.3.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/portal': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/dropdown@1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/form@1.4.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/async-validator': 5.0.4 + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/image@1.5.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/portal': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/input-number@1.6.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/mini-decimal': 1.1.0 + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/input@1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/mentions@1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/menu': 1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/textarea': 1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/menu@1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/overflow': 1.0.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/mini-decimal@1.1.0': + dependencies: + '@babel/runtime': 7.28.4 + + '@rc-component/motion@1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/mutate-observer@2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/notification@1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/overflow@1.0.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/pagination@1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/picker@1.8.0(dayjs@1.11.19)(moment@2.30.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/overflow': 1.0.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + dayjs: 1.11.19 + moment: 2.30.1 + + '@rc-component/portal@2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/progress@1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/qrcode@1.1.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@babel/runtime': 7.28.4 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/rate@1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/resize-observer@1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/segmented@1.2.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/select@1.3.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/overflow': 1.0.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/slider@1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/steps@1.2.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/switch@1.0.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/table@1.9.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/context': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/tabs@1.7.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/dropdown': 1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/menu': 1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/textarea@1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/tooltip@1.4.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/tour@2.2.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/portal': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/tree-select@1.4.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/select': 1.3.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/tree': 1.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/tree@1.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/trigger@3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/portal': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/upload@1.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@rc-component/util@1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + is-mobile: 5.0.0 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + react-is: 18.3.1 + + '@rc-component/virtual-list@1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + + '@reduxjs/toolkit@2.11.1(react-redux@9.2.0(@types/react@19.2.7)(react@19.2.1)(redux@5.0.1))(react@19.2.1)': + dependencies: + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 11.0.1 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.2.1 + react-redux: 9.2.0(@types/react@19.2.7)(react@19.2.1)(redux@5.0.1) + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/pluginutils@5.3.0(rollup@4.53.3)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.53.3 + + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + + '@rollup/rollup-android-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@svgr/babel-preset@8.1.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.5) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.5) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.5) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.5) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.5) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.5) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.5) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.5) + + '@svgr/core@8.1.0(typescript@5.9.3)': + dependencies: + '@babel/core': 7.28.5 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.5) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.28.5 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': + dependencies: + '@babel/core': 7.28.5 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.5) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@tailwindcss/node@4.1.17': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.17 + + '@tailwindcss/oxide-android-arm64@4.1.17': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.17': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.17': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + optional: true + + '@tailwindcss/oxide@4.1.17': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-x64': 4.1.17 + '@tailwindcss/oxide-freebsd-x64': 4.1.17 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-x64-musl': 4.1.17 + '@tailwindcss/oxide-wasm32-wasi': 4.1.17 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 + + '@tailwindcss/vite@4.1.17(vite@7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@tailwindcss/node': 4.1.17 + '@tailwindcss/oxide': 4.1.17 + tailwindcss: 4.1.17 + vite: 7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2) + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@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.28.5 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.5 + + '@types/estree@1.0.8': {} + + '@types/node@20.19.26': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.3(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + + '@types/use-sync-external-store@0.0.6': {} + + '@vitejs/plugin-react@4.7.0(vite@7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2) + transitivePeerDependencies: + - supports-color + + antd@6.1.0(moment@2.30.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1): + dependencies: + '@ant-design/colors': 8.0.0 + '@ant-design/cssinjs': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@ant-design/cssinjs-utils': 2.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@ant-design/fast-color': 3.0.0 + '@ant-design/icons': 6.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@ant-design/react-slick': 2.0.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@babel/runtime': 7.28.4 + '@rc-component/cascader': 1.9.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/checkbox': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/collapse': 1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/color-picker': 3.0.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/dialog': 1.5.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/drawer': 1.3.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/dropdown': 1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/form': 1.4.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/image': 1.5.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/input': 1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/input-number': 1.6.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/mentions': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/menu': 1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/motion': 1.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/mutate-observer': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/notification': 1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/pagination': 1.2.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/picker': 1.8.0(dayjs@1.11.19)(moment@2.30.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/progress': 1.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/qrcode': 1.1.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/rate': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/segmented': 1.2.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/select': 1.3.4(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/slider': 1.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/steps': 1.2.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/switch': 1.0.3(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/table': 1.9.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/tabs': 1.7.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/textarea': 1.1.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/tooltip': 1.4.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/tour': 2.2.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/tree': 1.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/tree-select': 1.4.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/trigger': 3.7.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/upload': 1.1.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@rc-component/util': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + clsx: 2.1.1 + dayjs: 1.11.19 + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + + argparse@2.0.1: {} + + asynckit@0.4.0: {} + + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + baseline-browser-mapping@2.9.5: {} + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.5 + caniuse-lite: 1.0.30001760 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.2(browserslist@4.28.1) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001760: {} + + clsx@2.1.1: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + compute-scroll-into-view@3.1.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + csstype@3.2.3: {} + + dayjs@1.11.19: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + delayed-stream@1.0.0: {} + + detect-libc@2.1.2: {} + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.267: {} + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + entities@4.5.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + 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: {} + + estree-walker@2.0.2: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + globals@16.5.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + immer@11.0.1: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + is-arrayish@0.2.1: {} + + is-mobile@5.0.0: {} + + jiti@2.6.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + + json5@2.2.3: {} + + libphonenumber-js@1.12.31: {} + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + lines-and-columns@1.2.4: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + moment@2.30.1: + optional: true + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-releases@2.0.27: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.7.4: {} + + proxy-from-env@1.1.0: {} + + react-dom@19.2.1(react@19.2.1): + dependencies: + react: 19.2.1 + scheduler: 0.27.0 + + react-is@18.3.1: {} + + react-redux@9.2.0(@types/react@19.2.7)(react@19.2.1)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.2.1 + use-sync-external-store: 1.6.0(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + redux: 5.0.1 + + react-refresh@0.17.0: {} + + react-router-dom@7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1): + dependencies: + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + react-router: 7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + + react-router@7.10.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1): + dependencies: + cookie: 1.1.1 + react: 19.2.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.1(react@19.2.1) + + react@19.2.1: {} + + redux-persist@6.0.0(react@19.2.1)(redux@5.0.1): + dependencies: + redux: 5.0.1 + optionalDependencies: + react: 19.2.1 + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + + reselect@5.1.1: {} + + resolve-from@4.0.0: {} + + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + + scheduler@0.27.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + semver@6.3.1: {} + + set-cookie-parser@2.7.2: {} + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + source-map-js@1.2.1: {} + + string-convert@0.2.1: {} + + stylis@4.3.6: {} + + svg-parser@2.0.4: {} + + tailwindcss@4.1.17: {} + + tapable@2.3.0: {} + + throttle-debounce@5.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + update-browserslist-db@1.2.2(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.6.0(react@19.2.1): + dependencies: + react: 19.2.1 + + vite-plugin-svgr@4.5.0(rollup@4.53.3)(typescript@5.9.3)(vite@7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)): + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + vite: 7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + + vite@7.2.7(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.26 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + + yallist@3.1.1: {} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..a8bc145 --- /dev/null +++ b/public/vite.svg @@ -0,0 +1,21 @@ + diff --git a/src/api/auth/index.ts b/src/api/auth/index.ts new file mode 100644 index 0000000..d750f62 --- /dev/null +++ b/src/api/auth/index.ts @@ -0,0 +1,68 @@ +import webClient from "@/service/web-client" +import { HttpStatus } from "@/constant" +import type { CaptchaResponse, UserAuthResponse } from "@/types/web/response" +import type { UsernamePasswordLoginRequest } from "@/types/web/request" + +/** + * 获取验证码图片及验证码 UUID + */ +async function getCaptcha(): Promise { + const { data, status } = await webClient.get("/captcha") + if (status == HttpStatus.OK) { + return data as CaptchaResponse + } else { + return null + } +} + +/** + * 使用用户名密码登录 + * @param request 用户名密码 + */ +async function usernamePasswordLogin( + request: UsernamePasswordLoginRequest +): Promise { + const { data } = await webClient.post("/auth/login", request) + return data +} + +/** + * 使用企业微信登录 + * @param code 由企业微信提供的身份验证 code + */ +async function wecomLogin(code: string): Promise { + const urlSearchParams = new URLSearchParams() + urlSearchParams.append("code", code) + + + const { data } = await webClient.get(`/auth/wecom/login?${urlSearchParams.toString()}`) + return data +} + +/** + * 使用 Microsoft Entra 登录 + * @param msalToken 由 Microsoft Entra 提供的用户身份令牌 + */ +async function msalLogin(msalToken: string): Promise { + const { data, headers } = await webClient.post(`/auth/msal/login`, { + msalToken, + }) + + const token = (headers as Record).authorization ?? "" + + if (!token) { + return Promise.reject(new Error("未获取到身份令牌,登录失败")) + } + + return data +} + +/** + * 获取注册功能是否启用 + */ +async function fetchRegisterEnabled() { + const { data } = await webClient.get("/auth/register-enabled") + return data +} + +export { usernamePasswordLogin, wecomLogin, msalLogin, getCaptcha, fetchRegisterEnabled } diff --git a/src/api/department/index.ts b/src/api/department/index.ts new file mode 100644 index 0000000..e7bd189 --- /dev/null +++ b/src/api/department/index.ts @@ -0,0 +1,13 @@ +import webClient from "@/service/web-client" +import type { TreeNode } from "@/types/tree" +import type { Department } from "@/types/entity" + +export async function fetchDepartmentTree(): Promise> { + const { data } = await webClient.get>("/departments/tree") + return data +} + +export async function fetchDepartments(): Promise { + const { data } = await webClient.get("/departments") + return data +} diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..5c1d525 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,6 @@ +export * as AuthApi from "./auth" +export * as MenuApi from "./menu" +export * as DeptApi from "./department" +export * as UserApi from "./user" +export * as PositionApi from "./position" +export * as RoleApi from "./role" diff --git a/src/api/menu/index.ts b/src/api/menu/index.ts new file mode 100644 index 0000000..4defb0e --- /dev/null +++ b/src/api/menu/index.ts @@ -0,0 +1,8 @@ +import webClient from "@/service/web-client" +import type { TreeNode } from "@/types/tree" +import type { MenuItem } from "@/types/entity" + +export async function fetchMenuTree() { + const { data } = await webClient.get[]>("/menus") + return data +} diff --git a/src/api/position/index.ts b/src/api/position/index.ts new file mode 100644 index 0000000..0c8a79b --- /dev/null +++ b/src/api/position/index.ts @@ -0,0 +1,9 @@ +import webClient from "@/service/web-client" +import type { QueryPositionRequest } from "@/types/web/request" +import type { PageResponse } from "@/types/web/response" +import type { Position } from "@/types/entity" + +export async function fetchPositions(request: QueryPositionRequest): Promise> { + const { data } = await webClient.get>("/positions") + return data +} diff --git a/src/api/role/index.ts b/src/api/role/index.ts new file mode 100644 index 0000000..df036ac --- /dev/null +++ b/src/api/role/index.ts @@ -0,0 +1,26 @@ +import type { QueryRoleRequest } from "@/types/web/request" +import webClient from "@/service/web-client" +import type { PageResponse, RoleResponse } from "@/types/web/response" + +export async function fetchRoles( + request: QueryRoleRequest | null +): Promise { + const params = new URLSearchParams() + params.append("pageNum", `${request?.pageNum ?? 1}`) + params.append("pageSize", `${request?.pageSize ?? 1}`) + + if (request?.name) { + params.append("name", request.name) + } + + if (request?.code) { + params.append("code", request.code) + } + + if (request?.status) { + params.append("status", request.status) + } + + const { data } = await webClient.get(`/roles?${params.toString()}`) + return data +} diff --git a/src/api/user/index.ts b/src/api/user/index.ts new file mode 100644 index 0000000..34adf46 --- /dev/null +++ b/src/api/user/index.ts @@ -0,0 +1,88 @@ +import webClient from "@/service/web-client" +import { getCountryCallingCode } from "libphonenumber-js" +import { getDefaultCountryCode } from "@/utils/phone-number-utils" +import type { AddUserRequest, EditUserRequest, QueryUserRequest } from "@/types/web/request" +import type { PageResponse, UserDetailResponse } from "@/types/web/response" +import type { UserFormValues } from "@/types/form" + +export async function fetchUsers( + request?: QueryUserRequest +): Promise> { + const urlSearchParam = new URLSearchParams() + urlSearchParam.append("pageNum", `${request?.pageNum ?? 1}`) + urlSearchParam.append("pageSize", `${request?.pageSize ?? 10}`) + + if (request?.departmentId) { + urlSearchParam.append("departmentId", `${request.departmentId}`) + } + + if (request?.username) { + urlSearchParam.append("username", request.username) + } + + if (request?.regionAbbreviation) { + urlSearchParam.append("regionAbbreviation", request.regionAbbreviation) + } + + if (request?.phoneNumber) { + urlSearchParam.append("phoneNumber", request.phoneNumber) + } + + if (request?.status) { + urlSearchParam.append("status", request.status) + } + + if (request?.createdAtStart) { + urlSearchParam.append("createdAtStart", request.createdAtStart) + } + + if (request?.createdAtEnd) { + urlSearchParam.append("createdAtEnd", request.createdAtEnd) + } + + const { data } = await webClient.get>( + `/users?${urlSearchParam.toString()}` + ) + return data +} + +export async function fetchUserById(userId: number): Promise { + const { data } = await webClient.get(`/users/${userId}`) + return data +} + +export async function addUser(values: UserFormValues) { + await webClient.post("/users", { + username: values.username, + password: values.password, + fullName: values.fullName, + email: values.email, + regionAbbreviation: values.regionAbbreviation ?? getDefaultCountryCode(), + phoneNumber: values.phoneNumber, + avatarUrl: values.avatarUrl, + status: values.status, + departmentId: values.departmentId, + positionId: values.positionId, + roleIds: [], + } as AddUserRequest) +} + +export async function editUser(values: UserFormValues) { + await webClient.put("/users", { + id: values.id, + username: values.username, + fullName: values.fullName, + email: values.email, + regionAbbreviation: values.regionAbbreviation ?? getDefaultCountryCode(), + phoneNumber: values.phoneNumber, + avatarUrl: values.avatarUrl, + status: values.status, + departmentId: values.departmentId, + positionId: values.positionId, + roleIds: [], + } as EditUserRequest) +} + +export async function deleteUser(userId: number | string) { + await webClient.delete(`/users/${userId}`) +} diff --git a/src/assets/dingtalk.svg b/src/assets/dingtalk.svg new file mode 100644 index 0000000..97fb5a3 --- /dev/null +++ b/src/assets/dingtalk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/discord.svg b/src/assets/discord.svg new file mode 100644 index 0000000..fb73caa --- /dev/null +++ b/src/assets/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/email.svg b/src/assets/email.svg new file mode 100644 index 0000000..f878707 --- /dev/null +++ b/src/assets/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/gitlab.svg b/src/assets/gitlab.svg new file mode 100644 index 0000000..e3c800e --- /dev/null +++ b/src/assets/gitlab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/google.svg b/src/assets/google.svg new file mode 100644 index 0000000..77cdb81 --- /dev/null +++ b/src/assets/google.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/lark.svg b/src/assets/lark.svg new file mode 100644 index 0000000..c26c6a7 --- /dev/null +++ b/src/assets/lark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..e958d9c --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/microsoft.svg b/src/assets/microsoft.svg new file mode 100644 index 0000000..89f6237 --- /dev/null +++ b/src/assets/microsoft.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/password.svg b/src/assets/password.svg new file mode 100644 index 0000000..7ceee90 --- /dev/null +++ b/src/assets/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/slack.svg b/src/assets/slack.svg new file mode 100644 index 0000000..2514288 --- /dev/null +++ b/src/assets/slack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/wecom.svg b/src/assets/wecom.svg new file mode 100644 index 0000000..91b6f30 --- /dev/null +++ b/src/assets/wecom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/add-user-dialogue/index.tsx b/src/components/add-user-dialogue/index.tsx new file mode 100644 index 0000000..f0d7898 --- /dev/null +++ b/src/components/add-user-dialogue/index.tsx @@ -0,0 +1,30 @@ +import { type FormInstance } from "antd" +import UserDisplayForm from "@/components/user-display-form" +import type { UserFormValues } from "@/types/form" + +interface AddUserProps { + form: FormInstance +} + +export default function AddUserDialogue({ form }: AddUserProps) { + return ( + + ) +} diff --git a/src/components/edit-user-dialogue/index.tsx b/src/components/edit-user-dialogue/index.tsx new file mode 100644 index 0000000..2703e62 --- /dev/null +++ b/src/components/edit-user-dialogue/index.tsx @@ -0,0 +1,22 @@ +import type { FormInstance } from "antd" +import UserDisplayForm from "@/components/user-display-form" +import type { User } from "@/types/entity" +import type { UserFormValues } from "@/types/form" + +interface EditUserProps { + user: User + form: FormInstance +} + +export default function EditUserDialogue({ user, form }: EditUserProps) { + return ( + + ) +} diff --git a/src/components/icon/index.tsx b/src/components/icon/index.tsx new file mode 100644 index 0000000..192dc40 --- /dev/null +++ b/src/components/icon/index.tsx @@ -0,0 +1,99 @@ +import type { ForwardRefExoticComponent } from "react" +import Icon from "@ant-design/icons" + +import BrandMicrosoft from "@/assets/microsoft.svg?react" +import BrandDingTalk from "@/assets/dingtalk.svg?react" +import BrandWeCom from "@/assets/wecom.svg?react" +import BrandLark from "@/assets/lark.svg?react" +import BrandSlack from "@/assets/slack.svg?react" +import BrandGoogle from "@/assets/google.svg?react" +import BrandGitlab from "@/assets/gitlab.svg?react" +import BrandEmail from "@/assets/email.svg?react" +import BrandDiscord from "@/assets/discord.svg?react" +import Logo from "@/assets/logo.svg?react" +import type { AntIconComponentProps } from "@/types/antd" + +/** + * Microsoft 图标 + * @param props Icon 属性 + * @constructor + */ +export function MicrosoftFilled(props: Partial) { + return } {...props} /> +} + +/** + * 钉钉图标 + * @param props Icon 属性 + * @constructor + */ +export function DingTalkFilled(props: Partial) { + return } {...props} /> +} + +/** + * 企业微信图标 + * @param props Icon 属性 + * @constructor + */ +export function WeComFilled(props: Partial) { + return } {...props} /> +} + +/** + * 飞书图标 + * @param props Icon 属性 + * @constructor + */ +export function LarkFilled(props: Partial) { + return } {...props} /> +} + +/** + * Slack 图标 + * @param props Icon 属性 + * @constructor + */ +export function SlackFilled(props: Partial) { + return } {...props} /> +} + +/** + * Google 图标 + * @param props Icon 属性 + * @constructor + */ +export function GoogleFilled(props: Partial) { + return } {...props} /> +} + +/** + * GitLab 图标 + * @param props Icon 属性 + * @constructor + */ +export function GitlabFilled(props: Partial) { + return } {...props} /> +} + +/** + * Email 图标 + * @param props Icon 属性 + * @constructor + */ +export function EmailFilled(props: Partial) { + return } {...props} /> +} + +/** + * Discord 图标 + * @param props Icon 属性 + * @constructor + */ +export function DiscordFilled(props: Partial) { + return } {...props} /> +} + +export function ApplicationLogo(props: Partial) { + return } {...props} /> +} diff --git a/src/components/loading-fallback/index.tsx b/src/components/loading-fallback/index.tsx new file mode 100644 index 0000000..fc1adb9 --- /dev/null +++ b/src/components/loading-fallback/index.tsx @@ -0,0 +1,5 @@ +import { Spin } from "antd" + +export default function LoadingFallback() { + return +} diff --git a/src/components/protected-route/index.tsx b/src/components/protected-route/index.tsx new file mode 100644 index 0000000..b93b92b --- /dev/null +++ b/src/components/protected-route/index.tsx @@ -0,0 +1,22 @@ +import { Navigate, Outlet, useLocation } from "react-router" +import { useAppSelector } from "@/store" +import DashboardLayout from "@/layouts/dashboard-layout" + +/** + * 需要身份验证的前置组件 + * @constructor + */ +export default function ProtectedRoute() { + const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated) + const location = useLocation() + + if (!isAuthenticated) { + return + } + + return ( + + + + ) +} diff --git a/src/components/user-display-form/index.tsx b/src/components/user-display-form/index.tsx new file mode 100644 index 0000000..69225bf --- /dev/null +++ b/src/components/user-display-form/index.tsx @@ -0,0 +1,200 @@ +import { useEffect, useMemo, useState } from "react" +import { App, Checkbox, Form, type FormInstance, Input, Select, Space, TreeSelect } from "antd" +import type { AxiosError } from "axios" +import { getCountries, getCountryCallingCode } from "libphonenumber-js" +import { DeptApi, PositionApi } from "@/api" +import type { UserFormValues } from "@/types/form" +import type { Department, Position } from "@/types/entity" +import type { GeneralErrorResponse } from "@/types/web/response" +import type { AntTreeSelectOption } from "@/types/antd" + +interface UserDisplayFormProps { + initialValues?: UserFormValues + isEditing: boolean + isAdding?: boolean + form: FormInstance +} + +const countryOptions = getCountries().map((country) => { + const callingCode = getCountryCallingCode(country) + return { + label: `${country} (+${callingCode})`, + value: country, + key: country, + } +}) + +function transformDepartmentToTree(departments: Department[]): AntTreeSelectOption[] { + const sortedDepartments = [...departments].sort((a, b) => a.sort - b.sort) + + const map: Record> = {} + const tree: AntTreeSelectOption[] = [] + + sortedDepartments.forEach((dept) => { + map[dept.id] = { + label: dept.name, + value: dept.id, + children: [], + } + }) + + sortedDepartments.forEach((dept) => { + const node = map[dept.id] + if (dept.parentId !== null && map[dept.parentId]) { + map[dept.parentId].children!.push(node) + } else { + tree.push(node) + } + }) + return tree +} + +export default function UserDisplayForm({ + initialValues, + isEditing, + form, + isAdding = false, +}: UserDisplayFormProps) { + // Get message wrapper from Antd + const { message } = App.useApp() + + // Build form values. + const initialFormValues: UserFormValues | undefined = initialValues + ? ({ + ...initialValues, + password: "", + } as UserFormValues) + : undefined + + // Department state used to save all departments + const [departments, setDepartments] = useState([]) + // Position state used to save all positions + const [positions, setPositions] = useState([]) + + // Departments options + const departmentOptions = useMemo(() => { + return transformDepartmentToTree(departments) + }, [departments]) + + const positionOptions = useMemo(() => { + return positions.map((position) => ({ + label: position.name, + value: position.id, + key: position.id, + })) + }, [positions]) + + // Initialise form values + useEffect(() => { + if (initialFormValues) { + form.setFieldsValue(initialFormValues) + } else { + form.resetFields() + } + }, [initialValues, form]) + + // Initialise department data on component mounted + useEffect(() => { + const fetchDepartmentsFuture = DeptApi.fetchDepartments() + const fetchPositionsFuture = PositionApi.fetchPositions({ pageNum: 1, pageSize: 999 }) + + Promise.all([fetchDepartmentsFuture, fetchPositionsFuture]) + .then(([departments, positionPage]) => { + setDepartments(departments) + setPositions(positionPage.content) + }) + .catch((error: unknown) => { + const err = error as AxiosError + void message.error(err.response?.data.message ?? "获取部门或岗位数据失败,请稍后再试") + }) + }, []) + + return ( + + form={form} + initialValues={initialFormValues} + layout="vertical" + labelAlign="right" + disabled={!isEditing}> + label="用户 ID" name="id" hidden={isAdding}> + + + + + label="全名" + name="fullName" + rules={[{ required: true, message: "用户全名不能为空" }]}> + + + + + label="用户名" + name="username" + rules={[{ required: true, message: "请输入用户名" }]}> + + + + {isAdding && ( + + label="密码" + name="password" + rules={[{ required: true, message: "请输入密码" }]}> + + + )} + + + label="电子邮箱" + name="email" + rules={[ + { type: "email", message: "邮箱格式不正确" }, + { required: true, message: "邮箱不能为空" }, + ]}> + + + + label="联系电话" required> + + + noStyle + name="regionAbbreviation" + rules={[{ required: true, message: "请选择国际电信区域码" }]}> + + + + + + + label="用户状态" + name="status" + rules={[{ required: true, message: "用户状态不能为空" }]}> + + + + ) +} diff --git a/src/config/dayjs-config/index.ts b/src/config/dayjs-config/index.ts new file mode 100644 index 0000000..b48e603 --- /dev/null +++ b/src/config/dayjs-config/index.ts @@ -0,0 +1,11 @@ +// 配置插件 +import dayjs from "dayjs" + +import duration from "dayjs/plugin/duration" +dayjs.extend(duration) + +// 配置语言 +import "dayjs/locale/zh-cn" +dayjs.locale("zh-cn") + +console.log("Global Dayjs plugins initialised.") diff --git a/src/config/msal-config/index.ts b/src/config/msal-config/index.ts new file mode 100644 index 0000000..81bfabb --- /dev/null +++ b/src/config/msal-config/index.ts @@ -0,0 +1,17 @@ +import { type Configuration, PublicClientApplication } from "@azure/msal-browser" + +const clientId = import.meta.env.VITE_MSAL_CLIENT_ID +const tenantId = import.meta.env.VITE_MSAL_TENANT_ID + +const msalConfig: Configuration = { + auth: { + clientId, + authority: `https://login.microsoftonline.com/${tenantId}`, + redirectUri: "http://localhost:5173" + }, + cache: { + cacheLocation: "localStorage", + } +} + +export const msalInstance = new PublicClientApplication(msalConfig) \ No newline at end of file diff --git a/src/constant/http-status/index.ts b/src/constant/http-status/index.ts new file mode 100644 index 0000000..8d79423 --- /dev/null +++ b/src/constant/http-status/index.ts @@ -0,0 +1,66 @@ +/** + * HTTP 状态码 + */ +export const HttpStatus = { + CONTINUE: 100, + SWITCHING_PROTOCOLS: 101, + PROCESSING: 102, + EARLY_HINTS: 103, + OK: 200, + CREATED: 201, + ACCEPTED: 202, + NON_AUTHORITATIVE_INFORMATION: 203, + NO_CONTENT: 204, + RESET_CONTENT: 205, + PARTIAL_CONTENT: 206, + MULTI_STATUS: 207, + ALREADY_REPORTED: 208, + IM_USED: 226, + MULTIPLE_CHOICES: 300, + MOVED_PERMANENTLY: 301, + FOUND: 302, + SEE_OTHER: 303, + NOT_MODIFIED: 304, + USE_PROXY: 305, + TEMPORARY_REDIRECT: 307, + PERMANENT_REDIRECT: 308, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + PAYMENT_REQUIRED: 402, + FORBIDDEN: 403, + NOT_FOUND: 404, + METHOD_NOT_ALLOWED: 405, + NOT_ACCEPTABLE: 406, + PROXY_AUTHENTICATION_REQUIRED: 407, + REQUEST_TIMEOUT: 408, + CONFLICT: 409, + GONE: 410, + LENGTH_REQUIRED: 411, + PRECONDITION_FAILED: 412, + PAYLOAD_TOO_LARGE: 413, + URI_TOO_LONG: 414, + UNSUPPORTED_MEDIA_TYPE: 415, + RANGE_NOT_SATISFIABLE: 416, + EXPECTATION_FAILED: 417, + I_AM_A_TEAPOT: 418, + UNPROCESSABLE_ENTITY: 422, + LOCKED: 423, + FAILED_DEPENDENCY: 424, + TOO_EARLY: 425, + UPGRADE_REQUIRED: 426, + PRECONDITION_REQUIRED: 428, + TOO_MANY_REQUESTS: 429, + REQUEST_HEADER_FIELDS_TOO_LARGE: 431, + UNAVAILABLE_FOR_LEGAL_REASONS: 451, + INTERNAL_SERVER_ERROR: 500, + NOT_IMPLEMENTED: 501, + BAD_GATEWAY: 502, + SERVICE_UNAVAILABLE: 503, + GATEWAY_TIMEOUT: 504, + HTTP_VERSION_NOT_SUPPORTED: 505, + VARIANT_ALSO_NEGOTIATES: 506, + INSUFFICIENT_STORAGE: 507, + LOOP_DETECTED: 508, + NOT_EXTENDED: 510, + NETWORK_AUTHENTICATION_REQUIRED: 511, +} diff --git a/src/constant/index.ts b/src/constant/index.ts new file mode 100644 index 0000000..4b4f38e --- /dev/null +++ b/src/constant/index.ts @@ -0,0 +1 @@ +export { HttpStatus } from "./http-status" diff --git a/src/hooks/index.tsx b/src/hooks/index.tsx new file mode 100644 index 0000000..bf22bea --- /dev/null +++ b/src/hooks/index.tsx @@ -0,0 +1,20 @@ +import { useMatches } from "react-router" +import type { BreadcrumbItemType } from "antd/es/breadcrumb/Breadcrumb" +import type { RouteHandle } from "@/types/route" + +export function useAntBreadcrumbs(): BreadcrumbItemType[] { + const matches = useMatches() + + return matches + .filter((match) => (match.handle as RouteHandle)?.label) + .map((match) => { + const handle = match.handle as RouteHandle + + const path = match.pathname + const title = handle.label || "" + + return { + title, + } + }) +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..8e47a54 --- /dev/null +++ b/src/index.css @@ -0,0 +1,13 @@ +@layer theme, base, antd, components, utilities; +@import "tailwindcss"; + +html, body { + margin: 0; + padding: 0; + height: 100vh; + overflow: hidden; +} + +#root { + height: 100%; +} diff --git a/src/layouts/dashboard-layout/index.tsx b/src/layouts/dashboard-layout/index.tsx new file mode 100644 index 0000000..3a18e6f --- /dev/null +++ b/src/layouts/dashboard-layout/index.tsx @@ -0,0 +1,134 @@ +import React, { useEffect, useState } from "react" +import { useNavigate } from "react-router" +import { Avatar, Breadcrumb, Dropdown, Layout, Menu, type MenuProps, Modal, Space } from "antd" +import { DownOutlined } from "@ant-design/icons" +import { ApplicationLogo } from "@/components/icon" +import { useAppDispatch, useAppSelector } from "@/store" +import { useAntBreadcrumbs } from "@/hooks" +import { logout } from "@/store/auth-slice" +import { MenuApi } from "@/api" +import type { AxiosError } from "axios" +import type { TreeNode } from "@/types/tree" +import type { MenuItem } from "@/types/entity" + +const { Header, Footer, Sider, Content } = Layout +type AntMenuItem = Required["items"][number] + +function transformMenuData(nodes: TreeNode[]): AntMenuItem[] { + if (!nodes || nodes.length === 0) { + return [] + } + + return nodes + .sort((a, b) => a.item.sort - b.item.sort) + .map((node) => { + const { item, children } = node + const hasChildren = children && children.length > 0 + + const menuItem: AntMenuItem = { + key: item.code, + label: item.name, + } + + if (hasChildren) { + // Append children + return { ...menuItem, children: transformMenuData(children) } + } + + return menuItem + }) +} + +export default function DashboardLayout({ children }: { children: React.ReactNode }) { + const user = useAppSelector((store) => store.auth.user!) + const dispatch = useAppDispatch() + const breadcrumbItems = useAntBreadcrumbs() + const navigate = useNavigate() + + const onLogout = ({ key }: { key: string }) => { + if (key == "logout") { + Modal.confirm({ + title: "确定要注销吗?", + okText: "确定", + cancelText: "取消", + onOk: () => { + dispatch(logout()) + void navigate("/login") + }, + maskClosable: false, + keyboard: false, + }) + } + } + + const [menuItems, setMenuItems] = useState([]) + + useEffect(() => { + MenuApi.fetchMenuTree() + .then((response) => { + setMenuItems(transformMenuData(response)) + }) + .catch((error: unknown) => { + const err = error as AxiosError + console.log(err) + }) + }, []) + + const dropDownMenuItems: MenuProps["items"] = [ + { + key: "logout", + danger: true, + label: "注销", + }, + ] + + return ( + +
+
+ + Onixbyte Hi-Tech Co., Ltd +
+ +
+ + + {user.fullName} + + + + +
+
+ + + { + console.log(`key = ${key}`) + switch (key) { + case "user-mgmt": + void navigate("/users") + break + case "role-mgmt": + void navigate("/roles") + break + case "menu-mgmt": + void navigate("/menus") + break + } + }} + /> + + + + {children} + + + + ) +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..aa00697 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,32 @@ +import { StrictMode } from "react" +import { createRoot } from "react-dom/client" +import { Provider as ReduxProvider } from "react-redux" +import { PersistGate } from "redux-persist/integration/react" +import { RouterProvider } from "react-router" +import { App as AntApp, ConfigProvider as AntConfigProvider } from "antd" +import { StyleProvider } from "@ant-design/cssinjs" +import AntSimplifiedChinese from "antd/locale/zh_CN" +import "./index.css" +import "@/config/dayjs-config" +import store, { persistor } from "@/store" +import router from "@/router" + +createRoot(document.getElementById("root")!).render( + + + + + + + + + + + + + +) diff --git a/src/page/error/index.tsx b/src/page/error/index.tsx new file mode 100644 index 0000000..8cadb5f --- /dev/null +++ b/src/page/error/index.tsx @@ -0,0 +1,33 @@ +import React from "react" +import { Link } from "react-router-dom" + +/** + * 携带错误信息的通用错误展示页 + * + * @param message 自定义的错误信息,默认为`发生了未知错误,请稍后再试` + * @constructor + */ +export default function ErrorPage({ message = "发生了未知错误,请稍后再试" }) { + return ( +
+
+
+
+
+

⚠️

+

+ 系统出现了一些异常。 +

+

{message}

+

我们已经知晓该问题并正在进行处理。

+ + 返回主页 + +
+
+ ) +} diff --git a/src/page/home/index.tsx b/src/page/home/index.tsx new file mode 100644 index 0000000..cec1b11 --- /dev/null +++ b/src/page/home/index.tsx @@ -0,0 +1,7 @@ +import { useAppSelector } from "@/store" + +export default function HomePage() { + const user = useAppSelector((store) => store.auth.user!) + + return <>Welcome to Helix, {user.fullName} +} diff --git a/src/page/login/index.tsx b/src/page/login/index.tsx new file mode 100644 index 0000000..f439e5b --- /dev/null +++ b/src/page/login/index.tsx @@ -0,0 +1,373 @@ +import { type MouseEvent, useCallback, useEffect, useState } from "react" +import { Link, useNavigate } from "react-router" +import { Form, Input, Button, Card, message, Divider } from "antd" +import dayjs from "dayjs" +import type { AxiosError } from "axios" +// import { useMsal } from "@azure/msal-react" +import { AuthApi } from "@/api" +import { useAppDispatch, useAppSelector } from "@/store" +import { loginSuccess, updateRegistrationEnabled } from "@/store/auth-slice" +import { + DingTalkFilled, + GoogleFilled, + LarkFilled, + MicrosoftFilled, + SlackFilled, + WeComFilled, + GitlabFilled, + DiscordFilled, + EmailFilled, +} from "@/components/icon" + +import { GithubFilled } from "@ant-design/icons" +import { fetchRegisterEnabled } from "@/api/auth" +import type { UsernamePasswordLoginRequest } from "@/types/web/request" +import type { CaptchaResponse, GeneralErrorResponse } from "@/types/web/response" + +export default function LoginPage() { + const registrationEnabled = useAppSelector((state) => state.auth.registrationEnabled) + const dispatch = useAppDispatch() + const navigate = useNavigate() + + // const msalContext = useMsal() + + const [messageApi, contextHolder] = message.useMessage() + const [form] = Form.useForm() + + // const [isLoading, setIsLoading] = useState(false) + const [hasCaptcha, setHasCaptcha] = useState(false) + const [captchaData, setCaptchaData] = useState() + + const fetchCaptcha = useCallback(async () => { + try { + const response = await AuthApi.getCaptcha() + if (response) { + setHasCaptcha(true) + setCaptchaData(response) + form.setFieldValue("uuid", response.uuid) + } else { + setHasCaptcha(false) + setCaptchaData(null) + form.setFieldValue("uuid", null) + } + } catch (error) { + console.error("Failed to fetch captcha due to an error:", error) + setHasCaptcha(false) + setCaptchaData(null) + form.setFieldValue("uuid", null) + } + }, [form]) + + useEffect(() => { + void fetchCaptcha() + }, [fetchCaptcha]) + + useEffect(() => { + void fetchRegisterEnabled().then((enabled) => { + dispatch(updateRegistrationEnabled(enabled)) + }) + }, [dispatch]) + + /** + * 用户名密码登录 + */ + const performLogin = useCallback( + async (values: UsernamePasswordLoginRequest) => { + try { + console.log("Login values:", values) + + const loginResponse = await AuthApi.usernamePasswordLogin(values) + if (loginResponse) { + dispatch( + loginSuccess({ + user: loginResponse.user, + token: loginResponse.accessToken, + }) + ) + messageApi.success("登录成功", dayjs.duration({ seconds: 3 }).asSeconds()) + await navigate("/") + } else { + messageApi.error("登录失败:服务器响应异常。") + } + } catch (errorInfo: unknown) { + const error = errorInfo as AxiosError + console.log(error) + messageApi.error( + error.response?.data.message ?? "登录失败,请稍后再试", + dayjs.duration({ seconds: 3 }).asSeconds() + ) + } + }, + [dispatch, navigate, messageApi] + ) + + /** + * 刷新验证码图片 + */ + const refreshCaptcha = (event: MouseEvent) => { + event.preventDefault() + void fetchCaptcha() + } + + /** + * 使用 Microsoft Entra ID 登录 + */ + const performMsalLogin = () => { + console.log("使用 Microsoft 账号登录") + // void doMsalLogin(msalContext.instance, dispatch, () => void navigate("/")) + } + + /** + * 使用 DingTalk 登录 + */ + const performDingTalkLogin = () => { + console.log("使用钉钉登录") + // todo implement this + } + + /** + * 使用 WeCom 登录 + */ + const performWeComLogin = () => { + console.log("使用企业微信登录") + // todo implement this + } + + /** + * 使用 Lark 登录 + */ + const performLarkLogin = () => { + console.log("使用飞书登录") + // todo implement this + } + + /** + * 使用 Slack 登录 + */ + const performSlackLogin = () => { + console.log("使用 Slack 登录") + // todo implement this + } + + /** + * 使用 Github 登录 + */ + const performGithubLogin = () => { + console.log("使用 GitHub 登录") + // todo implement this + } + + /** + * 使用 Google 登录 + */ + const performGoogleLogin = () => { + console.log("使用 Google 登录") + // todo implement this + } + + /** + * 使用 Gitlab 登录 + */ + const performGitlabLogin = () => { + console.log("使用 Gitlab 登录") + // todo implement this + } + + /** + * 使用 Discord 登录 + */ + const performDiscordLogin = () => { + console.log("使用 Discord 登录") + // todo implement this + } + + /** + * 使用 Email 登录 + */ + const performEmailLogin = () => { + console.log("使用 Email 登录") + // todo implement this + } + + return ( +
+ {contextHolder} + + {/* 背景装饰元素 */} +
+
+
+ + +

欢迎回来

+

请登录您的账户

+
+ } + className="w-full max-w-md shadow-2xl border-0 backdrop-blur-sm bg-white/90 relative z-10" + styles={{ + body: { + padding: "32px", + }, + }}> + + name="usernamePasswordLoginForm" + form={form} + onFinish={(values) => { + void performLogin(values) + }} + layout="vertical" + className="space-y-4"> + + label={用户名} + name="username" + rules={[{ required: true, message: "请输入用户名!" }]}> + + + + + label={密码} + name="password" + rules={[{ required: true, message: "请输入密码!" }]}> + + + + {hasCaptcha ? ( + <> + + label={验证码} + name="captcha" + rules={[{ required: true, message: "请输入验证码!" }]}> +
+ + {captchaData?.captcha && ( + 验证码 + )} +
+ + name="uuid" hidden> +
Placeholder
+ + + ) : null} + + + + + + + {registrationEnabled ? ( +
+ 还没有账号?立即注册 +
+ ) : null} + + + 第三方帐号登录 + + +
+
+ +
+

© 2024 Your Company. All rights reserved.

+
+ + + ) +} diff --git a/src/page/menu/index.tsx b/src/page/menu/index.tsx new file mode 100644 index 0000000..d466fd1 --- /dev/null +++ b/src/page/menu/index.tsx @@ -0,0 +1,7 @@ +export default function MenuPage() { + return ( + <> +
菜单管理
+ + ) +} diff --git a/src/page/not-found/index.tsx b/src/page/not-found/index.tsx new file mode 100644 index 0000000..664bb41 --- /dev/null +++ b/src/page/not-found/index.tsx @@ -0,0 +1,30 @@ +import { Link } from "react-router" + +/** + * General page not found page. + * @constructor + */ +export default function NotFoundPage() { + return ( +
+
+
+
+ +
+

404

+

找不到页面

+

+ 找不到您需要的页面,您是否输入了错误的地址? +

+ + 返回主页 + +
+
+ ) +} diff --git a/src/page/register/index.tsx b/src/page/register/index.tsx new file mode 100644 index 0000000..fcfe59c --- /dev/null +++ b/src/page/register/index.tsx @@ -0,0 +1,49 @@ +import { useEffect } from "react" +import { useNavigate } from "react-router" +import dayjs from "dayjs" +import type { AxiosError } from "axios" +import { message } from "antd" + +import { AuthApi } from "@/api" +import type { GeneralErrorResponse } from "@/types/web/response" + +export default function RegisterPage() { + const [messageApi, contextHolder] = message.useMessage() + const navigate = useNavigate() + + useEffect(() => { + let cancelled = false + + AuthApi.fetchRegisterEnabled() + .then((enabled) => { + if (!enabled && !cancelled) { + void messageApi + .error("注册功能暂未开放", dayjs.duration({ seconds: 3 }).asSeconds()) + .then(() => { + if (!cancelled) { + void navigate("/login") + } + }) + } + }) + .catch((reason: unknown) => { + if (cancelled) return + const error = reason as AxiosError + void messageApi.error(error.response?.data.message ?? "发生未知错误,请稍后再试") + }) + + return () => { + cancelled = true + } + }, [messageApi, navigate]) + + return ( +
+ {contextHolder} +
+
+
+
注册页
+
+ ) +} diff --git a/src/page/role/index.tsx b/src/page/role/index.tsx new file mode 100644 index 0000000..e8b4cf8 --- /dev/null +++ b/src/page/role/index.tsx @@ -0,0 +1,190 @@ +import { useEffect, useState } from "react" +import type { AxiosError } from "axios" +import { App, Button, Form, Input, Select, Space, Switch, Table } from "antd" +import type { Role } from "@/types/entity" +import { RoleApi } from "@/api" +import type { QueryRoleRequest } from "@/types/web/request" +import type { GeneralErrorResponse, RoleResponse } from "@/types/web/response" +import { + DeleteOutlined, + ExportOutlined, + ImportOutlined, + PlusOutlined, + SearchOutlined, + UndoOutlined, +} from "@ant-design/icons" +import type { QueryRoleForm } from "@/types/form" +import type { Status } from "@/types/constant" + +export default function RolePage() { + const { message } = App.useApp() + + const [queryForm] = Form.useForm() + const [roles, setRoles] = useState([]) + const [pageNum, setPageNum] = useState(1) + const [pageSize, setPageSize] = useState(10) + const [totalElementCount, setTotalElementCount] = useState(0) + + const queryRoles = (pageNum: number, pageSize: number, queryRoleForm: QueryRoleForm | null) => { + const queryRoleRequest: QueryRoleRequest = { + code: queryRoleForm?.qCode ?? null, + name: queryRoleForm?.qName ?? null, + status: queryRoleForm?.qStatus ?? null, + pageNum, + pageSize, + } + + RoleApi.fetchRoles(queryRoleRequest) + .then((response) => { + console.log("role response", response) + setPageNum(response.pageable.pageNumber + 1) + setPageSize(response.pageable.pageSize) + setTotalElementCount(response.totalElements) + setRoles(response.content.sort((role1, role2) => role1.sort - role2.sort)) + }) + .catch((error) => { + const err = error as AxiosError + void message.error(err.response?.data.message ?? "获取角色失败") + }) + } + + const changeRoleStatus = (roleId: number | string, checked: boolean) => { + const status: Status = checked ? "ACTIVE" : "INACTIVE" + setRoles((prevRoles) => + prevRoles.map((role) => (role.id === roleId ? { ...role, status } : role)) + ) + } + + useEffect(() => { + queryRoles(pageNum, pageSize, null) + }, [pageNum, pageSize]) + + return ( +
+ + form={queryForm} + className="mt-0 mb-6" + layout="inline" + labelAlign="right" + onFinish={(values) => { + queryRoles(pageNum, pageSize, values) + }} + onReset={() => { + queryRoles(pageNum, pageSize, null) + }}> + label="角色名称" name="qName"> + + + label="角色编码" name="qCode"> + + + label="角色状态" name="qStatus"> + + className="w-26" + options={[ + { + label: "已启用", + value: "ACTIVE", + }, + { + label: "未启用", + value: "INACTIVE", + }, + ]} + /> + + > + + + + + + + + + + + + + + + + columns={[ + { title: "角色编号", dataIndex: "id" }, + { title: "角色名称", dataIndex: "name" }, + { title: "角色编码", dataIndex: "code" }, + { title: "显示顺序", dataIndex: "sort" }, + { + title: "状态", + key: "status", + render: (role: Role) => ( + <> + changeRoleStatus(role.id, checked)} + /> + + ), + }, + { + title: "默认角色", + key: "defaultValue", + render: (role: Role) => ( + <> + + + ), + }, + { title: "创建时间", dataIndex: "createdAt" }, + { + title: "操作", + render: (role: Role) => ( + <> + + + + + + ), + key: "operations", + }, + ]} + dataSource={roles} + rowKey="id" + pagination={{ + current: pageNum, + total: totalElementCount, + pageSize: pageSize, + defaultCurrent: 1, + defaultPageSize: 10, + pageSizeOptions: [10, 25, 50], + showSizeChanger: true, + onShowSizeChange: (pageNum, pageSize) => { + console.log(`onSizeChange ==> pageNum = ${pageNum}, pageSize = ${pageSize}`) + setPageNum(pageNum) + setPageSize(pageSize) + }, + onChange: (pageNum, pageSize) => { + console.log(`onChange ==> pageNum = ${pageNum}, pageSize = ${pageSize}`) + setPageNum(pageNum) + setPageSize(pageSize) + }, + }} + /> +
+ ) +} diff --git a/src/page/user/index.tsx b/src/page/user/index.tsx new file mode 100644 index 0000000..963f7ff --- /dev/null +++ b/src/page/user/index.tsx @@ -0,0 +1,463 @@ +import { type Key, useEffect, useState } from "react" +import { + Avatar, + Button, + DatePicker, + Form, + Input, + Select, + Space, + Table, + Tag, + Tree, + type TreeDataNode, + Typography, +} from "antd" +import { App } from "antd" +import { type AxiosError } from "axios" +import { getCountries, getCountryCallingCode } from "libphonenumber-js" +import { DeptApi, UserApi } from "@/api" +import { PhoneNumberUtils, DepartmentUtils } from "@/utils" +import { + DeleteOutlined, + EditOutlined, + ExportOutlined, + ImportOutlined, + KeyOutlined, + PlusOutlined, + SearchOutlined, + UndoOutlined, +} from "@ant-design/icons" +import AddUserDialogue from "@/components/add-user-dialogue" +import EditUserDialogue from "@/components/edit-user-dialogue" +import type { QueryUserRequest } from "@/types/web/request" +import type { QueryUserForm, UserFormValues } from "@/types/form" +import type { GeneralErrorResponse, UserDetailResponse } from "@/types/web/response" +import type { Department } from "@/types/entity" +import type { TreeNode } from "@/types/tree" +import { useAppSelector } from "@/store" + +export default function UserPage() { + const { message, modal } = App.useApp() + + const user = useAppSelector((state) => state.auth.user!) + + const [queryForm] = Form.useForm() + const [addUserForm] = Form.useForm() + const [editUserForm] = Form.useForm() + const [department, setDepartment] = useState>() + const [departmentTree, setDepartmentTree] = useState([]) + const [selectedDepartment, setSelectedDepartment] = useState(1) + const [users, setUsers] = useState([]) + const [pageNum, setPageNum] = useState(1) + const [totalElementCount, setTotalElementCount] = useState(0) + const [pageSize, setPageSize] = useState(10) + + const onAddUserFinish = async () => { + try { + const values = await addUserForm.validateFields() + + await UserApi.addUser(values) + + void message.success(`用户 ${values.username} 创建成功`) + return true + } catch (error: unknown) { + console.log(error) + if (error instanceof Error && error.message.includes("Validation Failed")) { + return false + } + const err = error as AxiosError + void message.error(err.response?.data.message ?? "创建失败,请稍后再试") + return false + } + } + + const handleAddUser = () => { + modal + .confirm({ + title: "添加用户", + content: , + width: 600, + onOk: onAddUserFinish, + }) + .then( + () => { + const formValues = queryForm.getFieldsValue() + queryUsers(pageNum, pageSize, selectedDepartment!, formValues) + }, + () => { + console.error("用户取消添加用户") + } + ) + } + + const onEditUserFinish = async () => { + try { + const values = await editUserForm.validateFields() + + await UserApi.editUser(values) + + void message.success(`用户更新成功`) + return true + } catch (error: unknown) { + if (error instanceof Error && error.message.includes("Validation Failed")) { + return false + } + const err = error as AxiosError + void message.error(err.response?.data.message ?? "更新失败,请稍后再试") + return false + } + } + + const handleEditUser = (user: UserDetailResponse) => { + modal + .confirm({ + title: `编辑用户: ${user.username}`, + content: , + width: 600, + onOk: onEditUserFinish, + }) + .then( + () => { + const formValues = queryForm.getFieldsValue() + queryUsers(pageNum, pageSize, selectedDepartment, formValues) + }, + () => { + console.error("用户取消添加用户") + } + ) + } + + const handleDeleteUser = (user: UserDetailResponse) => { + const isLastElementOnPage = users.length === 1 && pageNum > 1 + modal.confirm({ + title: ( + <> + 确认删除用户 {user.username} 吗? + + ), + content: "删除后数据将无法恢复。", + okText: "删除", + cancelText: "取消", + okButtonProps: { danger: true }, + onOk: async () => { + try { + await UserApi.deleteUser(user.id) + void message.success(`用户 ${user.username} 删除成功`) + setTotalElementCount((count) => count - 1) + if (isLastElementOnPage) { + setPageNum((prevPage) => prevPage - 1) + } else { + queryUsers(pageNum, pageSize, selectedDepartment, queryForm.getFieldsValue()) + } + } catch (error: unknown) { + const err = error as AxiosError + void message.error(err.response?.data.message ?? "删除失败,请稍后再试") + } + }, + }) + } + + // Query users + const queryUsers = ( + pageNum: number, + pageSize: number, + departmentId: number, + formValues: QueryUserForm | null + ) => { + const queryUserRequest: QueryUserRequest = { + createdAtEnd: null, + createdAtStart: null, + phoneNumber: null, + regionAbbreviation: null, + status: null, + username: null, + pageNum, + pageSize, + departmentId, + } + + if (formValues && formValues.qUsername) { + queryUserRequest.username = formValues.qUsername + } + + if (formValues && formValues.qRegionAbbreviation) { + queryUserRequest.regionAbbreviation = formValues.qRegionAbbreviation + } + + if (formValues && formValues.qPhoneNumber) { + queryUserRequest.phoneNumber = formValues.qPhoneNumber + } + + if (formValues && formValues.qStatus) { + queryUserRequest.status = formValues.qStatus + } + + if (formValues && formValues.qCreatedAtStart) { + queryUserRequest.createdAtStart = formValues.qCreatedAtStart.format("YYYY-MM-DD HH:mm:ss") + } + + if (formValues && formValues.qCreatedAtEnd) { + queryUserRequest.createdAtEnd = formValues.qCreatedAtEnd.format("YYYY-MM-DD HH:mm:ss") + } + + console.log(`queryUsers ==> `, queryUserRequest) + + UserApi.fetchUsers(queryUserRequest) + .then((userResponse) => { + console.log(userResponse) + setUsers(userResponse.content) + setPageNum(userResponse.pageable.pageNumber + 1) + setPageSize(userResponse.pageable.pageSize) + setTotalElementCount(userResponse.totalElements) + }) + .catch((error: unknown) => { + const err = error as AxiosError + void message.error(err.response?.data.message ?? "发生未知错误,请稍后再试") + }) + } + + useEffect(() => { + DeptApi.fetchDepartmentTree() + .then((departmentResponse) => { + setDepartment(departmentResponse) + setDepartmentTree(DepartmentUtils.transformDepartmentData([departmentResponse])) + }) + .catch((error: unknown) => { + const err = error as AxiosError + void message.error(err.response?.data.message ?? "发生未知错误,请稍后再试") + }) + }, []) + + useEffect(() => { + const formValues = queryForm.getFieldsValue() + queryUsers(pageNum, pageSize, selectedDepartment, formValues) + }, [pageNum, pageSize, selectedDepartment]) + + const regionOptions = getCountries().map((country) => { + const callingCode = getCountryCallingCode(country) + return { + label: `${country} (+${callingCode})`, + value: country, + key: country, + } + }) + + const userStatusOptions = [ + { + label: "已启用", + value: "ACTIVE", + key: "ACTIVE", + }, + { + label: "已停用", + value: "INACTIVE", + key: "INACTIVE", + }, + { + label: "已禁用", + value: "LOCKED", + key: "LOCKED", + }, + ] + + return ( +
+ { + if (typeof selectedKey == "number") { + setSelectedDepartment(selectedKey) + } + }} + selectedKeys={[selectedDepartment]} + /> +
+ + className="mt-0 mb-6" + form={queryForm} + layout="inline" + labelAlign="right" + onFinish={(values) => { + console.log("Query User Form ==> ", values) + queryUsers(pageNum, pageSize, Number(selectedDepartment), values) + }} + onReset={() => { + queryUsers(pageNum, pageSize, Number(selectedDepartment), null) + }}> + label="用户名" name="qUsername"> + + + + label="用户状态" name="qStatus"> + + + + noStyle name="qPhoneNumber"> + + + + + + + + name="qCreatedAtStart"> + + + name="qCreatedAtEnd"> + + + + + + + + + + + + + + + + + + + + + + rowKey="id" + dataSource={users} + pagination={{ + current: pageNum, + total: totalElementCount, + pageSize: pageSize, + defaultCurrent: 1, + defaultPageSize: 10, + pageSizeOptions: [10, 25, 50], + showSizeChanger: true, + onShowSizeChange: (pageNum, pageSize) => { + console.log(`onSizeChange ==> pageNum = ${pageNum}, pageSize = ${pageSize}`) + setPageNum(pageNum) + setPageSize(pageSize) + }, + onChange: (pageNum, pageSize) => { + console.log(`onChange ==> pageNum = ${pageNum}, pageSize = ${pageSize}`) + setPageNum(pageNum) + setPageSize(pageSize) + }, + }} + columns={[ + { title: "用户 ID", dataIndex: "id" }, + { title: "用户名称", dataIndex: "username" }, + { title: "姓名", dataIndex: "fullName" }, + { title: "电子邮箱", dataIndex: "email" }, + { + title: "联系电话", + render: (user: UserDetailResponse) => { + return PhoneNumberUtils.formatInternationalPhoneNumber( + user.regionAbbreviation, + user.phoneNumber + ) + }, + key: "phoneNumber", + }, + { + title: "头像", + render: (user: UserDetailResponse) => { + return + }, + key: "avatarUrl", + }, + { + title: "用户状态", + render: (user: UserDetailResponse) => { + const colour = user.status == "ACTIVE" ? "green" : "red" + return ( + + {user.status} + + ) + }, + key: "status", + }, + { title: "所在部门", dataIndex: "departmentName" }, + { title: "用户岗位", dataIndex: "positionName" }, + { + title: "操作", + render: (value: UserDetailResponse) => { + return String(value.id) == String(user.id) ? ( + <> + ) : ( + + + + + + ) + }, + }, + ]} + /> +
+
+ ) +} diff --git a/src/router/index.tsx b/src/router/index.tsx new file mode 100644 index 0000000..5c38b51 --- /dev/null +++ b/src/router/index.tsx @@ -0,0 +1,86 @@ +import type { ComponentType } from "react" +import { createBrowserRouter } from "react-router-dom" +import ErrorPage from "@/page/error" +import LoadingFallback from "@/components/loading-fallback" + +/** + * 懒加载组件 + * @param importer + */ +function lazyLoading }>(importer: () => Promise) { + return async () => { + const module = await importer() + return { + Component: module.default, + } + } +} + +const router = createBrowserRouter([ + { + path: "/login", + lazy: lazyLoading(() => import("@/page/login")), + handle: { + label: "用户登录", + }, + hydrateFallbackElement: , + }, + { + path: "/register", + lazy: lazyLoading(() => import("@/page/register")), + handle: { + label: "用户注册", + }, + hydrateFallbackElement: , + }, + { + path: "/", + lazy: lazyLoading(() => import("@/components/protected-route")), + errorElement: , + handle: { + label: "控制台", + }, + hydrateFallbackElement: , + children: [ + { + index: true, + lazy: lazyLoading(() => import("@/page/home")), + handle: { + label: "用户主页", + }, + hydrateFallbackElement: , + }, + { + path: "users", + lazy: lazyLoading(() => import("@/page/user")), + handle: { + label: "用户管理", + }, + hydrateFallbackElement: , + }, + { + path: "roles", + lazy: lazyLoading(() => import("@/page/role")), + handle: { + label: "角色管理", + }, + hydrateFallbackElement: , + }, + { + path: "menus", + lazy: lazyLoading(() => import("@/page/menu")), + handle: { + label: "菜单管理", + }, + hydrateFallbackElement: , + }, + ], + }, + { + path: "*", + lazy: lazyLoading(() => import("@/page/not-found")), + hydrateFallbackElement: , + }, +]) + +export default router diff --git a/src/service/auth/index.ts b/src/service/auth/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/service/auth/msal/index.ts b/src/service/auth/msal/index.ts new file mode 100644 index 0000000..4eec775 --- /dev/null +++ b/src/service/auth/msal/index.ts @@ -0,0 +1,29 @@ +import type { IPublicClientApplication } from "@azure/msal-browser" +import * as AuthApi from "@/api/auth" +import type { AppDispatch } from "@/store" +import { loginSuccess } from "@/store/auth-slice" + +/** + * Login with Microsoft Entra ID. + * + * @param instance Microsoft Entra ID application instance + * @param dispatch app dispatcher + * @param onSuccess callback when login succeeded + */ +export async function doMsalLogin( + instance: IPublicClientApplication, + dispatch: AppDispatch, + onSuccess?: () => void +) { + try { + const response = await instance.loginPopup({ + scopes: ["openid", "profile", "email"], + }) + + const { accessToken, user } = await AuthApi.msalLogin(response.idToken) + dispatch(loginSuccess({ user, token: accessToken })) + if (onSuccess) onSuccess() + } catch (err) { + console.error("MSAL login failed", err) + } +} diff --git a/src/service/web-client/index.ts b/src/service/web-client/index.ts new file mode 100644 index 0000000..5e933ba --- /dev/null +++ b/src/service/web-client/index.ts @@ -0,0 +1,38 @@ +import axios, { type AxiosError } from "axios" +import dayjs from "dayjs" +import store from "@/store" +import type { GeneralErrorResponse } from "@/types" +import { HttpStatus } from "@/constant" +import { logout } from "@/store/auth-slice" + +const webClient = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL, + timeout: dayjs.duration({ seconds: 10 }).asMilliseconds(), +}) + +webClient.interceptors.request.use( + (config) => { + const state = store.getState() + if (state.auth.isAuthenticated) { + config.headers["Authorization"] = `Bearer ${state.auth.token!}` + } + + return config + }, + (error: unknown) => { + console.log(error) + return Promise.reject(new Error("请求错误,请稍后再试")) + } +) + +webClient.interceptors.response.use((response) => { + return response +}, (error: unknown) => { + const err = error as AxiosError + if (err.response?.status == HttpStatus.UNAUTHORIZED) { + store.dispatch(logout()) + } + return Promise.reject(error as AxiosError) +}) + +export default webClient diff --git a/src/store/auth-slice/index.ts b/src/store/auth-slice/index.ts new file mode 100644 index 0000000..0c58e44 --- /dev/null +++ b/src/store/auth-slice/index.ts @@ -0,0 +1,46 @@ +import { createSlice, type PayloadAction } from "@reduxjs/toolkit" +import type { User } from "@/types/entity" + +interface AuthState { + isAuthenticated: boolean + user: User | null + token: string | null, + registrationEnabled: boolean +} + +const initialState: AuthState = { + isAuthenticated: false, + user: null, + token: null, + registrationEnabled: false +} + +const authSlice = createSlice({ + name: "auth", + initialState, + reducers: { + loginSuccess( + state, + action: PayloadAction<{ + user: User + token: string + }> + ) { + console.log("更新用户信息:", action.payload.user) + state.isAuthenticated = true + state.user = action.payload.user + state.token = action.payload.token + }, + logout(state) { + state.isAuthenticated = false + state.user = null + state.token = null + }, + updateRegistrationEnabled(state, action: PayloadAction) { + state.registrationEnabled = action.payload + } + }, +}) + +export const { loginSuccess, logout, updateRegistrationEnabled } = authSlice.actions +export default authSlice.reducer diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..b786fc2 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,47 @@ +import { configureStore, combineReducers } from "@reduxjs/toolkit" +import { useDispatch, useSelector } from "react-redux" +import authReducer from "./auth-slice" +import { + persistStore, + persistReducer, + FLUSH, + REHYDRATE, + PAUSE, + PERSIST, + PURGE, + REGISTER, +} from "redux-persist" +import storage from "redux-persist/lib/storage/session" + +const persistConfig = { + key: "root", + storage, + whitelist: ["auth"], + // blacklist: ['department'], +} + +const rootReducer = combineReducers({ + auth: authReducer, +}) + +const persistedReducer = persistReducer(persistConfig, rootReducer) + +const store = configureStore({ + reducer: persistedReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: { + ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], + }, + }), +}) + +export const persistor = persistStore(store) + +export default store +export type RootState = ReturnType +export type AppDispatch = typeof store.dispatch +export type AppStore = typeof store + +export const useAppDispatch = useDispatch.withTypes() +export const useAppSelector = useSelector.withTypes() diff --git a/src/types/antd/index.ts b/src/types/antd/index.ts new file mode 100644 index 0000000..a90422c --- /dev/null +++ b/src/types/antd/index.ts @@ -0,0 +1,14 @@ +import type { GetProps } from "antd" +import type Icon from "@ant-design/icons" + +export type AntIconComponentProps = GetProps + +export interface AntTreeSelectOption { + value: T + label: string + children: AntTreeSelectOption[] + disabled?: boolean + disableCheckbox?: boolean + selectable?: boolean + checkable?: boolean +} diff --git a/src/types/config/index.ts b/src/types/config/index.ts new file mode 100644 index 0000000..443140d --- /dev/null +++ b/src/types/config/index.ts @@ -0,0 +1,14 @@ +/** + * WeCom config + */ +export type WecomConfig = { + /** + * Corporation ID + */ + corpId: string + + /** + * Application ID + */ + agentId: string +} diff --git a/src/types/constant/index.ts b/src/types/constant/index.ts new file mode 100644 index 0000000..839087b --- /dev/null +++ b/src/types/constant/index.ts @@ -0,0 +1,6 @@ +/** + * Status + */ +export type Status = "ACTIVE" | "INACTIVE" + +export type UserStatus = Status | "LOCKED" diff --git a/src/types/entity/index.ts b/src/types/entity/index.ts new file mode 100644 index 0000000..5abfaf9 --- /dev/null +++ b/src/types/entity/index.ts @@ -0,0 +1,73 @@ +import type { Status, UserStatus } from "@/types/constant" +import type { CountryCode as RegionAbbreviation } from "libphonenumber-js" +import type { Dayjs } from "dayjs" + +/** + * User information + */ +export interface User { + id: number + username: string + password: string + fullName: string + email: string + regionAbbreviation: RegionAbbreviation + phoneNumber: string + avatarUrl: string + status: UserStatus + departmentId: number + positionId: number + createdAt: string + updatedAt: string +} + +/** + * Menu Item + */ +export interface MenuItem { + id: number + name: string + parentId: number | null + code: string + sort: number + isExternalLink: boolean + isVisible: boolean + status: Status + authorityCode: string | null + icon: string | null + createdAt: string + updatedAt: string +} + +export interface Department { + id: number + name: string + parentId: number | null + sort: number + status: Status + createdAt: string + updatedAt: string +} + +export interface Position { + id: number + name: string + code: string | null + description: string | null + sort: number + status: Status + createdAt: string + updatedAt: string +} + +export interface Role { + id: number | string + name: string + code: string + sort: number + defaultValue: boolean + description: string | null + status: Status + createdAt: Dayjs | string + updatedAt: Dayjs | string +} diff --git a/src/types/form/index.ts b/src/types/form/index.ts new file mode 100644 index 0000000..29db71c --- /dev/null +++ b/src/types/form/index.ts @@ -0,0 +1,31 @@ +import type { User } from "@/types/entity" +import type { CountryCode as RegionAbbreviation } from "libphonenumber-js" +import type { Status } from "@/types/constant" +import type { Dayjs } from "dayjs" + +export interface UserFormValues extends Omit< + User, + "id" | "password" | "regionAbbreviation" | "departmentId" | "positionId" | "createdAt" | "updatedAt" +> { + id: number | string | null + password: string | null + regionAbbreviation: RegionAbbreviation | null + departmentId: number | null + positionId: number | null +} + +export interface QueryRoleForm { + qName: string | null + qCode: string | null + qStatus: Status | null +} + +export interface QueryUserForm { + qDepartmentId: number | null + qUsername: string | null + qRegionAbbreviation: RegionAbbreviation | null + qPhoneNumber: string | null + qStatus: Status | null + qCreatedAtStart: Dayjs | null + qCreatedAtEnd: Dayjs | null +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/types/page/index.ts b/src/types/page/index.ts new file mode 100644 index 0000000..9f97449 --- /dev/null +++ b/src/types/page/index.ts @@ -0,0 +1,14 @@ +export interface Sortable { + empty: boolean + sorted: boolean + unsorted: boolean +} + +export interface Pageable { + pageNumber: number + pageSize: number + sort: Sortable + offset: number + paged: boolean + unpaged: boolean +} diff --git a/src/types/route/index.ts b/src/types/route/index.ts new file mode 100644 index 0000000..ae7b549 --- /dev/null +++ b/src/types/route/index.ts @@ -0,0 +1,6 @@ +/** + * React Router Metadata + */ +export interface RouteHandle { + label: string +} diff --git a/src/types/tree/index.ts b/src/types/tree/index.ts new file mode 100644 index 0000000..9495b87 --- /dev/null +++ b/src/types/tree/index.ts @@ -0,0 +1,37 @@ +/** + * Represents a single node within a tree structure. + * + * Each node holds a data item of type T and an array of its children. + * + * @template T the type of the data item stored in the node + */ +export interface TreeNode { + /** + * The actual data item contained within this node. + */ + item: T + /** + * A list of child nodes belonging to this node. + * + * This array will be empty if the node is a leaf node. + */ + children: TreeNode[] +} + +/** + * Defines a tree structure, which is simply a top-level tree node. + * + * It represents the root of the hierarchical data structure. + * + * @template T the type of the data items within the tree + */ +export type Tree = TreeNode + +/** + * Defines a forest, which is an array of zero or more disjoint trees. + * + * Each element in the array is the root of an independent tree. + * + * @template T the type of the data items within the trees + */ +export type Forest = TreeNode[] diff --git a/src/types/web/request/index.ts b/src/types/web/request/index.ts new file mode 100644 index 0000000..93e12be --- /dev/null +++ b/src/types/web/request/index.ts @@ -0,0 +1,54 @@ +import type { CountryCode as RegionAbbreviation } from "libphonenumber-js" +import type { Status, UserStatus } from "@/types/constant" +import type { User } from "@/types/entity" + +export interface PageRequest { + pageNum?: number + pageSize?: number +} + +export interface UsernamePasswordLoginRequest { + username: string + password: string + captcha?: string + uuid?: string +} + +export interface QueryUserRequest extends PageRequest { + departmentId: number | null + username: string | null + regionAbbreviation: RegionAbbreviation | null + phoneNumber: string | null + status: Status | null + createdAtStart: string | null + createdAtEnd: string | null +} + +export interface QueryPositionRequest extends PageRequest { +} + +export interface AddUserRequest extends Omit{ + roleIds: number[] | null +} + +export interface EditUserRequest { + id: number | string + username: string | null + fullName: string | null + email: string | null + regionAbbreviation: string | null + phoneNumber: string | null + avatarUrl: string | null + status: UserStatus | null + departmentId: number | null + positionId: number | null + roleIds: number[] | null +} + +export interface QueryRoleRequest extends PageRequest { + name: string | null + code: string | null + status: Status | null +} + + diff --git a/src/types/web/response/index.ts b/src/types/web/response/index.ts new file mode 100644 index 0000000..bd20934 --- /dev/null +++ b/src/types/web/response/index.ts @@ -0,0 +1,39 @@ +import type { Role, User } from "@/types/entity" +import type { Pageable, Sortable } from "@/types/page" + +export interface PageResponse { + content: T[] + last: boolean + totalPages: number + totalElements: number + size: number + sort: Sortable + first: boolean + numberOfElements: number + empty: boolean, + pageable: Pageable +} + +export interface UserAuthResponse { + user: User + accessToken: string +} + +export interface UserDetailResponse extends User { + departmentName: string + positionName: string +} + +export interface CaptchaResponse { + uuid: string + captcha: string +} + +export interface GeneralErrorResponse { + message: string + timestamp: string +} + +export interface RoleResponse extends PageResponse { +} + diff --git a/src/utils/department-utils/index.ts b/src/utils/department-utils/index.ts new file mode 100644 index 0000000..76481f3 --- /dev/null +++ b/src/utils/department-utils/index.ts @@ -0,0 +1,28 @@ +import { type TreeDataNode } from "antd" +import type { TreeNode } from "@/types/tree" +import type { Department } from "@/types/entity" + +export function transformDepartmentData(departments: TreeNode[]): TreeDataNode[] { + if (!departments || departments.length === 0) { + return [] + } + + return departments + .sort((a, b) => a.item.sort - b.item.sort) + .map((node) => { + const { item, children } = node + const hasChildren = children && children.length > 0 + + const treeItem: TreeDataNode = { + key: item.id, + title: item.name, + } + + if (hasChildren) { + // Append children + return { ...treeItem, children: transformDepartmentData(children) } + } + + return treeItem + }) +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..39caf36 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,2 @@ +export * as PhoneNumberUtils from "./phone-number-utils" +export * as DepartmentUtils from "./department-utils" diff --git a/src/utils/phone-number-utils/index.ts b/src/utils/phone-number-utils/index.ts new file mode 100644 index 0000000..0196a5b --- /dev/null +++ b/src/utils/phone-number-utils/index.ts @@ -0,0 +1,28 @@ +import { type CountryCode as RegionAbbreviation, getCountryCallingCode, parsePhoneNumberWithError, PhoneNumber } from "libphonenumber-js" + +/** + * Format user's phone number as user's country/region. + * + * @param regionAbbreviation user's region abbreviation + * @param phoneNumber user's phone number + * @return formatted phone number + */ +export function formatInternationalPhoneNumber(regionAbbreviation: string, phoneNumber: string): string { + try { + const _phoneNumber = parsePhoneNumberWithError(phoneNumber, regionAbbreviation as RegionAbbreviation) + + if (!_phoneNumber.isValid()) { + console.warn(`Phone number ${_phoneNumber.formatInternational()} is not valid`) + } + return _phoneNumber.formatInternational() + } catch (error) { + console.error("Phone number parsing failed:", error) + const _regionAbbreviation: RegionAbbreviation = regionAbbreviation as RegionAbbreviation + const callingCode = getCountryCallingCode(_regionAbbreviation) + return `+${callingCode} ${phoneNumber}` + } +} + +export function getDefaultCountryCode(): RegionAbbreviation { + return import.meta.env.VITE_DEFAULT_REGION_CODE +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..b133581 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,27 @@ +/// +/// + +import type { CountryCode as RegionAbbreviation } from "libphonenumber-js" + +interface ImportMetaEnv { + /** + * Server Base URL + */ + readonly VITE_API_BASE_URL: string + + /** + * Microsoft Entra ID Client ID + */ + readonly VITE_MSAL_CLIENT_ID: string + + /** + * Microsoft Entra ID Tenant ID + */ + readonly VITE_MSAL_TENANT_ID: string + + readonly VITE_DEFAULT_REGION_ABBREVIATION: RegionAbbreviation +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..9b30999 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fec8c8e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..70d82d6 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..a5708a3 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,18 @@ +import { fileURLToPath, URL } from "node:url" +import { defineConfig } from "vite" +import react from "@vitejs/plugin-react" +import tailwindcss from "@tailwindcss/vite" +import svgr from "vite-plugin-svgr" + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss(), svgr()], + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + }, + server: { + allowedHosts: true, + } +})