mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-24 05:16:09 +00:00
refactor(web): migrate from Next.js to Vite + React Router (#2102)
* refactor(web): migrate from Next.js to Vite + React Router
* fix: update build pipelines for Vite migration (out → dist)
- Dockerfile: npm run build → npx vite build, web/out → web/dist
- pyproject.toml: package-data web/out/** → web/dist/**
- paths.py: support both web/dist (Vite) and web/out (legacy) with fallback
* fix: remove .next from git tracking, add to .gitignore
1334 cached files from web/.next/ were accidentally committed.
Added .next/ to both root and web/.gitignore.
* fix: update build process to use Vite and correct output directory
* fix: update pnpm-lock.yaml and eslint config for Vite migration
* style: fix prettier formatting issues
* fix: add eslint-plugin-react-hooks for Vite migration
* fix: remove undefined eslint rule reference, downgrade react-hooks plugin to v5
* fix(web): clean up remaining Next.js artifacts in Vite migration
- Add vite-env.d.ts for import.meta.env and asset type declarations
- Remove dead layout.tsx (providers already in main.tsx)
- Fix useSearchParams destructuring to [searchParams] tuple (11 locations)
- Replace process.env.NEXT_PUBLIC_* with import.meta.env.VITE_*
- Fix langbotIcon.src to langbotIcon (Vite returns URL string)
- Fix Link href to Link to for react-router-dom
- Fix navigate({ scroll: false }) to { preventScrollReset: true }
- Fix [router] dependency arrays to [navigate]
- Remove Next.js plugin from tsconfig, set rsc: false in components.json
- Replace next lint with eslint in lint-staged
* feat: add tools API endpoint and tools-selector form type
Backend:
- Add GET /api/v1/tools — list all available tools (plugin + MCP)
- Add GET /api/v1/tools/<tool_name> — get specific tool details
Frontend:
- Add TOOLS_SELECTOR form type for plugin config forms
- Multi-select dialog with tool name and description
- Add PluginTool entity type and API client methods
* Revert "feat: add tools API endpoint and tools-selector form type"
This reverts commit 3c637fc563.
This commit is contained in:
@@ -43,10 +43,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd /tmp/langbot_build_web/web
|
cd /tmp/langbot_build_web/web
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npx vite build
|
||||||
- name: Package Output
|
- name: Package Output
|
||||||
run: |
|
run: |
|
||||||
cp -r /tmp/langbot_build_web/web/out ./web
|
cp -r /tmp/langbot_build_web/web/dist ./web
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ jobs:
|
|||||||
npm install -g pnpm
|
npm install -g pnpm
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm build
|
pnpm build
|
||||||
mkdir -p ../src/langbot/web/out
|
mkdir -p ../src/langbot/web/dist
|
||||||
cp -r out ../src/langbot/web/
|
cp -r dist ../src/langbot/web/
|
||||||
|
|
||||||
- name: Install the latest version of uv
|
- name: Install the latest version of uv
|
||||||
uses: astral-sh/setup-uv@v6
|
uses: astral-sh/setup-uv@v6
|
||||||
|
|||||||
@@ -52,3 +52,6 @@ src/langbot/web/
|
|||||||
/dist
|
/dist
|
||||||
/build
|
/build
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
|
||||||
|
# Next.js build cache (legacy)
|
||||||
|
web/.next/
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ WORKDIR /app
|
|||||||
|
|
||||||
COPY web ./web
|
COPY web ./web
|
||||||
|
|
||||||
RUN cd web && npm install && npm run build
|
RUN cd web && npm install && npx vite build
|
||||||
|
|
||||||
FROM python:3.12.7-slim
|
FROM python:3.12.7-slim
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ WORKDIR /app
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
COPY --from=node /app/web/out ./web/out
|
COPY --from=node /app/web/dist ./web/dist
|
||||||
|
|
||||||
RUN apt update \
|
RUN apt update \
|
||||||
&& apt install gcc -y \
|
&& apt install gcc -y \
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ requires = ["setuptools>=61.0", "wheel"]
|
|||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
package-data = { "langbot" = ["templates/**", "pkg/provider/modelmgr/requesters/*", "pkg/platform/sources/*", "web/out/**"] }
|
package-data = { "langbot" = ["templates/**", "pkg/provider/modelmgr/requesters/*", "pkg/platform/sources/*", "web/dist/**"] }
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
|
|||||||
@@ -38,28 +38,31 @@ def get_frontend_path() -> str:
|
|||||||
"""
|
"""
|
||||||
Get the path to the frontend build files.
|
Get the path to the frontend build files.
|
||||||
|
|
||||||
Returns the path to web/out directory, handling both:
|
Returns the path to web/dist directory (Vite build output), handling both:
|
||||||
- Development mode: running from source directory
|
- Development mode: running from source directory
|
||||||
- Package mode: installed via pip/uvx
|
- Package mode: installed via pip/uvx
|
||||||
|
- Legacy mode: web/out (Next.js, for backward compatibility)
|
||||||
"""
|
"""
|
||||||
# First, check if we're running from source directory
|
# Check both dist (Vite) and out (legacy Next.js) paths
|
||||||
if _check_if_source_install() and os.path.exists('web/out'):
|
for dirname in ('dist', 'out'):
|
||||||
return 'web/out'
|
web_dir = f'web/{dirname}'
|
||||||
|
|
||||||
# Second, check current directory for web/out (in case user is in source dir)
|
# First, check if we're running from source directory
|
||||||
if os.path.exists('web/out'):
|
if _check_if_source_install() and os.path.exists(web_dir):
|
||||||
return 'web/out'
|
return web_dir
|
||||||
|
|
||||||
# Third, find it relative to the package installation
|
# Second, check current directory
|
||||||
# Get the directory where this file is located
|
if os.path.exists(web_dir):
|
||||||
# paths.py is in pkg/utils/, so parent.parent goes up to pkg/, then parent again goes up to the package root
|
return web_dir
|
||||||
pkg_dir = Path(__file__).parent.parent.parent
|
|
||||||
frontend_path = pkg_dir / 'web' / 'out'
|
# Third, find it relative to the package installation
|
||||||
if frontend_path.exists():
|
pkg_dir = Path(__file__).parent.parent.parent
|
||||||
return str(frontend_path)
|
frontend_path = pkg_dir / 'web' / dirname
|
||||||
|
if frontend_path.exists():
|
||||||
|
return str(frontend_path)
|
||||||
|
|
||||||
# Return the default path (will be checked by caller)
|
# Return the default path (will be checked by caller)
|
||||||
return 'web/out'
|
return 'web/dist'
|
||||||
|
|
||||||
|
|
||||||
def get_resource_path(resource: str) -> str:
|
def get_resource_path(resource: str) -> str:
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
NEXT_PUBLIC_API_BASE_URL=http://localhost:5300
|
VITE_API_BASE_URL=http://localhost:5300
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
|
/dist/
|
||||||
/.next/
|
/.next/
|
||||||
/out/
|
/out/
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://ui.shadcn.com/schema.json",
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
"style": "new-york",
|
"style": "new-york",
|
||||||
"rsc": true,
|
"rsc": false,
|
||||||
"tsx": true,
|
"tsx": true,
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "",
|
"config": "",
|
||||||
|
|||||||
+20
-11
@@ -1,18 +1,27 @@
|
|||||||
import { dirname } from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { FlatCompat } from '@eslint/eslintrc';
|
|
||||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks';
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
import tseslint from 'typescript-eslint';
|
||||||
const __dirname = dirname(__filename);
|
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
|
||||||
baseDirectory: __dirname,
|
|
||||||
});
|
|
||||||
|
|
||||||
const eslintConfig = [
|
const eslintConfig = [
|
||||||
...compat.extends('next/core-web-vitals', 'next/typescript'),
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'warn',
|
||||||
|
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
eslintPluginPrettierRecommended,
|
eslintPluginPrettierRecommended,
|
||||||
|
{
|
||||||
|
ignores: ['dist/**', 'node_modules/**'],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default eslintConfig;
|
export default eslintConfig;
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
sed -i 's/children={<HomePage \/>} />\n <HomePage \/>\n <\/HomeLayout>/g' src/router.tsx
|
||||||
|
# well it's easier to recreate router.tsx
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>LangBot</title>
|
||||||
|
<meta name="description" content="Production-grade platform for building agentic IM bots" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cd /root/.openclaw/workspace/coding/projects/LangBot/web
|
||||||
|
|
||||||
|
# Find and replace next/navigation
|
||||||
|
find src -type f \( -name "*.ts" -o -name "*.tsx" \) -exec sed -i \
|
||||||
|
-e "s/import {.*useRouter.*} from 'next\/navigation'/import { useNavigate } from 'react-router-dom'/g" \
|
||||||
|
-e "s/import {.*usePathname.*} from 'next\/navigation'/import { useLocation } from 'react-router-dom'/g" \
|
||||||
|
-e "s/import {.*useSearchParams.*} from 'next\/navigation'/import { useSearchParams } from 'react-router-dom'/g" \
|
||||||
|
-e "s/const router = useRouter()/const navigate = useNavigate()/g" \
|
||||||
|
-e "s/router\.push(/navigate(/g" \
|
||||||
|
-e "s/router\.replace(/navigate(/g" \
|
||||||
|
-e "s/router\.back()/navigate(-1)/g" \
|
||||||
|
-e "s/router\.refresh()/navigate(0)/g" \
|
||||||
|
-e "s/const pathname = usePathname()/const location = useLocation();\n const pathname = location.pathname;/g" \
|
||||||
|
-e "s/usePathname()/useLocation().pathname/g" \
|
||||||
|
{} +
|
||||||
|
|
||||||
|
# Note: useSearchParams returns a tuple in react-router-dom. This might need manual fix depending on usage.
|
||||||
|
|
||||||
|
# Replace next/link
|
||||||
|
find src -type f \( -name "*.ts" -o -name "*.tsx" \) -exec sed -i \
|
||||||
|
-e "s/import Link from 'next\/link'/import { Link } from 'react-router-dom'/g" \
|
||||||
|
-e "s/<Link href=/<Link to=/g" \
|
||||||
|
{} +
|
||||||
|
|
||||||
|
# Remove 'use client'
|
||||||
|
find src -type f \( -name "*.ts" -o -name "*.tsx" \) -exec sed -i "s/'use client';//g" {} +
|
||||||
|
find src -type f \( -name "*.ts" -o -name "*.tsx" \) -exec sed -i 's/"use client";//g' {} +
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import type { NextConfig } from 'next';
|
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
|
||||||
/* config options here */
|
|
||||||
output: 'export',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default nextConfig;
|
|
||||||
Generated
+891
-3449
File diff suppressed because it is too large
Load Diff
+11
-11
@@ -3,16 +3,15 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "vite",
|
||||||
"build": "next build",
|
"build": "tsc && vite build",
|
||||||
"start": "next start",
|
"preview": "vite preview",
|
||||||
"lint": "eslint src",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint src --fix",
|
"format": "prettier --write ."
|
||||||
"lint-staged": "lint-staged"
|
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
"next lint --fix",
|
"eslint --fix",
|
||||||
"prettier --write"
|
"prettier --write"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -46,6 +45,7 @@
|
|||||||
"@radix-ui/react-tooltip": "^1.2.7",
|
"@radix-ui/react-tooltip": "^1.2.7",
|
||||||
"@tailwindcss/postcss": "^4.1.5",
|
"@tailwindcss/postcss": "^4.1.5",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"axios": "^1.13.5",
|
"axios": "^1.13.5",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@@ -55,8 +55,6 @@
|
|||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"lucide-react": "^0.507.0",
|
"lucide-react": "^0.507.0",
|
||||||
"next": "~16.1.5",
|
|
||||||
"next-themes": "^0.4.6",
|
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
@@ -65,6 +63,7 @@
|
|||||||
"react-i18next": "^15.5.1",
|
"react-i18next": "^15.5.1",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-photo-view": "^1.2.7",
|
"react-photo-view": "^1.2.7",
|
||||||
|
"react-router-dom": "^7.14.0",
|
||||||
"react-syntax-highlighter": "^16.1.0",
|
"react-syntax-highlighter": "^16.1.0",
|
||||||
"recharts": "2.15.4",
|
"recharts": "2.15.4",
|
||||||
"rehype-autolink-headings": "^7.1.0",
|
"rehype-autolink-headings": "^7.1.0",
|
||||||
@@ -77,10 +76,10 @@
|
|||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^3.2.0",
|
||||||
"tailwindcss": "^4.1.5",
|
"tailwindcss": "^4.1.5",
|
||||||
"uuidjs": "^5.1.0",
|
"uuidjs": "^5.1.0",
|
||||||
|
"vite": "^8.0.3",
|
||||||
"zod": "^3.24.4"
|
"zod": "^3.24.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
|
||||||
"@types/debug": "^4.1.12",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/estree": "^1.0.8",
|
"@types/estree": "^1.0.8",
|
||||||
"@types/estree-jsx": "^1.0.5",
|
"@types/estree-jsx": "^1.0.5",
|
||||||
@@ -95,9 +94,10 @@
|
|||||||
"@types/react-syntax-highlighter": "^15.5.13",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
"@types/unist": "^3.0.3",
|
"@types/unist": "^3.0.3",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.2.4",
|
|
||||||
"eslint-config-prettier": "^10.1.2",
|
"eslint-config-prettier": "^10.1.2",
|
||||||
"eslint-plugin-prettier": "^5.2.6",
|
"eslint-plugin-prettier": "^5.2.6",
|
||||||
|
"eslint-plugin-react": "^7.37.5",
|
||||||
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"lint-staged": "^15.5.1",
|
"lint-staged": "^15.5.1",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"tw-animate-css": "^1.2.9",
|
"tw-animate-css": "^1.2.9",
|
||||||
|
|||||||
Generated
+1401
-4172
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState, useCallback, Suspense } from 'react';
|
import { useEffect, useState, useCallback, Suspense } from 'react';
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -23,8 +21,8 @@ import { LoadingSpinner } from '@/components/ui/loading-spinner';
|
|||||||
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
||||||
|
|
||||||
function SpaceOAuthCallbackContent() {
|
function SpaceOAuthCallbackContent() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [status, setStatus] = useState<
|
const [status, setStatus] = useState<
|
||||||
@@ -51,7 +49,7 @@ function SpaceOAuthCallbackContent() {
|
|||||||
const wizardState = localStorage.getItem('langbot_wizard_state');
|
const wizardState = localStorage.getItem('langbot_wizard_state');
|
||||||
const redirectTo = wizardState ? '/wizard' : '/home';
|
const redirectTo = wizardState ? '/wizard' : '/home';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
router.push(redirectTo);
|
navigate(redirectTo);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setStatus('error');
|
setStatus('error');
|
||||||
@@ -64,7 +62,7 @@ function SpaceOAuthCallbackContent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[router, t],
|
[navigate, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [bindState, setBindState] = useState<string | null>(null);
|
const [bindState, setBindState] = useState<string | null>(null);
|
||||||
@@ -81,7 +79,7 @@ function SpaceOAuthCallbackContent() {
|
|||||||
setStatus('success');
|
setStatus('success');
|
||||||
toast.success(t('account.bindSpaceSuccess'));
|
toast.success(t('account.bindSpaceSuccess'));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
router.push('/home');
|
navigate('/home');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setStatus('error');
|
setStatus('error');
|
||||||
@@ -96,7 +94,7 @@ function SpaceOAuthCallbackContent() {
|
|||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[router, t],
|
[navigate, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -146,7 +144,7 @@ function SpaceOAuthCallbackContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCancelBind = () => {
|
const handleCancelBind = () => {
|
||||||
router.push('/home');
|
navigate('/home');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -154,7 +152,7 @@ function SpaceOAuthCallbackContent() {
|
|||||||
<Card className="w-[400px] shadow-lg dark:shadow-white/10">
|
<Card className="w-[400px] shadow-lg dark:shadow-white/10">
|
||||||
<CardHeader className="text-center">
|
<CardHeader className="text-center">
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon}
|
||||||
alt="LangBot"
|
alt="LangBot"
|
||||||
className="w-16 h-16 mb-4 mx-auto"
|
className="w-16 h-16 mb-4 mx-auto"
|
||||||
/>
|
/>
|
||||||
@@ -217,7 +215,7 @@ function SpaceOAuthCallbackContent() {
|
|||||||
<>
|
<>
|
||||||
<AlertCircle className="h-12 w-12 text-red-500" />
|
<AlertCircle className="h-12 w-12 text-red-500" />
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push(isBindMode ? '/home' : '/login')}
|
onClick={() => navigate(isBindMode ? '/home' : '/login')}
|
||||||
className="w-full mt-4"
|
className="w-full mt-4"
|
||||||
>
|
>
|
||||||
{isBindMode ? t('common.backToHome') : t('common.backToLogin')}
|
{isBindMode ? t('common.backToHome') : t('common.backToLogin')}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@import "tw-animate-css";
|
||||||
:root {
|
:root {
|
||||||
/* 适用于 Firefox 的滚动条 */
|
/* 适用于 Firefox 的滚动条 */
|
||||||
scrollbar-color: rgba(0, 0, 0, 0.2) transparent; /* 滑块颜色 + 轨道颜色 */
|
scrollbar-color: rgba(0, 0, 0, 0.2) transparent; /* 滑块颜色 + 轨道颜色 */
|
||||||
@@ -72,9 +74,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@import 'tailwindcss';
|
|
||||||
|
|
||||||
@import 'tw-animate-css';
|
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
@@ -34,7 +32,7 @@ import { toast } from 'sonner';
|
|||||||
|
|
||||||
export default function BotDetailContent({ id }: { id: string }) {
|
export default function BotDetailContent({ id }: { id: string }) {
|
||||||
const isCreateMode = id === 'new';
|
const isCreateMode = id === 'new';
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { refreshBots, bots, setDetailEntityName } = useSidebarData();
|
const { refreshBots, bots, setDetailEntityName } = useSidebarData();
|
||||||
|
|
||||||
@@ -105,12 +103,12 @@ export default function BotDetailContent({ id }: { id: string }) {
|
|||||||
|
|
||||||
function handleBotDeleted() {
|
function handleBotDeleted() {
|
||||||
refreshBots();
|
refreshBots();
|
||||||
router.push('/home/bots');
|
navigate('/home/bots');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNewBotCreated(newBotId: string) {
|
function handleNewBotCreated(newBotId: string) {
|
||||||
refreshBots();
|
refreshBots();
|
||||||
router.push(`/home/bots?id=${encodeURIComponent(newBotId)}`);
|
navigate(`/home/bots?id=${encodeURIComponent(newBotId)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete() {
|
function confirmDelete() {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { BotLog } from '@/app/infra/http/requestParam/bots/GetBotLogsResponse';
|
import { BotLog } from '@/app/infra/http/requestParam/bots/GetBotLogsResponse';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { BotLogManager } from '@/app/home/bots/components/bot-log/BotLogManager';
|
import { BotLogManager } from '@/app/home/bots/components/bot-log/BotLogManager';
|
||||||
import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
|
import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
|
||||||
import { BotLog } from '@/app/infra/http/requestParam/bots/GetBotLogsResponse';
|
import { BotLog } from '@/app/infra/http/requestParam/bots/GetBotLogsResponse';
|
||||||
@@ -15,7 +13,7 @@ import { Checkbox } from '@/components/ui/checkbox';
|
|||||||
import { ChevronDownIcon, ExternalLink } from 'lucide-react';
|
import { ChevronDownIcon, ExternalLink } from 'lucide-react';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
export function BotLogListComponent({
|
export function BotLogListComponent({
|
||||||
botId,
|
botId,
|
||||||
@@ -32,7 +30,7 @@ export function BotLogListComponent({
|
|||||||
hideToolbar?: boolean;
|
hideToolbar?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const manager = useRef(new BotLogManager(botId)).current;
|
const manager = useRef(new BotLogManager(botId)).current;
|
||||||
const [botLogList, setBotLogList] = useState<BotLog[]>([]);
|
const [botLogList, setBotLogList] = useState<BotLog[]>([]);
|
||||||
const [autoFlush, setAutoFlush] = useState(true);
|
const [autoFlush, setAutoFlush] = useState(true);
|
||||||
@@ -231,7 +229,7 @@ export function BotLogListComponent({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="gap-1"
|
className="gap-1"
|
||||||
onClick={() => router.push(`/home/monitoring?botId=${botId}`)}
|
onClick={() => navigate(`/home/monitoring?botId=${botId}`)}
|
||||||
>
|
>
|
||||||
<ExternalLink className="size-3.5" />
|
<ExternalLink className="size-3.5" />
|
||||||
<span className="text-sm">{t('bots.viewDetailedLogs')}</span>
|
<span className="text-sm">{t('bots.viewDetailedLogs')}</span>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
useState,
|
useState,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
'use client';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import BotDetailContent from './BotDetailContent';
|
import BotDetailContent from './BotDetailContent';
|
||||||
|
|
||||||
export default function BotConfigPage() {
|
export default function BotConfigPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const detailId = searchParams.get('id');
|
const detailId = searchParams.get('id');
|
||||||
|
|
||||||
if (detailId) {
|
if (detailId) {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { Copy, Check, Trash2, Plus } from 'lucide-react';
|
import { Copy, Check, Trash2, Plus } from 'lucide-react';
|
||||||
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
|
import { useNavigate, useLocation, useSearchParams } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -67,9 +65,10 @@ export default function ApiIntegrationDialog({
|
|||||||
onOpenChange,
|
onOpenChange,
|
||||||
}: ApiIntegrationDialogProps) {
|
}: ApiIntegrationDialogProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const pathname = usePathname();
|
const location = useLocation();
|
||||||
const searchParams = useSearchParams();
|
const pathname = location.pathname;
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const [activeTab, setActiveTab] = useState('apikeys');
|
const [activeTab, setActiveTab] = useState('apikeys');
|
||||||
const [apiKeys, setApiKeys] = useState<ApiKey[]>([]);
|
const [apiKeys, setApiKeys] = useState<ApiKey[]>([]);
|
||||||
const [webhooks, setWebhooks] = useState<Webhook[]>([]);
|
const [webhooks, setWebhooks] = useState<Webhook[]>([]);
|
||||||
@@ -94,7 +93,9 @@ export default function ApiIntegrationDialog({
|
|||||||
if (open) {
|
if (open) {
|
||||||
const params = new URLSearchParams(searchParams.toString());
|
const params = new URLSearchParams(searchParams.toString());
|
||||||
params.set('action', 'showApiIntegrationSettings');
|
params.set('action', 'showApiIntegrationSettings');
|
||||||
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
|
navigate(`${pathname}?${params.toString()}`, {
|
||||||
|
preventScrollReset: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
@@ -108,7 +109,7 @@ export default function ApiIntegrationDialog({
|
|||||||
const newUrl = params.toString()
|
const newUrl = params.toString()
|
||||||
? `${pathname}?${params.toString()}`
|
? `${pathname}?${params.toString()}`
|
||||||
: pathname;
|
: pathname;
|
||||||
router.replace(newUrl, { scroll: false });
|
navigate(newUrl, { preventScrollReset: true });
|
||||||
}
|
}
|
||||||
onOpenChange(newOpen);
|
onOpenChange(newOpen);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { SidebarChildVO } from '@/app/home/components/home-sidebar/HomeSidebarChild';
|
import { SidebarChildVO } from '@/app/home/components/home-sidebar/HomeSidebarChild';
|
||||||
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
|
import { useNavigate, useLocation, useSearchParams } from 'react-router-dom';
|
||||||
import { sidebarConfigList } from '@/app/home/components/home-sidebar/sidbarConfigList';
|
import { sidebarConfigList } from '@/app/home/components/home-sidebar/sidbarConfigList';
|
||||||
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
||||||
import { systemInfo, httpClient } from '@/app/infra/http/HttpClient';
|
import { systemInfo, httpClient } from '@/app/infra/http/HttpClient';
|
||||||
@@ -29,7 +27,7 @@ import {
|
|||||||
Github,
|
Github,
|
||||||
Zap,
|
Zap,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from '@/components/providers/theme-provider';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -244,9 +242,10 @@ function NavItems({
|
|||||||
sectionOpenState: Record<string, boolean>;
|
sectionOpenState: Record<string, boolean>;
|
||||||
onSectionToggle: (id: string, open: boolean) => void;
|
onSectionToggle: (id: string, open: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const pathname = usePathname();
|
const location = useLocation();
|
||||||
const searchParams = useSearchParams();
|
const pathname = location.pathname;
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const sidebarData = useSidebarData();
|
const sidebarData = useSidebarData();
|
||||||
const { setPendingPluginInstallAction } = sidebarData;
|
const { setPendingPluginInstallAction } = sidebarData;
|
||||||
const { state: sidebarState, isMobile } = useSidebar();
|
const { state: sidebarState, isMobile } = useSidebar();
|
||||||
@@ -413,7 +412,7 @@ function NavItems({
|
|||||||
'bg-accent text-accent-foreground font-medium',
|
'bg-accent text-accent-foreground font-medium',
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(itemRoute);
|
navigate(itemRoute);
|
||||||
setPopoverOpen((prev) => ({
|
setPopoverOpen((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[config.id]: false,
|
[config.id]: false,
|
||||||
@@ -471,7 +470,7 @@ function NavItems({
|
|||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
router.push(itemRoute);
|
navigate(itemRoute);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.emoji ? (
|
{item.emoji ? (
|
||||||
@@ -623,7 +622,7 @@ function NavItems({
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
router.push('/home/market');
|
navigate('/home/market');
|
||||||
setPopoverOpen((prev) => ({
|
setPopoverOpen((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[config.id]: false,
|
[config.id]: false,
|
||||||
@@ -638,7 +637,7 @@ function NavItems({
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setPendingPluginInstallAction('local');
|
setPendingPluginInstallAction('local');
|
||||||
router.push('/home/plugins');
|
navigate('/home/plugins');
|
||||||
setPopoverOpen((prev) => ({
|
setPopoverOpen((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[config.id]: false,
|
[config.id]: false,
|
||||||
@@ -652,7 +651,7 @@ function NavItems({
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setPendingPluginInstallAction('github');
|
setPendingPluginInstallAction('github');
|
||||||
router.push('/home/plugins');
|
navigate('/home/plugins');
|
||||||
setPopoverOpen((prev) => ({
|
setPopoverOpen((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[config.id]: false,
|
[config.id]: false,
|
||||||
@@ -669,7 +668,7 @@ function NavItems({
|
|||||||
type="button"
|
type="button"
|
||||||
className="p-1 rounded-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
|
className="p-1 rounded-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(`${routePrefix}?id=new`);
|
navigate(`${routePrefix}?id=new`);
|
||||||
setPopoverOpen((prev) => ({
|
setPopoverOpen((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[config.id]: false,
|
[config.id]: false,
|
||||||
@@ -731,7 +730,7 @@ function NavItems({
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
router.push('/home/market');
|
navigate('/home/market');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Store className="size-4" />
|
<Store className="size-4" />
|
||||||
@@ -742,7 +741,7 @@ function NavItems({
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setPendingPluginInstallAction('local');
|
setPendingPluginInstallAction('local');
|
||||||
router.push('/home/plugins');
|
navigate('/home/plugins');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Upload className="size-4" />
|
<Upload className="size-4" />
|
||||||
@@ -752,7 +751,7 @@ function NavItems({
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setPendingPluginInstallAction('github');
|
setPendingPluginInstallAction('github');
|
||||||
router.push('/home/plugins');
|
navigate('/home/plugins');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Github className="size-4" />
|
<Github className="size-4" />
|
||||||
@@ -766,7 +765,7 @@ function NavItems({
|
|||||||
className="p-1 rounded-sm text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground opacity-0 group-hover/category-header:opacity-100 transition-all"
|
className="p-1 rounded-sm text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground opacity-0 group-hover/category-header:opacity-100 transition-all"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
router.push(`${routePrefix}?id=new`);
|
navigate(`${routePrefix}?id=new`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Plus className="size-3.5" />
|
<Plus className="size-3.5" />
|
||||||
@@ -1029,9 +1028,10 @@ export default function HomeSidebar({
|
|||||||
}: {
|
}: {
|
||||||
onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void;
|
onSelectedChangeAction: (sidebarChild: SidebarChildVO) => void;
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const pathname = usePathname();
|
const location = useLocation();
|
||||||
const searchParams = useSearchParams();
|
const pathname = location.pathname;
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const { isMobile } = useSidebar();
|
const { isMobile } = useSidebar();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1071,14 +1071,16 @@ export default function HomeSidebar({
|
|||||||
if (open) {
|
if (open) {
|
||||||
const params = new URLSearchParams(searchParams.toString());
|
const params = new URLSearchParams(searchParams.toString());
|
||||||
params.set('action', 'showModelSettings');
|
params.set('action', 'showModelSettings');
|
||||||
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
|
navigate(`${pathname}?${params.toString()}`, {
|
||||||
|
preventScrollReset: true,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const params = new URLSearchParams(searchParams.toString());
|
const params = new URLSearchParams(searchParams.toString());
|
||||||
params.delete('action');
|
params.delete('action');
|
||||||
const newUrl = params.toString()
|
const newUrl = params.toString()
|
||||||
? `${pathname}?${params.toString()}`
|
? `${pathname}?${params.toString()}`
|
||||||
: pathname;
|
: pathname;
|
||||||
router.replace(newUrl, { scroll: false });
|
navigate(newUrl, { preventScrollReset: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1087,14 +1089,16 @@ export default function HomeSidebar({
|
|||||||
if (open) {
|
if (open) {
|
||||||
const params = new URLSearchParams(searchParams.toString());
|
const params = new URLSearchParams(searchParams.toString());
|
||||||
params.set('action', 'showAccountSettings');
|
params.set('action', 'showAccountSettings');
|
||||||
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
|
navigate(`${pathname}?${params.toString()}`, {
|
||||||
|
preventScrollReset: true,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const params = new URLSearchParams(searchParams.toString());
|
const params = new URLSearchParams(searchParams.toString());
|
||||||
params.delete('action');
|
params.delete('action');
|
||||||
const newUrl = params.toString()
|
const newUrl = params.toString()
|
||||||
? `${pathname}?${params.toString()}`
|
? `${pathname}?${params.toString()}`
|
||||||
: pathname;
|
: pathname;
|
||||||
router.replace(newUrl, { scroll: false });
|
navigate(newUrl, { preventScrollReset: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1165,7 +1169,7 @@ export default function HomeSidebar({
|
|||||||
// User click: update state AND navigate
|
// User click: update state AND navigate
|
||||||
function handleChildClick(child: SidebarChildVO) {
|
function handleChildClick(child: SidebarChildVO) {
|
||||||
selectChild(child);
|
selectChild(child);
|
||||||
router.push(child.route);
|
navigate(child.route);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSelect() {
|
function initSelect() {
|
||||||
@@ -1226,7 +1230,7 @@ export default function HomeSidebar({
|
|||||||
tooltip="LangBot"
|
tooltip="LangBot"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon}
|
||||||
alt="LangBot"
|
alt="LangBot"
|
||||||
className="size-8 rounded-lg"
|
className="size-8 rounded-lg"
|
||||||
/>
|
/>
|
||||||
@@ -1406,7 +1410,7 @@ export default function HomeSidebar({
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserMenuOpen(false);
|
setUserMenuOpen(false);
|
||||||
router.push('/wizard');
|
navigate('/wizard');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Zap className="text-blue-500" />
|
<Zap className="text-blue-500" />
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
createContext,
|
createContext,
|
||||||
useContext,
|
useContext,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Plus, Boxes } from 'lucide-react';
|
import { Plus, Boxes } from 'lucide-react';
|
||||||
import { httpClient, systemInfo } from '@/app/infra/http/HttpClient';
|
import { httpClient, systemInfo } from '@/app/infra/http/HttpClient';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Plus, MessageSquareText, Cpu, Eye, Wrench, Check } from 'lucide-react';
|
import { Plus, MessageSquareText, Cpu, Eye, Wrench, Check } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { Plus, X } from 'lucide-react';
|
import { Plus, X } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Trash2, Eye, Wrench, Check } from 'lucide-react';
|
import { Trash2, Eye, Wrench, Check } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Plus,
|
Plus,
|
||||||
@@ -135,7 +133,7 @@ export default function ProviderCard({
|
|||||||
{isLangBotModels ? (
|
{isLangBotModels ? (
|
||||||
<div className="w-9 h-9 rounded-lg overflow-hidden flex-shrink-0">
|
<div className="w-9 h-9 rounded-lg overflow-hidden flex-shrink-0">
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon}
|
||||||
alt="LangBot"
|
alt="LangBot"
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
@@ -32,7 +30,7 @@ import { FileText, FolderOpen, Search, Trash2 } from 'lucide-react';
|
|||||||
|
|
||||||
export default function KBDetailContent({ id }: { id: string }) {
|
export default function KBDetailContent({ id }: { id: string }) {
|
||||||
const isCreateMode = id === 'new';
|
const isCreateMode = id === 'new';
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { refreshKnowledgeBases, knowledgeBases, setDetailEntityName } =
|
const { refreshKnowledgeBases, knowledgeBases, setDetailEntityName } =
|
||||||
useSidebarData();
|
useSidebarData();
|
||||||
@@ -84,12 +82,12 @@ export default function KBDetailContent({ id }: { id: string }) {
|
|||||||
|
|
||||||
function handleKbDeleted() {
|
function handleKbDeleted() {
|
||||||
refreshKnowledgeBases();
|
refreshKnowledgeBases();
|
||||||
router.push('/home/knowledge');
|
navigate('/home/knowledge');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNewKbCreated(newKbId: string) {
|
function handleNewKbCreated(newKbId: string) {
|
||||||
refreshKnowledgeBases();
|
refreshKnowledgeBases();
|
||||||
router.push(`/home/knowledge?id=${encodeURIComponent(newKbId)}`);
|
navigate(`/home/knowledge?id=${encodeURIComponent(newKbId)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKbUpdated() {
|
function handleKbUpdated() {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { ColumnDef } from '@tanstack/react-table';
|
import { ColumnDef } from '@tanstack/react-table';
|
||||||
import { MoreHorizontal } from 'lucide-react';
|
import { MoreHorizontal } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
flexRender,
|
flexRender,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from 'react-router-dom';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -304,7 +304,7 @@ export default function KBForm({
|
|||||||
{t('knowledge.noEnginesAvailable')}
|
{t('knowledge.noEnginesAvailable')}
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href="/home/market?category=KnowledgeEngine"
|
to="/home/market?category=KnowledgeEngine"
|
||||||
className="text-sm text-primary hover:underline"
|
className="text-sm text-primary hover:underline"
|
||||||
>
|
>
|
||||||
{t('knowledge.installEngineHint')}
|
{t('knowledge.installEngineHint')}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
'use client';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
@@ -10,7 +8,7 @@ import KBDetailContent from './KBDetailContent';
|
|||||||
|
|
||||||
export default function KnowledgePage() {
|
export default function KnowledgePage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const detailId = searchParams.get('id');
|
const detailId = searchParams.get('id');
|
||||||
const { refreshKnowledgeBases } = useSidebarData();
|
const { refreshKnowledgeBases } = useSidebarData();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import HomeSidebar from '@/app/home/components/home-sidebar/HomeSidebar';
|
import HomeSidebar from '@/app/home/components/home-sidebar/HomeSidebar';
|
||||||
import SurveyWidget from '@/app/home/components/survey/SurveyWidget';
|
import SurveyWidget from '@/app/home/components/survey/SurveyWidget';
|
||||||
import React, {
|
import React, {
|
||||||
@@ -21,8 +19,8 @@ import {
|
|||||||
initializeUserInfo,
|
initializeUserInfo,
|
||||||
initializeSystemInfo,
|
initializeSystemInfo,
|
||||||
} from '@/app/infra/http';
|
} from '@/app/infra/http';
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import Link from 'next/link';
|
import { Link } from 'react-router-dom';
|
||||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||||
import { CircleHelp } from 'lucide-react';
|
import { CircleHelp } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -59,7 +57,7 @@ export default function HomeLayout({
|
|||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// Initialize user info if not already initialized
|
// Initialize user info if not already initialized
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -75,14 +73,14 @@ export default function HomeLayout({
|
|||||||
// Always re-fetch to ensure we have the latest wizard_status from backend
|
// Always re-fetch to ensure we have the latest wizard_status from backend
|
||||||
await initializeSystemInfo();
|
await initializeSystemInfo();
|
||||||
if (systemInfo.wizard_status === 'none') {
|
if (systemInfo.wizard_status === 'none') {
|
||||||
router.replace('/wizard');
|
navigate('/wizard');
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// If fetching system info fails, don't redirect
|
// If fetching system info fails, don't redirect
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
checkWizard();
|
checkWizard();
|
||||||
}, [router]);
|
}, [navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarDataProvider>
|
<SidebarDataProvider>
|
||||||
@@ -101,7 +99,8 @@ function HomeLayoutInner({ children }: { children: React.ReactNode }) {
|
|||||||
zh_Hans: '',
|
zh_Hans: '',
|
||||||
});
|
});
|
||||||
const { detailEntityName } = useSidebarData();
|
const { detailEntityName } = useSidebarData();
|
||||||
const pathname = usePathname();
|
const location = useLocation();
|
||||||
|
const pathname = location.pathname;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const onSelectedChangeAction = useCallback((child: SidebarChildVO) => {
|
const onSelectedChangeAction = useCallback((child: SidebarChildVO) => {
|
||||||
@@ -139,7 +138,7 @@ function HomeLayoutInner({ children }: { children: React.ReactNode }) {
|
|||||||
<BreadcrumbList>
|
<BreadcrumbList>
|
||||||
<BreadcrumbItem className="hidden md:block">
|
<BreadcrumbItem className="hidden md:block">
|
||||||
<BreadcrumbLink asChild>
|
<BreadcrumbLink asChild>
|
||||||
<Link href={sectionLink}>{sectionLabel}</Link>
|
<Link to={sectionLink}>{sectionLabel}</Link>
|
||||||
</BreadcrumbLink>
|
</BreadcrumbLink>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
<BreadcrumbSeparator className="hidden md:block" />
|
<BreadcrumbSeparator className="hidden md:block" />
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import MarketPage from '@/app/home/plugins/components/plugin-market/PluginMarketComponent';
|
import MarketPage from '@/app/home/plugins/components/plugin-market/PluginMarketComponent';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
@@ -30,7 +28,7 @@ import { toast } from 'sonner';
|
|||||||
|
|
||||||
export default function MCPDetailContent({ id }: { id: string }) {
|
export default function MCPDetailContent({ id }: { id: string }) {
|
||||||
const isCreateMode = id === 'new';
|
const isCreateMode = id === 'new';
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { refreshMCPServers, mcpServers, setDetailEntityName } =
|
const { refreshMCPServers, mcpServers, setDetailEntityName } =
|
||||||
useSidebarData();
|
useSidebarData();
|
||||||
@@ -96,12 +94,12 @@ export default function MCPDetailContent({ id }: { id: string }) {
|
|||||||
|
|
||||||
function handleServerDeleted() {
|
function handleServerDeleted() {
|
||||||
refreshMCPServers();
|
refreshMCPServers();
|
||||||
router.push('/home/mcp');
|
navigate('/home/mcp');
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNewServerCreated(serverName: string) {
|
function handleNewServerCreated(serverName: string) {
|
||||||
refreshMCPServers();
|
refreshMCPServers();
|
||||||
router.push(`/home/mcp?id=${encodeURIComponent(serverName)}`);
|
navigate(`/home/mcp?id=${encodeURIComponent(serverName)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete() {
|
function confirmDelete() {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
useState,
|
useState,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
'use client';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import MCPDetailContent from './MCPDetailContent';
|
import MCPDetailContent from './MCPDetailContent';
|
||||||
|
|
||||||
export default function MCPPage() {
|
export default function MCPPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const detailId = searchParams.get('id');
|
const detailId = searchParams.get('id');
|
||||||
|
|
||||||
if (detailId) {
|
if (detailId) {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
MessageChainComponent,
|
MessageChainComponent,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { MessageDetails } from '../types/monitoring';
|
import { MessageDetails } from '../types/monitoring';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
|
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import MetricCard from './MetricCard';
|
import MetricCard from './MetricCard';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { FilterState, TimeRangeOption, DateRange } from '../types/monitoring';
|
import { FilterState, TimeRangeOption, DateRange } from '../types/monitoring';
|
||||||
import { getPresetDateRange } from '../utils/dateUtils';
|
import { getPresetDateRange } from '../utils/dateUtils';
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ import { getPresetDateRange } from '../utils/dateUtils';
|
|||||||
* Custom hook for managing monitoring filters
|
* Custom hook for managing monitoring filters
|
||||||
*/
|
*/
|
||||||
export function useMonitoringFilters() {
|
export function useMonitoringFilters() {
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
// Initialize filters from URL params
|
// Initialize filters from URL params
|
||||||
const [selectedBots, setSelectedBots] = useState<string[]>(() => {
|
const [selectedBots, setSelectedBots] = useState<string[]>(() => {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { Suspense, useState, useMemo } from 'react';
|
import React, { Suspense, useState, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import PipelineFormComponent from '@/app/home/pipelines/components/pipeline-form/PipelineFormComponent';
|
import PipelineFormComponent from '@/app/home/pipelines/components/pipeline-form/PipelineFormComponent';
|
||||||
@@ -13,7 +11,7 @@ import { Settings, Bug, BarChart3 } from 'lucide-react';
|
|||||||
|
|
||||||
export default function PipelineDetailContent({ id }: { id: string }) {
|
export default function PipelineDetailContent({ id }: { id: string }) {
|
||||||
const isCreateMode = id === 'new';
|
const isCreateMode = id === 'new';
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { refreshPipelines, pipelines, setDetailEntityName } = useSidebarData();
|
const { refreshPipelines, pipelines, setDetailEntityName } = useSidebarData();
|
||||||
|
|
||||||
@@ -38,7 +36,7 @@ export default function PipelineDetailContent({ id }: { id: string }) {
|
|||||||
|
|
||||||
function handleNewPipelineCreated(newPipelineId: string) {
|
function handleNewPipelineCreated(newPipelineId: string) {
|
||||||
refreshPipelines();
|
refreshPipelines();
|
||||||
router.push(`/home/pipelines?id=${encodeURIComponent(newPipelineId)}`);
|
navigate(`/home/pipelines?id=${encodeURIComponent(newPipelineId)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Create Mode ====================
|
// ==================== Create Mode ====================
|
||||||
@@ -73,7 +71,7 @@ export default function PipelineDetailContent({ id }: { id: string }) {
|
|||||||
|
|
||||||
function handleDeletePipeline() {
|
function handleDeletePipeline() {
|
||||||
refreshPipelines();
|
refreshPipelines();
|
||||||
router.push('/home/pipelines');
|
navigate('/home/pipelines');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Edit Mode ====================
|
// ==================== Edit Mode ====================
|
||||||
@@ -129,7 +127,7 @@ export default function PipelineDetailContent({ id }: { id: string }) {
|
|||||||
onFinish={handleFinish}
|
onFinish={handleFinish}
|
||||||
onNewPipelineCreated={handleNewPipelineCreated}
|
onNewPipelineCreated={handleNewPipelineCreated}
|
||||||
onDeletePipeline={handleDeletePipeline}
|
onDeletePipeline={handleDeletePipeline}
|
||||||
onCancel={() => router.push('/home/pipelines')}
|
onCancel={() => navigate('/home/pipelines')}
|
||||||
onDirtyChange={setFormDirty}
|
onDirtyChange={setFormDirty}
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
@@ -152,7 +150,7 @@ export default function PipelineDetailContent({ id }: { id: string }) {
|
|||||||
<PipelineMonitoringTab
|
<PipelineMonitoringTab
|
||||||
pipelineId={id}
|
pipelineId={id}
|
||||||
onNavigateToMonitoring={() => {
|
onNavigateToMonitoring={() => {
|
||||||
router.push('/home/monitoring');
|
navigate('/home/monitoring');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { backendClient } from '@/app/infra/http';
|
import { backendClient } from '@/app/infra/http';
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
'use client';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PipelineDetailContent from './PipelineDetailContent';
|
import PipelineDetailContent from './PipelineDetailContent';
|
||||||
|
|
||||||
export default function PipelineConfigPage() {
|
export default function PipelineConfigPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const detailId = searchParams.get('id');
|
const detailId = searchParams.get('id');
|
||||||
|
|
||||||
if (detailId) {
|
if (detailId) {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import PluginForm from '@/app/home/plugins/components/plugin-installed/plugin-form/PluginForm';
|
import PluginForm from '@/app/home/plugins/components/plugin-installed/plugin-form/PluginForm';
|
||||||
import PluginReadme from '@/app/home/plugins/components/plugin-installed/plugin-readme/PluginReadme';
|
import PluginReadme from '@/app/home/plugins/components/plugin-installed/plugin-readme/PluginReadme';
|
||||||
|
|||||||
-2
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
createContext,
|
createContext,
|
||||||
useContext,
|
useContext,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Progress } from '@/components/ui/progress';
|
import { Progress } from '@/components/ui/progress';
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
|
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { PluginCardVO } from '@/app/home/plugins/components/plugin-installed/PluginCardVO';
|
import { PluginCardVO } from '@/app/home/plugins/components/plugin-installed/PluginCardVO';
|
||||||
import PluginCardComponent from '@/app/home/plugins/components/plugin-installed/plugin-card/PluginCardComponent';
|
import PluginCardComponent from '@/app/home/plugins/components/plugin-installed/plugin-card/PluginCardComponent';
|
||||||
import styles from '@/app/home/plugins/plugins.module.css';
|
import styles from '@/app/home/plugins/plugins.module.css';
|
||||||
@@ -33,11 +31,10 @@ enum PluginOperationType {
|
|||||||
UPDATE = 'UPDATE',
|
UPDATE = 'UPDATE',
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
const PluginInstalledComponent = forwardRef<PluginInstalledComponentRef>(
|
const PluginInstalledComponent = forwardRef<PluginInstalledComponentRef>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { refreshPlugins } = useSidebarData();
|
const { refreshPlugins } = useSidebarData();
|
||||||
const [pluginList, setPluginList] = useState<PluginCardVO[]>([]);
|
const [pluginList, setPluginList] = useState<PluginCardVO[]>([]);
|
||||||
const [showOperationModal, setShowOperationModal] = useState(false);
|
const [showOperationModal, setShowOperationModal] = useState(false);
|
||||||
@@ -163,7 +160,7 @@ const PluginInstalledComponent = forwardRef<PluginInstalledComponentRef>(
|
|||||||
|
|
||||||
function handlePluginClick(plugin: PluginCardVO) {
|
function handlePluginClick(plugin: PluginCardVO) {
|
||||||
const pluginId = `${plugin.author}/${plugin.name}`;
|
const pluginId = `${plugin.author}/${plugin.name}`;
|
||||||
router.push(`/home/plugins?id=${encodeURIComponent(pluginId)}`);
|
navigate(`/home/plugins?id=${encodeURIComponent(pluginId)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePluginDelete(plugin: PluginCardVO) {
|
function handlePluginDelete(plugin: PluginCardVO) {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect, useCallback, useRef, Suspense } from 'react';
|
import { useState, useEffect, useCallback, useRef, Suspense } from 'react';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -47,7 +45,7 @@ function MarketPageContent({
|
|||||||
installPlugin: (plugin: PluginV4) => void;
|
installPlugin: (plugin: PluginV4) => void;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const validCategories = [
|
const validCategories = [
|
||||||
'Tool',
|
'Tool',
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||||
import { ChevronLeft, ChevronRight, Star } from 'lucide-react';
|
import { ChevronLeft, ChevronRight, Star } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import MCPCardComponent from '@/app/home/plugins/mcp-server/mcp-card/MCPCardComponent';
|
import MCPCardComponent from '@/app/home/plugins/mcp-server/mcp-card/MCPCardComponent';
|
||||||
import { MCPCardVO } from '@/app/home/plugins/mcp-server/MCPCardVO';
|
import { MCPCardVO } from '@/app/home/plugins/mcp-server/MCPCardVO';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Resolver, useForm } from 'react-hook-form';
|
import { Resolver, useForm } from 'react-hook-form';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use client';
|
|
||||||
import PluginInstalledComponent, {
|
import PluginInstalledComponent, {
|
||||||
PluginInstalledComponentRef,
|
PluginInstalledComponentRef,
|
||||||
} from '@/app/home/plugins/components/plugin-installed/PluginInstalledComponent';
|
} from '@/app/home/plugins/components/plugin-installed/PluginInstalledComponent';
|
||||||
@@ -45,7 +44,7 @@ import {
|
|||||||
} from '@/components/ui/card';
|
} from '@/components/ui/card';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
||||||
import { useSearchParams, useRouter } from 'next/navigation';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -84,7 +83,7 @@ interface GithubAsset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PluginConfigPage() {
|
export default function PluginConfigPage() {
|
||||||
const searchParams = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const detailId = searchParams.get('id');
|
const detailId = searchParams.get('id');
|
||||||
|
|
||||||
// Show plugin detail view when ?id= query param is present
|
// Show plugin detail view when ?id= query param is present
|
||||||
@@ -97,7 +96,7 @@ export default function PluginConfigPage() {
|
|||||||
|
|
||||||
function PluginListView() {
|
function PluginListView() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const {
|
const {
|
||||||
refreshPlugins,
|
refreshPlugins,
|
||||||
pendingPluginInstallAction,
|
pendingPluginInstallAction,
|
||||||
@@ -672,7 +671,7 @@ function PluginListView() {
|
|||||||
{systemInfo.enable_marketplace && (
|
{systemInfo.enable_marketplace && (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push('/home/market');
|
navigate('/home/market');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StoreIcon className="w-4 h-4" />
|
<StoreIcon className="w-4 h-4" />
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ export let userInfo: {
|
|||||||
* 获取基础 URL
|
* 获取基础 URL
|
||||||
*/
|
*/
|
||||||
const getBaseURL = (): string => {
|
const getBaseURL = (): string => {
|
||||||
if (typeof window !== 'undefined' && process.env.NEXT_PUBLIC_API_BASE_URL) {
|
if (typeof window !== 'undefined' && import.meta.env.VITE_API_BASE_URL) {
|
||||||
return process.env.NEXT_PUBLIC_API_BASE_URL;
|
return import.meta.env.VITE_API_BASE_URL;
|
||||||
}
|
}
|
||||||
return '/';
|
return '/';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,10 +78,10 @@ export class WebSocketClient {
|
|||||||
|
|
||||||
// 构建WebSocket URL
|
// 构建WebSocket URL
|
||||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
// extract host from process.env.NEXT_PUBLIC_API_BASE_URL
|
// extract host from import.meta.env.VITE_API_BASE_URL
|
||||||
// 如果环境变量未定义,使用当前页面的 host (适配生产环境)
|
// If env var is undefined, use current page host (for production)
|
||||||
const host =
|
const host =
|
||||||
process.env.NEXT_PUBLIC_API_BASE_URL?.split('://')[1] ||
|
import.meta.env.VITE_API_BASE_URL?.split('://')[1] ||
|
||||||
window.location.host;
|
window.location.host;
|
||||||
const url = `${protocol}//${host}/api/v1/pipelines/${this.pipelineId}/ws/connect?session_type=${this.sessionType}`;
|
const url = `${protocol}//${host}/api/v1/pipelines/${this.pipelineId}/ws/connect?session_type=${this.sessionType}`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import './global.css';
|
|
||||||
import 'react-photo-view/dist/react-photo-view.css';
|
|
||||||
import type { Metadata } from 'next';
|
|
||||||
import { Toaster } from '@/components/ui/sonner';
|
|
||||||
import I18nProvider from '@/i18n/I18nProvider';
|
|
||||||
import { ThemeProvider } from '@/components/providers/theme-provider';
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'LangBot',
|
|
||||||
description:
|
|
||||||
'Production-grade platform for building agentic IM bots, integrated with Telegram, Slack, Discord, WeChat, QQ, etc.',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout({
|
|
||||||
children,
|
|
||||||
}: Readonly<{
|
|
||||||
children: React.ReactNode;
|
|
||||||
}>) {
|
|
||||||
return (
|
|
||||||
<html lang="zh" suppressHydrationWarning>
|
|
||||||
<body className={``}>
|
|
||||||
<ThemeProvider>
|
|
||||||
<I18nProvider>
|
|
||||||
{children}
|
|
||||||
<Toaster />
|
|
||||||
</I18nProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default function LoginLayout({
|
export default function LoginLayout({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use client';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import {
|
import {
|
||||||
@@ -22,12 +21,12 @@ import {
|
|||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { httpClient, initializeUserInfo } from '@/app/infra/http';
|
import { httpClient, initializeUserInfo } from '@/app/infra/http';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Mail, Lock, Loader2, AlertCircle, RefreshCw } from 'lucide-react';
|
import { Mail, Lock, Loader2, AlertCircle, RefreshCw } from 'lucide-react';
|
||||||
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
import langbotIcon from '@/app/assets/langbot-logo.webp';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Link from 'next/link';
|
import { Link } from 'react-router-dom';
|
||||||
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
||||||
import { LoadingSpinner } from '@/components/ui/loading-spinner';
|
import { LoadingSpinner } from '@/components/ui/loading-spinner';
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ const formSchema = (t: (key: string) => string) =>
|
|||||||
type AccountType = 'local' | 'space';
|
type AccountType = 'local' | 'space';
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [spaceLoading, setSpaceLoading] = useState(false);
|
const [spaceLoading, setSpaceLoading] = useState(false);
|
||||||
const [accountType, setAccountType] = useState<AccountType | null>(null);
|
const [accountType, setAccountType] = useState<AccountType | null>(null);
|
||||||
@@ -66,7 +65,7 @@ export default function Login() {
|
|||||||
setLoadError(null);
|
setLoadError(null);
|
||||||
const res = await httpClient.getAccountInfo();
|
const res = await httpClient.getAccountInfo();
|
||||||
if (!res.initialized) {
|
if (!res.initialized) {
|
||||||
router.push('/register');
|
navigate('/register');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setAccountType(res.account_type || 'local');
|
setAccountType(res.account_type || 'local');
|
||||||
@@ -97,7 +96,7 @@ export default function Login() {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.token) {
|
if (res.token) {
|
||||||
localStorage.setItem('token', res.token);
|
localStorage.setItem('token', res.token);
|
||||||
router.push('/home');
|
navigate('/home');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
@@ -114,7 +113,7 @@ export default function Login() {
|
|||||||
localStorage.setItem('token', res.token);
|
localStorage.setItem('token', res.token);
|
||||||
localStorage.setItem('userEmail', username);
|
localStorage.setItem('userEmail', username);
|
||||||
await initializeUserInfo();
|
await initializeUserInfo();
|
||||||
router.push('/home');
|
navigate('/home');
|
||||||
toast.success(t('common.loginSuccess'));
|
toast.success(t('common.loginSuccess'));
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -154,7 +153,7 @@ export default function Login() {
|
|||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon}
|
||||||
alt="LangBot"
|
alt="LangBot"
|
||||||
className="w-16 h-16 mb-4 mx-auto"
|
className="w-16 h-16 mb-4 mx-auto"
|
||||||
/>
|
/>
|
||||||
@@ -205,7 +204,7 @@ export default function Login() {
|
|||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon}
|
||||||
alt="LangBot"
|
alt="LangBot"
|
||||||
className="w-16 h-16 mb-4 mx-auto"
|
className="w-16 h-16 mb-4 mx-auto"
|
||||||
/>
|
/>
|
||||||
@@ -313,7 +312,7 @@ export default function Login() {
|
|||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<FormLabel>{t('common.password')}</FormLabel>
|
<FormLabel>{t('common.password')}</FormLabel>
|
||||||
<Link
|
<Link
|
||||||
href="/reset-password"
|
to="/reset-password"
|
||||||
className="text-sm text-blue-500"
|
className="text-sm text-blue-500"
|
||||||
>
|
>
|
||||||
{t('common.forgotPassword')}
|
{t('common.forgotPassword')}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
'use client';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
router.push('/login');
|
navigate('/login');
|
||||||
}, []);
|
}, []);
|
||||||
return <div className={``}></div>;
|
return <div className={``}></div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default function RegisterLayout({
|
export default function RegisterLayout({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use client';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import {
|
import {
|
||||||
@@ -22,7 +21,7 @@ import {
|
|||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Mail, Lock, Loader2, Info } from 'lucide-react';
|
import { Mail, Lock, Loader2, Info } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
@@ -42,7 +41,7 @@ const formSchema = (t: (key: string) => string) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default function Register() {
|
export default function Register() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [spaceLoading, setSpaceLoading] = useState(false);
|
const [spaceLoading, setSpaceLoading] = useState(false);
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ export default function Register() {
|
|||||||
.checkIfInited()
|
.checkIfInited()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.initialized) {
|
if (res.initialized) {
|
||||||
router.push('/login');
|
navigate('/login');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
@@ -78,7 +77,7 @@ export default function Register() {
|
|||||||
.initUser(username, password)
|
.initUser(username, password)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(t('register.initSuccess'));
|
toast.success(t('register.initSuccess'));
|
||||||
router.push('/login');
|
navigate('/login');
|
||||||
})
|
})
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
toast.error(t('register.initFailed') + (err as CustomApiError).msg);
|
toast.error(t('register.initFailed') + (err as CustomApiError).msg);
|
||||||
@@ -114,7 +113,7 @@ export default function Register() {
|
|||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={langbotIcon.src}
|
src={langbotIcon}
|
||||||
alt="LangBot"
|
alt="LangBot"
|
||||||
className="w-16 h-16 mb-4 mx-auto"
|
className="w-16 h-16 mb-4 mx-auto"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default function ResetPasswordLayout({
|
export default function ResetPasswordLayout({
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
'use client';
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import {
|
import {
|
||||||
@@ -28,11 +27,11 @@ import {
|
|||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Mail, Lock, ArrowLeft } from 'lucide-react';
|
import { Mail, Lock, ArrowLeft } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Link from 'next/link';
|
import { Link } from 'react-router-dom';
|
||||||
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
||||||
|
|
||||||
const REGEXP_ONLY_DIGITS_AND_CHARS = /^[0-9a-zA-Z]+$/;
|
const REGEXP_ONLY_DIGITS_AND_CHARS = /^[0-9a-zA-Z]+$/;
|
||||||
@@ -45,7 +44,7 @@ const formSchema = (t: (key: string) => string) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default function ResetPassword() {
|
export default function ResetPassword() {
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isResetting, setIsResetting] = useState(false);
|
const [isResetting, setIsResetting] = useState(false);
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ export default function ResetPassword() {
|
|||||||
.resetPassword(email, recoveryKey, newPassword)
|
.resetPassword(email, recoveryKey, newPassword)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(t('resetPassword.resetSuccess'));
|
toast.success(t('resetPassword.resetSuccess'));
|
||||||
router.push('/login');
|
navigate('/login');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error(t('resetPassword.resetFailed'));
|
toast.error(t('resetPassword.resetFailed'));
|
||||||
@@ -88,7 +87,7 @@ export default function ResetPassword() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<Link
|
<Link
|
||||||
href="/login"
|
to="/login"
|
||||||
className="flex items-center text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100 transition-colors"
|
className="flex items-center text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100 transition-colors"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="h-4 w-4 mr-1" />
|
<ArrowLeft className="h-4 w-4 mr-1" />
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { UUID } from 'uuidjs';
|
import { UUID } from 'uuidjs';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -81,7 +79,7 @@ const TOTAL_STEPS = 4;
|
|||||||
|
|
||||||
export default function WizardPage() {
|
export default function WizardPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// ---- Wizard state ----
|
// ---- Wizard state ----
|
||||||
const [currentStep, setCurrentStep] = useState(0);
|
const [currentStep, setCurrentStep] = useState(0);
|
||||||
@@ -519,8 +517,8 @@ export default function WizardPage() {
|
|||||||
}
|
}
|
||||||
setIsSkipping(false);
|
setIsSkipping(false);
|
||||||
setShowSkipConfirm(false);
|
setShowSkipConfirm(false);
|
||||||
router.push('/home');
|
navigate('/home');
|
||||||
}, [router, t]);
|
}, [navigate, t]);
|
||||||
|
|
||||||
// ---- Render ----
|
// ---- Render ----
|
||||||
|
|
||||||
@@ -1169,7 +1167,7 @@ function StepAIEngine({
|
|||||||
|
|
||||||
function StepDone() {
|
function StepDone() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [particles] = useState(() =>
|
const [particles] = useState(() =>
|
||||||
Array.from({ length: 30 }, (_, i) => ({
|
Array.from({ length: 30 }, (_, i) => ({
|
||||||
@@ -1213,8 +1211,8 @@ function StepDone() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setIsCompleting(false);
|
setIsCompleting(false);
|
||||||
router.push('/home/bots');
|
navigate('/home/bots');
|
||||||
}, [router, t]);
|
}, [navigate, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex flex-col items-center justify-center h-full min-h-[400px]">
|
<div className="relative flex flex-col items-center justify-center h-full min-h-[400px]">
|
||||||
@@ -1244,7 +1242,7 @@ function StepDone() {
|
|||||||
{t('wizard.done.backToWorkbench')}
|
{t('wizard.done.backToWorkbench')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<style jsx>{`
|
<style>{`
|
||||||
@keyframes wizardConfetti {
|
@keyframes wizardConfetti {
|
||||||
0% {
|
0% {
|
||||||
transform: translateY(100vh) rotate(0deg);
|
transform: translateY(100vh) rotate(0deg);
|
||||||
|
|||||||
@@ -1,18 +1,79 @@
|
|||||||
'use client';
|
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
type Theme = 'light' | 'dark' | 'system';
|
||||||
import { type ThemeProviderProps } from 'next-themes';
|
|
||||||
|
interface ThemeProviderProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
defaultTheme?: Theme;
|
||||||
|
storageKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ThemeProviderState {
|
||||||
|
theme: Theme;
|
||||||
|
setTheme: (theme: Theme) => void;
|
||||||
|
resolvedTheme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: ThemeProviderState = {
|
||||||
|
theme: 'system',
|
||||||
|
setTheme: () => null,
|
||||||
|
resolvedTheme: 'light',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
||||||
|
|
||||||
|
export function ThemeProvider({
|
||||||
|
children,
|
||||||
|
defaultTheme = 'system',
|
||||||
|
storageKey = 'langbot-theme',
|
||||||
|
...props
|
||||||
|
}: ThemeProviderProps) {
|
||||||
|
const [theme, setTheme] = useState<Theme>(
|
||||||
|
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
|
||||||
|
);
|
||||||
|
const [resolvedTheme, setResolvedTheme] = useState('light');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = window.document.documentElement;
|
||||||
|
|
||||||
|
root.classList.remove('light', 'dark');
|
||||||
|
|
||||||
|
if (theme === 'system') {
|
||||||
|
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
.matches
|
||||||
|
? 'dark'
|
||||||
|
: 'light';
|
||||||
|
|
||||||
|
root.classList.add(systemTheme);
|
||||||
|
setResolvedTheme(systemTheme);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.classList.add(theme);
|
||||||
|
setResolvedTheme(theme);
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
theme,
|
||||||
|
setTheme: (theme: Theme) => {
|
||||||
|
localStorage.setItem(storageKey, theme);
|
||||||
|
setTheme(theme);
|
||||||
|
},
|
||||||
|
resolvedTheme,
|
||||||
|
};
|
||||||
|
|
||||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
|
||||||
return (
|
return (
|
||||||
<NextThemesProvider
|
<ThemeProviderContext.Provider {...props} value={value}>
|
||||||
attribute="class"
|
|
||||||
defaultTheme="system"
|
|
||||||
enableSystem
|
|
||||||
disableTransitionOnChange
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</NextThemesProvider>
|
</ThemeProviderContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useTheme = () => {
|
||||||
|
const context = useContext(ThemeProviderContext);
|
||||||
|
|
||||||
|
if (context === undefined)
|
||||||
|
throw new Error('useTheme must be used within a ThemeProvider');
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||||
import { CheckIcon } from 'lucide-react';
|
import { CheckIcon } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
|
||||||
|
|
||||||
function Collapsible({
|
function Collapsible({
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
|
import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
|
||||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||||
import { XIcon } from 'lucide-react';
|
import { XIcon } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
||||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||||
import { Slot } from '@radix-ui/react-slot';
|
import { Slot } from '@radix-ui/react-slot';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
|
import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { OTPInput, OTPInputContext } from 'input-otp';
|
import { OTPInput, OTPInputContext } from 'input-otp';
|
||||||
import { MinusIcon } from 'lucide-react';
|
import { MinusIcon } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user