mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-07 14:26:03 +00:00
feat(web): improve login error layout and add Terms of Service link
- Improve backend connection error display with bordered container, inline icon, and better visual hierarchy - Extract actual error message from axios response object - Add Terms of Service link (https://langbot.app/terms) to login footer - Add termsOfService i18n key for all 7 locales
This commit is contained in:
@@ -75,9 +75,18 @@ export default function Login() {
|
||||
// Also check if already logged in
|
||||
checkIfAlreadyLoggedIn();
|
||||
} catch (err) {
|
||||
const errorMessage =
|
||||
err instanceof Error ? err.message : t('common.loginLoadError');
|
||||
setLoadError(errorMessage);
|
||||
let detail = '';
|
||||
if (err instanceof Error) {
|
||||
detail = err.message;
|
||||
} else if (
|
||||
err &&
|
||||
typeof err === 'object' &&
|
||||
'msg' in err &&
|
||||
typeof (err as Record<string, unknown>).msg === 'string'
|
||||
) {
|
||||
detail = (err as Record<string, unknown>).msg as string;
|
||||
}
|
||||
setLoadError(detail || t('common.loginLoadError'));
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
@@ -146,8 +155,8 @@ export default function Login() {
|
||||
if (loadError) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-neutral-900">
|
||||
<Card className="w-[375px] shadow-lg dark:shadow-white/10">
|
||||
<CardHeader>
|
||||
<Card className="w-[400px] shadow-lg dark:shadow-white/10">
|
||||
<CardHeader className="pb-2">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<ThemeToggle />
|
||||
<LanguageSelector />
|
||||
@@ -161,20 +170,25 @@ export default function Login() {
|
||||
{t('common.welcome')}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex flex-col items-center gap-3 py-4">
|
||||
<AlertCircle className="h-10 w-10 text-destructive" />
|
||||
<p className="text-sm text-center text-muted-foreground">
|
||||
<CardContent>
|
||||
<div className="flex flex-col items-center gap-4 rounded-lg border border-destructive/20 bg-destructive/5 p-5">
|
||||
<div className="flex items-center gap-2 text-destructive">
|
||||
<AlertCircle className="h-5 w-5 shrink-0" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('common.loginLoadError')}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-center text-muted-foreground leading-relaxed">
|
||||
{t('common.loginLoadErrorDesc')}
|
||||
</p>
|
||||
<code className="text-xs bg-muted px-3 py-2 rounded max-w-full overflow-x-auto block text-center text-muted-foreground">
|
||||
<code className="text-xs bg-muted/80 px-3 py-2 rounded-md max-w-full overflow-x-auto block text-center text-muted-foreground/80 break-all">
|
||||
{loadError}
|
||||
</code>
|
||||
<Button
|
||||
onClick={handleRetry}
|
||||
disabled={retrying}
|
||||
variant="outline"
|
||||
className="mt-2 cursor-pointer"
|
||||
className="w-full cursor-pointer"
|
||||
>
|
||||
{retrying ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
@@ -348,6 +362,15 @@ export default function Login() {
|
||||
|
||||
<p className="text-xs text-center text-muted-foreground">
|
||||
{t('common.agreementNotice')}{' '}
|
||||
<a
|
||||
href="https://langbot.app/terms"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-foreground transition-colors"
|
||||
>
|
||||
{t('common.termsOfService')}
|
||||
</a>
|
||||
{'、'}
|
||||
<a
|
||||
href="https://langbot.app/privacy"
|
||||
target="_blank"
|
||||
|
||||
@@ -63,6 +63,7 @@ const enUS = {
|
||||
test: 'Test',
|
||||
forgotPassword: 'Forgot Password?',
|
||||
agreementNotice: 'By continuing, you agree to our',
|
||||
termsOfService: 'Terms of Service',
|
||||
privacyPolicy: 'Privacy Policy',
|
||||
and: 'and',
|
||||
dataCollectionPolicy: 'Data Collection Policy',
|
||||
|
||||
@@ -64,7 +64,8 @@ const esES = {
|
||||
copyFailed: 'Error al copiar',
|
||||
test: 'Probar',
|
||||
forgotPassword: '¿Olvidaste tu contraseña?',
|
||||
agreementNotice: 'Al continuar, aceptas nuestra',
|
||||
agreementNotice: 'Al continuar, aceptas nuestros',
|
||||
termsOfService: 'Términos de servicio',
|
||||
privacyPolicy: 'Política de privacidad',
|
||||
and: 'y',
|
||||
dataCollectionPolicy: 'Política de recopilación de datos',
|
||||
|
||||
@@ -64,6 +64,7 @@ const jaJP = {
|
||||
test: 'テスト',
|
||||
forgotPassword: 'パスワードを忘れた?',
|
||||
agreementNotice: '続行することで、以下に同意したものとみなされます:',
|
||||
termsOfService: '利用規約',
|
||||
privacyPolicy: 'プライバシーポリシー',
|
||||
and: 'および',
|
||||
dataCollectionPolicy: 'データ収集ポリシー',
|
||||
|
||||
@@ -63,6 +63,7 @@ const thTH = {
|
||||
test: 'ทดสอบ',
|
||||
forgotPassword: 'ลืมรหัสผ่าน?',
|
||||
agreementNotice: 'การดำเนินการต่อแสดงว่าคุณยอมรับ',
|
||||
termsOfService: 'ข้อกำหนดการให้บริการ',
|
||||
privacyPolicy: 'นโยบายความเป็นส่วนตัว',
|
||||
and: 'และ',
|
||||
dataCollectionPolicy: 'นโยบายการเก็บรวบรวมข้อมูล',
|
||||
|
||||
@@ -63,6 +63,7 @@ const viVN = {
|
||||
test: 'Kiểm tra',
|
||||
forgotPassword: 'Quên mật khẩu?',
|
||||
agreementNotice: 'Bằng việc tiếp tục, bạn đồng ý với',
|
||||
termsOfService: 'Điều khoản dịch vụ',
|
||||
privacyPolicy: 'Chính sách bảo mật',
|
||||
and: 'và',
|
||||
dataCollectionPolicy: 'Chính sách thu thập dữ liệu',
|
||||
|
||||
@@ -62,6 +62,7 @@ const zhHans = {
|
||||
test: '测试',
|
||||
forgotPassword: '忘记密码?',
|
||||
agreementNotice: '继续即表示您同意我们的',
|
||||
termsOfService: '服务条款',
|
||||
privacyPolicy: '隐私政策',
|
||||
and: '和',
|
||||
dataCollectionPolicy: '数据收集政策',
|
||||
|
||||
@@ -62,6 +62,7 @@ const zhHant = {
|
||||
test: '測試',
|
||||
forgotPassword: '忘記密碼?',
|
||||
agreementNotice: '繼續即表示您同意我們的',
|
||||
termsOfService: '服務條款',
|
||||
privacyPolicy: '隱私政策',
|
||||
and: '和',
|
||||
dataCollectionPolicy: '數據收集政策',
|
||||
|
||||
Reference in New Issue
Block a user