feat: portal redesign — shadcn/ui, React Query, Zod, isolated portals
Foundation: - CLAUDE.md with full coding rules (tech stack, patterns, redirect rules) - Install @tanstack/react-query, zod, react-hook-form, shadcn/ui deps - CSS variables design system (Tailwind v4 + tw-animate-css) - Shared components: Button, Input, Label, Card, Badge, Separator, Form - PortalLoginShell: two-column login layout (branding left, form right) - PortalNav: shared sidebar nav used by all portals - ReactQueryProvider in root layout - api-client.ts: typed fetch wrapper (no raw fetch in components) Portal isolation: - Sidebar now returns null for all non-chapter routes; portals own their layout - Admin layout: auth guard + PortalNav (slate accent) - Org layout: auth guard + PortalNav (violet accent) - Member layout: server-side cookie check → MemberNav client component - Chapter admin sidebar: PortalNav (blue accent) Login pages (all use react-hook-form + Zod + PortalLoginShell): - /login → defaults next to /search (was /) — fixes redirect bug - /admin/login → replaces /admin - /org/login → replaces /org - /member-login → two-step phone+OTP with proper form validation Portal selector (/) redesigned with accent border-left cards Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { cn } from '../_lib/utils';
|
||||
|
||||
interface PortalLoginShellProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
accentClass?: string;
|
||||
children: React.ReactNode;
|
||||
footer?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function PortalLoginShell({ title, subtitle, accentClass = 'bg-primary', children, footer }: PortalLoginShellProps) {
|
||||
return (
|
||||
<div className="min-h-screen grid lg:grid-cols-2">
|
||||
{/* Left branding panel */}
|
||||
<div className={cn('hidden lg:flex flex-col justify-between p-10 text-white', accentClass)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-bold text-xl tracking-tight">TOWER</span>
|
||||
</div>
|
||||
<div>
|
||||
<blockquote className="space-y-2">
|
||||
<p className="text-lg font-medium leading-relaxed">
|
||||
"Community knowledge, beautifully organised."
|
||||
</p>
|
||||
<footer className="text-sm opacity-70">Insignia TOWER Platform</footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right form panel */}
|
||||
<div className="flex items-center justify-center p-8 bg-background">
|
||||
<div className="w-full max-w-sm space-y-6">
|
||||
{/* Mobile logo */}
|
||||
<div className="flex items-center gap-2 lg:hidden">
|
||||
<span className="font-bold text-lg">TOWER</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-2xl font-bold tracking-tight">{title}</h1>
|
||||
<p className="text-sm text-muted-foreground">{subtitle}</p>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
|
||||
{footer && <div className="text-center text-sm text-muted-foreground">{footer}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user