From aabc5e801429906e34985a55b562c329f1915e6e Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Sat, 18 Apr 2026 21:56:50 +0700 Subject: [PATCH] feat(web): add demo accounts panel to login page for MVP Click-to-fill panel above the login form showing 4 seeded accounts (ADMIN/AGENT/SELLER/BUYER) with role badges. Clicking an account populates phone + shared demo password into the form, letting stakeholders try each role without memorizing credentials. Panel is collapsible and labeled "(MVP)" so it's obvious this is demo-only scaffolding to remove before production. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web/app/[locale]/(auth)/login/page.tsx | 63 ++++++++++++++++++++- apps/web/messages/en.json | 2 + apps/web/messages/vi.json | 2 + 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/apps/web/app/[locale]/(auth)/login/page.tsx b/apps/web/app/[locale]/(auth)/login/page.tsx index 4d132c8..3e503e7 100644 --- a/apps/web/app/[locale]/(auth)/login/page.tsx +++ b/apps/web/app/[locale]/(auth)/login/page.tsx @@ -1,12 +1,13 @@ 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Loader2 } from 'lucide-react'; +import { ChevronDown, Loader2, Sparkles } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useTranslations } from 'next-intl'; import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { OAuthButtons } from '@/components/auth/oauth-buttons'; +import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; @@ -15,11 +16,21 @@ import { Link } from '@/i18n/navigation'; import { useAuthStore } from '@/lib/auth-store'; import { loginSchema, type LoginFormData } from '@/lib/validations/auth'; +const DEMO_PASSWORD = 'Velik@2026'; + +const DEMO_ACCOUNTS: { phone: string; name: string; role: 'ADMIN' | 'AGENT' | 'SELLER' | 'BUYER'; badgeClass: string }[] = [ + { phone: '+84876677771', name: 'Hồ Ngọc Hải', role: 'ADMIN', badgeClass: 'bg-red-500/10 text-red-600 border-red-500/20' }, + { phone: '+84900000002', name: 'Nguyễn Văn An', role: 'AGENT', badgeClass: 'bg-blue-500/10 text-blue-600 border-blue-500/20' }, + { phone: '+84900000005', name: 'Phạm Đức Dũng', role: 'SELLER', badgeClass: 'bg-amber-500/10 text-amber-600 border-amber-500/20' }, + { phone: '+84900000004', name: 'Lê Minh Cường', role: 'BUYER', badgeClass: 'bg-emerald-500/10 text-emerald-600 border-emerald-500/20' }, +]; + export default function LoginPage() { const router = useRouter(); const searchParams = useSearchParams(); const { login, isLoading, error, clearError } = useAuthStore(); const [showPassword, setShowPassword] = useState(false); + const [demoOpen, setDemoOpen] = useState(true); const t = useTranslations('auth'); const oauthError = searchParams.get('error'); @@ -30,11 +41,18 @@ export default function LoginPage() { const { register, handleSubmit, + setValue, formState: { errors }, } = useForm({ resolver: zodResolver(loginSchema), }); + const fillDemoAccount = (phone: string) => { + setValue('phone', phone, { shouldValidate: true }); + setValue('password', DEMO_PASSWORD, { shouldValidate: true }); + clearError(); + }; + const onSubmit = async (data: LoginFormData) => { try { await login(data); @@ -51,6 +69,49 @@ export default function LoginPage() { {t('loginDescription')} + {/* Demo accounts panel — MVP only */} +
+ + {demoOpen && ( +
+

+ {t('demoAccountsHint')} {DEMO_PASSWORD} +

+
    + {DEMO_ACCOUNTS.map((acc) => ( +
  • + +
  • + ))} +
+
+ )} +
+
{oauthErrorMessage && (
diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 7cb17a9..2eba8f7 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -139,6 +139,8 @@ "auth": { "loginTitle": "Login", "loginDescription": "Enter your phone number and password to log in", + "demoAccountsTitle": "Demo accounts (MVP)", + "demoAccountsHint": "Click to auto-fill. Shared password:", "phone": "Phone number", "phonePlaceholder": "0912345678", "password": "Password", diff --git a/apps/web/messages/vi.json b/apps/web/messages/vi.json index 371990f..6ab09fd 100644 --- a/apps/web/messages/vi.json +++ b/apps/web/messages/vi.json @@ -139,6 +139,8 @@ "auth": { "loginTitle": "Đăng nhập", "loginDescription": "Nhập số điện thoại và mật khẩu để đăng nhập", + "demoAccountsTitle": "Tài khoản demo (MVP)", + "demoAccountsHint": "Bấm để tự điền. Mật khẩu chung:", "phone": "Số điện thoại", "phonePlaceholder": "0912345678", "password": "Mật khẩu",