feat: add shadcn admin workspace

This commit is contained in:
2026-03-28 17:56:36 +08:00
parent ec96d91548
commit 178434d63e
41 changed files with 6153 additions and 16 deletions

View File

@@ -0,0 +1,108 @@
import { LockKeyhole, ShieldCheck } from 'lucide-react'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
export function LoginPage({
submitting,
onLogin,
}: {
submitting: boolean
onLogin: (payload: { username: string; password: string }) => Promise<void>
}) {
const [username, setUsername] = useState('admin')
const [password, setPassword] = useState('admin123')
return (
<div className="flex min-h-screen items-center justify-center px-4 py-10">
<div className="grid w-full max-w-5xl gap-6 lg:grid-cols-[1.1fr_0.9fr]">
<Card className="overflow-hidden border-primary/12 bg-gradient-to-br from-card via-card to-primary/5">
<CardHeader className="space-y-4">
<div className="inline-flex w-fit items-center gap-2 rounded-full border border-primary/20 bg-primary/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.28em] text-primary">
<ShieldCheck className="h-3.5 w-3.5" />
Termi admin
</div>
<div className="space-y-3">
<CardTitle className="text-4xl leading-tight">
Separate the dashboard from the public site without losing momentum.
</CardTitle>
<CardDescription className="max-w-xl text-base leading-7">
This new workspace is where operations, moderation, and AI controls will migrate
out of the old server-rendered admin.
</CardDescription>
</div>
</CardHeader>
<CardContent className="grid gap-4 sm:grid-cols-3">
{[
['React app', 'Independent admin surface'],
['shadcn/ui', 'Consistent component foundation'],
['Loco API', 'Backend stays focused on data and rules'],
].map(([title, description]) => (
<div
key={title}
className="rounded-2xl border border-border/70 bg-background/75 p-4"
>
<div className="text-sm font-semibold">{title}</div>
<p className="mt-2 text-sm leading-6 text-muted-foreground">{description}</p>
</div>
))}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-3">
<span className="flex h-11 w-11 items-center justify-center rounded-2xl border border-primary/20 bg-primary/10 text-primary">
<LockKeyhole className="h-5 w-5" />
</span>
Sign in to the control room
</CardTitle>
<CardDescription>
The login bridge still uses the current backend admin credentials so we can migrate
screens incrementally without stopping delivery.
</CardDescription>
</CardHeader>
<CardContent>
<form
className="space-y-5"
onSubmit={(event) => {
event.preventDefault()
void onLogin({ username, password })
}}
>
<div className="space-y-2">
<Label htmlFor="username">Username</Label>
<Input
id="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
autoComplete="username"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
autoComplete="current-password"
required
/>
</div>
<Button className="w-full" size="lg" disabled={submitting}>
{submitting ? 'Signing in...' : 'Unlock admin'}
</Button>
</form>
</CardContent>
</Card>
</div>
</div>
)
}