doc: add i18n to log page
This commit is contained in:
@@ -219,5 +219,19 @@
|
|||||||
"dashboards": "Dashboards",
|
"dashboards": "Dashboards",
|
||||||
"new": "New",
|
"new": "New",
|
||||||
"pinChartToDashboard": "Pin chart to dashboard",
|
"pinChartToDashboard": "Pin chart to dashboard",
|
||||||
"selectDashboardToPin": "Select a dashboard to pin this chart to."
|
"selectDashboardToPin": "Select a dashboard to pin this chart to.",
|
||||||
|
"welcomeBack": "Welcome Back",
|
||||||
|
"createAccount": "Create Account",
|
||||||
|
"username": "Username",
|
||||||
|
"enterUsername": "Enter your username",
|
||||||
|
"email": "Email",
|
||||||
|
"enterEmail": "Enter your email",
|
||||||
|
"password": "Password",
|
||||||
|
"enterPassword": "Enter your password",
|
||||||
|
"signIn": "Sign In",
|
||||||
|
"signUp": "Sign Up",
|
||||||
|
"dontHaveAccount": "Don't have an account?",
|
||||||
|
"alreadyHaveAccount": "Already have an account?",
|
||||||
|
"registrationSuccess": "Registration successful! Please login.",
|
||||||
|
"errorOccurred": "An error occurred"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,5 +219,19 @@
|
|||||||
"threads": "会话",
|
"threads": "会话",
|
||||||
"archivedThreads": "已归档会话",
|
"archivedThreads": "已归档会话",
|
||||||
"defaultUser": "默认用户",
|
"defaultUser": "默认用户",
|
||||||
"searchSkills": "搜索技能..."
|
"searchSkills": "搜索技能...",
|
||||||
|
"welcomeBack": "欢迎回来",
|
||||||
|
"createAccount": "创建账号",
|
||||||
|
"username": "用户名",
|
||||||
|
"enterUsername": "请输入您的用户名",
|
||||||
|
"email": "邮箱",
|
||||||
|
"enterEmail": "请输入您的邮箱",
|
||||||
|
"password": "密码",
|
||||||
|
"enterPassword": "请输入您的密码",
|
||||||
|
"signIn": "登录",
|
||||||
|
"signUp": "注册",
|
||||||
|
"dontHaveAccount": "还没有账号?",
|
||||||
|
"alreadyHaveAccount": "已经有账号了?",
|
||||||
|
"registrationSuccess": "注册成功!请登录。",
|
||||||
|
"errorOccurred": "发生了一个错误"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
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 { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2, Languages } from "lucide-react";
|
||||||
import { api } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
import { useAuthStore } from "@/store/authStore";
|
import { useAuthStore } from "@/store/authStore";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
export function Login() {
|
export function Login() {
|
||||||
|
const { t, i18n } = useTranslation();
|
||||||
const [isLogin, setIsLogin] = useState(true);
|
const [isLogin, setIsLogin] = useState(true);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
@@ -58,10 +66,10 @@ export function Login() {
|
|||||||
|
|
||||||
// Auto login after successful registration
|
// Auto login after successful registration
|
||||||
setIsLogin(true);
|
setIsLogin(true);
|
||||||
setError("Registration successful! Please login.");
|
setError(t("registrationSuccess"));
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message || "An error occurred");
|
setError(err.message || t("errorOccurred"));
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -70,33 +78,50 @@ export function Login() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col items-center justify-center bg-zinc-50 px-4">
|
<div className="min-h-screen flex flex-col items-center justify-center bg-zinc-50 px-4">
|
||||||
<div className="w-full max-w-md">
|
<div className="w-full max-w-md">
|
||||||
<div className="mb-10 text-center flex flex-col items-center gap-4 select-none">
|
<div className="mb-10 text-center flex flex-col items-center gap-4 select-none relative">
|
||||||
<div className="text-[56px] leading-none animate-bounce-slow pb-2">
|
<div className="text-[56px] leading-none animate-bounce-slow pb-2">
|
||||||
🦞
|
🦞
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-[40px] font-bold bg-clip-text text-transparent bg-gradient-to-r from-red-500 via-orange-500 to-amber-500 tracking-tight">
|
<h1 className="text-[40px] font-bold bg-clip-text text-transparent bg-gradient-to-r from-red-500 via-orange-500 to-amber-500 tracking-tight">
|
||||||
DataClaw
|
DataClaw
|
||||||
</h1>
|
</h1>
|
||||||
|
<div className="absolute right-0 bottom-0 translate-y-4">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" size="icon" className="h-9 w-9 rounded-full bg-white/50 backdrop-blur-sm shadow-sm border border-zinc-200/50 text-zinc-500 hover:text-zinc-900 hover:bg-white transition-all">
|
||||||
|
<Languages className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-32">
|
||||||
|
<DropdownMenuItem onClick={() => i18n.changeLanguage('zh')} className={i18n.language === 'zh' ? 'bg-zinc-100' : ''}>
|
||||||
|
简体中文
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => i18n.changeLanguage('en')} className={i18n.language === 'en' ? 'bg-zinc-100' : ''}>
|
||||||
|
English
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white rounded-2xl shadow-xl border border-zinc-100 p-8">
|
<div className="bg-white rounded-2xl shadow-xl border border-zinc-100 p-8">
|
||||||
<h2 className="text-2xl font-bold text-zinc-800 mb-6 text-center">
|
<h2 className="text-2xl font-bold text-zinc-800 mb-6 text-center">
|
||||||
{isLogin ? "Welcome Back" : "Create Account"}
|
{isLogin ? t("welcomeBack") : t("createAccount")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className={`p-3 rounded-lg mb-6 text-sm ${error.includes("successful") ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"}`}>
|
<div className={`p-3 rounded-lg mb-6 text-sm ${error.includes(t("registrationSuccess")) ? "bg-emerald-50 text-emerald-600" : "bg-red-50 text-red-600"}`}>
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-5">
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="username">Username</Label>
|
<Label htmlFor="username">{t("username")}</Label>
|
||||||
<Input
|
<Input
|
||||||
id="username"
|
id="username"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter your username"
|
placeholder={t("enterUsername")}
|
||||||
value={formData.username}
|
value={formData.username}
|
||||||
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
||||||
required
|
required
|
||||||
@@ -106,11 +131,11 @@ export function Login() {
|
|||||||
|
|
||||||
{!isLogin && (
|
{!isLogin && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="email">Email</Label>
|
<Label htmlFor="email">{t("email")}</Label>
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="Enter your email"
|
placeholder={t("enterEmail")}
|
||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||||
required
|
required
|
||||||
@@ -120,11 +145,11 @@ export function Login() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="password">Password</Label>
|
<Label htmlFor="password">{t("password")}</Label>
|
||||||
<Input
|
<Input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Enter your password"
|
placeholder={t("enterPassword")}
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
||||||
required
|
required
|
||||||
@@ -140,13 +165,13 @@ export function Login() {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Loader2 className="h-5 w-5 animate-spin" />
|
<Loader2 className="h-5 w-5 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
isLogin ? "Sign In" : "Sign Up"
|
isLogin ? t("signIn") : t("signUp")
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div className="mt-6 text-center text-sm text-zinc-500">
|
<div className="mt-6 text-center text-sm text-zinc-500">
|
||||||
{isLogin ? "Don't have an account?" : "Already have an account?"}
|
{isLogin ? t("dontHaveAccount") : t("alreadyHaveAccount")}
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsLogin(!isLogin);
|
setIsLogin(!isLogin);
|
||||||
@@ -154,7 +179,7 @@ export function Login() {
|
|||||||
}}
|
}}
|
||||||
className="ml-2 font-medium text-indigo-600 hover:text-indigo-700 transition-colors"
|
className="ml-2 font-medium text-indigo-600 hover:text-indigo-700 transition-colors"
|
||||||
>
|
>
|
||||||
{isLogin ? "Sign Up" : "Sign In"}
|
{isLogin ? t("signUp") : t("signIn")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user