Files
veille-reglementaire/client/src/pages/ComponentShowcase.tsx
2026-03-16 10:20:39 -04:00

1438 lines
57 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
import { Checkbox } from "@/components/ui/checkbox";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Input } from "@/components/ui/input";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { Label } from "@/components/ui/label";
import {
Menubar,
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarSeparator,
MenubarTrigger,
} from "@/components/ui/menubar";
import {
Pagination,
PaginationContent,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Progress } from "@/components/ui/progress";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import { Slider } from "@/components/ui/slider";
import { Switch } from "@/components/ui/switch";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { Toggle } from "@/components/ui/toggle";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useTheme } from "@/contexts/ThemeContext";
import { format } from "date-fns";
import { zhCN } from "date-fns/locale";
import {
AlertCircle,
CalendarIcon,
Check,
Clock,
Moon,
Sun,
X,
} from "lucide-react";
import { useState } from "react";
import { toast as sonnerToast } from "sonner";
import { AIChatBox, type Message } from "@/components/AIChatBox";
export default function ComponentsShowcase() {
const { theme, toggleTheme } = useTheme();
const [date, setDate] = useState<Date | undefined>(new Date());
const [datePickerDate, setDatePickerDate] = useState<Date>();
const [selectedFruits, setSelectedFruits] = useState<string[]>([]);
const [progress, setProgress] = useState(33);
const [currentPage, setCurrentPage] = useState(2);
const [openCombobox, setOpenCombobox] = useState(false);
const [selectedFramework, setSelectedFramework] = useState("");
const [selectedMonth, setSelectedMonth] = useState("");
const [selectedYear, setSelectedYear] = useState("");
const [dialogInput, setDialogInput] = useState("");
const [dialogOpen, setDialogOpen] = useState(false);
// AI ChatBox demo state
const [chatMessages, setChatMessages] = useState<Message[]>([
{ role: "system", content: "You are a helpful assistant." },
]);
const [isChatLoading, setIsChatLoading] = useState(false);
const handleDialogSubmit = () => {
console.log("Dialog submitted with value:", dialogInput);
sonnerToast.success("Submitted successfully", {
description: `Input: ${dialogInput}`,
});
setDialogInput("");
setDialogOpen(false);
};
const handleDialogKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && !e.nativeEvent.isComposing) {
e.preventDefault();
handleDialogSubmit();
}
};
const handleChatSend = (content: string) => {
// Add user message
const newMessages: Message[] = [...chatMessages, { role: "user", content }];
setChatMessages(newMessages);
// Simulate AI response with delay
setIsChatLoading(true);
setTimeout(() => {
const aiResponse: Message = {
role: "assistant",
content: `This is a **demo response**. In a real app, you would call a tRPC mutation here:\n\n\`\`\`typescript\nconst chatMutation = trpc.ai.chat.useMutation({\n onSuccess: (response) => {\n setChatMessages(prev => [...prev, {\n role: "assistant",\n content: response.choices[0].message.content\n }]);\n }\n});\n\nchatMutation.mutate({ messages: newMessages });\n\`\`\`\n\nYour message was: "${content}"`,
};
setChatMessages([...newMessages, aiResponse]);
setIsChatLoading(false);
}, 1500);
};
return (
<div className="min-h-screen bg-background text-foreground">
<main className="container max-w-6xl mx-auto">
<div className="space-y-2 justify-between flex">
<h2 className="text-3xl font-bold tracking-tight mb-6">
Shadcn/ui Component Library
</h2>
<Button variant="outline" size="icon" onClick={toggleTheme}>
{theme === "light" ? (
<Moon className="h-5 w-5" />
) : (
<Sun className="h-5 w-5" />
)}
</Button>
</div>
<div className="space-y-12">
{/* Text Colors Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Text Colors</h3>
<Card>
<CardContent className="pt-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-3">
<div>
<p className="text-sm text-muted-foreground mb-1">
Foreground (Default)
</p>
<p className="text-foreground text-lg">
Default text color for main content
</p>
</div>
<div>
<p className="text-sm text-muted-foreground mb-1">
Muted Foreground
</p>
<p className="text-muted-foreground text-lg">
Muted text for secondary information
</p>
</div>
<div>
<p className="text-sm text-muted-foreground mb-1">
Primary
</p>
<p className="text-primary text-lg font-medium">
Primary brand color text
</p>
</div>
<div>
<p className="text-sm text-muted-foreground mb-1">
Secondary Foreground
</p>
<p className="text-secondary-foreground text-lg">
Secondary action text color
</p>
</div>
</div>
<div className="space-y-3">
<div>
<p className="text-sm text-muted-foreground mb-1">
Accent Foreground
</p>
<p className="text-accent-foreground text-lg">
Accent text for emphasis
</p>
</div>
<div>
<p className="text-sm text-muted-foreground mb-1">
Destructive
</p>
<p className="text-destructive text-lg font-medium">
Error or destructive action text
</p>
</div>
<div>
<p className="text-sm text-muted-foreground mb-1">
Card Foreground
</p>
<p className="text-card-foreground text-lg">
Text color on card backgrounds
</p>
</div>
<div>
<p className="text-sm text-muted-foreground mb-1">
Popover Foreground
</p>
<p className="text-popover-foreground text-lg">
Text color in popovers
</p>
</div>
</div>
</div>
</CardContent>
</Card>
</section>
{/* Color Combinations Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Color Combinations</h3>
<Card>
<CardContent className="pt-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="bg-primary text-primary-foreground rounded-lg p-4">
<p className="font-medium mb-1">Primary</p>
<p className="text-sm opacity-90">
Primary background with foreground text
</p>
</div>
<div className="bg-secondary text-secondary-foreground rounded-lg p-4">
<p className="font-medium mb-1">Secondary</p>
<p className="text-sm opacity-90">
Secondary background with foreground text
</p>
</div>
<div className="bg-muted text-muted-foreground rounded-lg p-4">
<p className="font-medium mb-1">Muted</p>
<p className="text-sm opacity-90">
Muted background with foreground text
</p>
</div>
<div className="bg-accent text-accent-foreground rounded-lg p-4">
<p className="font-medium mb-1">Accent</p>
<p className="text-sm opacity-90">
Accent background with foreground text
</p>
</div>
<div className="bg-destructive text-destructive-foreground rounded-lg p-4">
<p className="font-medium mb-1">Destructive</p>
<p className="text-sm opacity-90">
Destructive background with foreground text
</p>
</div>
<div className="bg-card text-card-foreground rounded-lg p-4 border">
<p className="font-medium mb-1">Card</p>
<p className="text-sm opacity-90">
Card background with foreground text
</p>
</div>
<div className="bg-popover text-popover-foreground rounded-lg p-4 border">
<p className="font-medium mb-1">Popover</p>
<p className="text-sm opacity-90">
Popover background with foreground text
</p>
</div>
<div className="bg-background text-foreground rounded-lg p-4 border">
<p className="font-medium mb-1">Background</p>
<p className="text-sm opacity-90">
Default background with foreground text
</p>
</div>
</div>
</CardContent>
</Card>
</section>
{/* Buttons Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Buttons</h3>
<Card>
<CardContent className="pt-6">
<div className="flex flex-wrap gap-4">
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon">
<Check className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
</section>
{/* Form Inputs Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Form Inputs</h3>
<Card>
<CardContent className="pt-6 space-y-6">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="Email" />
</div>
<div className="space-y-2">
<Label htmlFor="message">Message</Label>
<Textarea
id="message"
placeholder="Type your message here."
/>
</div>
<div className="space-y-2">
<Label>Select</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="orange">Orange</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<Label htmlFor="terms">Accept terms and conditions</Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="airplane-mode" />
<Label htmlFor="airplane-mode">Airplane Mode</Label>
</div>
<div className="space-y-2">
<Label>Radio Group</Label>
<RadioGroup defaultValue="option-one">
<div className="flex items-center space-x-2">
<RadioGroupItem value="option-one" id="option-one" />
<Label htmlFor="option-one">Option One</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option-two" id="option-two" />
<Label htmlFor="option-two">Option Two</Label>
</div>
</RadioGroup>
</div>
<div className="space-y-2">
<Label>Slider</Label>
<Slider defaultValue={[50]} max={100} step={1} />
</div>
<div className="space-y-2">
<Label>Input OTP</Label>
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
</div>
<div className="space-y-2">
<Label>Date Time Picker</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={`w-full justify-start text-left font-normal ${
!datePickerDate && "text-muted-foreground"
}`}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{datePickerDate ? (
format(datePickerDate, "PPP HH:mm", { locale: zhCN })
) : (
<span>Select date and time</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<div className="p-3 space-y-3">
<Calendar
mode="single"
selected={datePickerDate}
onSelect={setDatePickerDate}
/>
<div className="border-t pt-3 space-y-2">
<Label className="flex items-center gap-2">
<Clock className="h-4 w-4" />
Time
</Label>
<div className="flex gap-2">
<Input
type="time"
value={
datePickerDate
? format(datePickerDate, "HH:mm")
: "00:00"
}
onChange={e => {
const [hours, minutes] =
e.target.value.split(":");
const newDate = datePickerDate
? new Date(datePickerDate)
: new Date();
newDate.setHours(parseInt(hours));
newDate.setMinutes(parseInt(minutes));
setDatePickerDate(newDate);
}}
/>
</div>
</div>
</div>
</PopoverContent>
</Popover>
{datePickerDate && (
<p className="text-sm text-muted-foreground">
Selected:{" "}
{format(datePickerDate, "yyyy/MM/dd HH:mm", {
locale: zhCN,
})}
</p>
)}
</div>
<div className="space-y-2">
<Label>Searchable Dropdown</Label>
<Popover open={openCombobox} onOpenChange={setOpenCombobox}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={openCombobox}
className="w-full justify-between"
>
{selectedFramework
? [
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
{ value: "angular", label: "Angular" },
{ value: "svelte", label: "Svelte" },
{ value: "nextjs", label: "Next.js" },
{ value: "nuxt", label: "Nuxt" },
{ value: "remix", label: "Remix" },
].find(fw => fw.value === selectedFramework)?.label
: "Select framework..."}
<CalendarIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-full p-0">
<Command>
<CommandInput placeholder="Search frameworks..." />
<CommandList>
<CommandEmpty>No framework found</CommandEmpty>
<CommandGroup>
{[
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
{ value: "angular", label: "Angular" },
{ value: "svelte", label: "Svelte" },
{ value: "nextjs", label: "Next.js" },
{ value: "nuxt", label: "Nuxt" },
{ value: "remix", label: "Remix" },
].map(framework => (
<CommandItem
key={framework.value}
value={framework.value}
onSelect={currentValue => {
setSelectedFramework(
currentValue === selectedFramework
? ""
: currentValue
);
setOpenCombobox(false);
}}
>
<Check
className={`mr-2 h-4 w-4 ${
selectedFramework === framework.value
? "opacity-100"
: "opacity-0"
}`}
/>
{framework.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
{selectedFramework && (
<p className="text-sm text-muted-foreground">
Selected:{" "}
{
[
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
{ value: "angular", label: "Angular" },
{ value: "svelte", label: "Svelte" },
{ value: "nextjs", label: "Next.js" },
{ value: "nuxt", label: "Nuxt" },
{ value: "remix", label: "Remix" },
].find(fw => fw.value === selectedFramework)?.label
}
</p>
)}
</div>
<div className="space-y-2">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="month" className="text-sm font-medium">
Month
</Label>
<Select
value={selectedMonth}
onValueChange={setSelectedMonth}
>
<SelectTrigger id="month">
<SelectValue placeholder="MM" />
</SelectTrigger>
<SelectContent>
{Array.from({ length: 12 }, (_, i) => i + 1).map(
month => (
<SelectItem
key={month}
value={month.toString().padStart(2, "0")}
>
{month.toString().padStart(2, "0")}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="year" className="text-sm font-medium">
Year
</Label>
<Select
value={selectedYear}
onValueChange={setSelectedYear}
>
<SelectTrigger id="year">
<SelectValue placeholder="YYYY" />
</SelectTrigger>
<SelectContent>
{Array.from(
{ length: 10 },
(_, i) => new Date().getFullYear() - 5 + i
).map(year => (
<SelectItem key={year} value={year.toString()}>
{year}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{selectedMonth && selectedYear && (
<p className="text-sm text-muted-foreground">
Selected: {selectedYear}/{selectedMonth}/
</p>
)}
</div>
</CardContent>
</Card>
</section>
{/* Data Display Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Data Display</h3>
<Card>
<CardContent className="pt-6 space-y-6">
<div className="space-y-2">
<Label>Badges</Label>
<div className="flex flex-wrap gap-2">
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="outline">Outline</Badge>
</div>
</div>
<Separator />
<div className="space-y-2">
<Label>Avatar</Label>
<div className="flex gap-4">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>AB</AvatarFallback>
</Avatar>
</div>
</div>
<Separator />
<div className="space-y-2">
<Label>Progress</Label>
<Progress value={progress} />
<div className="flex gap-2">
<Button
size="sm"
onClick={() => setProgress(Math.max(0, progress - 10))}
>
-10
</Button>
<Button
size="sm"
onClick={() => setProgress(Math.min(100, progress + 10))}
>
+10
</Button>
</div>
</div>
<Separator />
<div className="space-y-2">
<Label>Skeleton</Label>
<div className="space-y-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-4 w-1/2" />
</div>
</div>
<Separator />
<div className="space-y-2">
<Label>Pagination</Label>
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
onClick={e => {
e.preventDefault();
setCurrentPage(Math.max(1, currentPage - 1));
}}
/>
</PaginationItem>
{[1, 2, 3, 4, 5].map(page => (
<PaginationItem key={page}>
<PaginationLink
href="#"
isActive={currentPage === page}
onClick={e => {
e.preventDefault();
setCurrentPage(page);
}}
>
{page}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem>
<PaginationNext
href="#"
onClick={e => {
e.preventDefault();
setCurrentPage(Math.min(5, currentPage + 1));
}}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
<p className="text-sm text-muted-foreground text-center">
Current page: {currentPage}
</p>
</div>
<Separator />
<div className="space-y-2">
<Label>Table</Label>
<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">INV001</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
<TableCell className="text-right">$250.00</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">INV002</TableCell>
<TableCell>Pending</TableCell>
<TableCell>PayPal</TableCell>
<TableCell className="text-right">$150.00</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">INV003</TableCell>
<TableCell>Unpaid</TableCell>
<TableCell>Bank Transfer</TableCell>
<TableCell className="text-right">$350.00</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
<Separator />
<div className="space-y-2">
<Label>Menubar</Label>
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>New Tab</MenubarItem>
<MenubarItem>New Window</MenubarItem>
<MenubarSeparator />
<MenubarItem>Share</MenubarItem>
<MenubarSeparator />
<MenubarItem>Print</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem>Undo</MenubarItem>
<MenubarItem>Redo</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarItem>Reload</MenubarItem>
<MenubarItem>Force Reload</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
</div>
<Separator />
<div className="space-y-2">
<Label>Breadcrumb</Label>
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink href="/components">
Components
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</CardContent>
</Card>
</section>
{/* Alerts Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Alerts</h3>
<div className="space-y-4">
<Alert>
<AlertCircle className="h-4 w-4" />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components to your app using the cli.
</AlertDescription>
</Alert>
<Alert variant="destructive">
<X className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>
Your session has expired. Please log in again.
</AlertDescription>
</Alert>
</div>
</section>
{/* Tabs Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Tabs</h3>
<Tabs defaultValue="account" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="account">
<Card>
<CardHeader>
<CardTitle>Account</CardTitle>
<CardDescription>
Make changes to your account here.
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="space-y-1">
<Label htmlFor="name">Name</Label>
<Input id="name" defaultValue="Pedro Duarte" />
</div>
</CardContent>
<CardFooter>
<Button>Save changes</Button>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="password">
<Card>
<CardHeader>
<CardTitle>Password</CardTitle>
<CardDescription>
Change your password here.
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="space-y-1">
<Label htmlFor="current">Current password</Label>
<Input id="current" type="password" />
</div>
<div className="space-y-1">
<Label htmlFor="new">New password</Label>
<Input id="new" type="password" />
</div>
</CardContent>
<CardFooter>
<Button>Save password</Button>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="settings">
<Card>
<CardHeader>
<CardTitle>Settings</CardTitle>
<CardDescription>
Manage your settings here.
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
Settings content goes here.
</p>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</section>
{/* Accordion Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Accordion</h3>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Is it styled?</AccordionTrigger>
<AccordionContent>
Yes. It comes with default styles that matches the other
components' aesthetic.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it animated?</AccordionTrigger>
<AccordionContent>
Yes. It's animated by default, but you can disable it if you
prefer.
</AccordionContent>
</AccordionItem>
</Accordion>
</section>
{/* Collapsible Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Collapsible</h3>
<Collapsible>
<Card>
<CardHeader>
<CollapsibleTrigger asChild>
<Button variant="ghost" className="w-full justify-between">
<CardTitle>@peduarte starred 3 repositories</CardTitle>
</Button>
</CollapsibleTrigger>
</CardHeader>
<CollapsibleContent>
<CardContent>
<div className="space-y-2">
<div className="rounded-md border px-4 py-3 font-mono text-sm">
@radix-ui/primitives
</div>
<div className="rounded-md border px-4 py-3 font-mono text-sm">
@radix-ui/colors
</div>
<div className="rounded-md border px-4 py-3 font-mono text-sm">
@stitches/react
</div>
</div>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
</section>
{/* Dialog, Sheet, Drawer Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Overlays</h3>
<Card>
<CardContent className="pt-6">
<div className="flex flex-wrap gap-4">
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogTrigger asChild>
<Button variant="outline">Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Test Input</DialogTitle>
<DialogDescription>
Enter some text below. Press Enter to submit (IME composition supported).
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="dialog-input">Input</Label>
<Input
id="dialog-input"
placeholder="Type something..."
value={dialogInput}
onChange={(e) => setDialogInput(e.target.value)}
onKeyDown={handleDialogKeyDown}
autoFocus
/>
</div>
</div>
<div className="flex justify-end gap-2">
<Button
variant="outline"
onClick={() => setDialogOpen(false)}
>
Cancel
</Button>
<Button onClick={handleDialogSubmit}>Submit</Button>
</div>
</DialogContent>
</Dialog>
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Open Sheet</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>
Make changes to your profile here. Click save when
you're done.
</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>
<Drawer>
<DrawerTrigger asChild>
<Button variant="outline">Open Drawer</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Are you absolutely sure?</DrawerTitle>
<DrawerDescription>
This action cannot be undone.
</DrawerDescription>
</DrawerHeader>
<DrawerFooter>
<Button>Submit</Button>
<DrawerClose asChild>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Open Popover</Button>
</PopoverTrigger>
<PopoverContent>
<div className="space-y-2">
<h4 className="font-medium leading-none">Dimensions</h4>
<p className="text-sm text-muted-foreground">
Set the dimensions for the layer.
</p>
</div>
</PopoverContent>
</Popover>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">Hover me</Button>
</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent>
</Tooltip>
</div>
</CardContent>
</Card>
</section>
{/* Menus Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Menus</h3>
<Card>
<CardContent className="pt-6">
<div className="flex flex-wrap gap-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Dropdown Menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Billing</DropdownMenuItem>
<DropdownMenuItem>Team</DropdownMenuItem>
<DropdownMenuItem>Subscription</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<ContextMenu>
<ContextMenuTrigger asChild>
<Button variant="outline">Right Click Me</Button>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>Profile</ContextMenuItem>
<ContextMenuItem>Billing</ContextMenuItem>
<ContextMenuItem>Team</ContextMenuItem>
<ContextMenuItem>Subscription</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
<HoverCard>
<HoverCardTrigger asChild>
<Button variant="outline">Hover Card</Button>
</HoverCardTrigger>
<HoverCardContent>
<div className="space-y-2">
<h4 className="text-sm font-semibold">@nextjs</h4>
<p className="text-sm">
The React Framework created and maintained by
@vercel.
</p>
</div>
</HoverCardContent>
</HoverCard>
</div>
</CardContent>
</Card>
</section>
{/* Calendar Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Calendar</h3>
<Card>
<CardContent className="pt-6 flex justify-center">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-md border"
/>
</CardContent>
</Card>
</section>
{/* Carousel Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Carousel</h3>
<Card>
<CardContent className="pt-6">
<Carousel className="w-full max-w-xs mx-auto">
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index}>
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="text-4xl font-semibold">
{index + 1}
</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</CardContent>
</Card>
</section>
{/* Toggle Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Toggle</h3>
<Card>
<CardContent className="pt-6 space-y-4">
<div className="space-y-2">
<Label>Toggle</Label>
<div className="flex gap-2">
<Toggle aria-label="Toggle italic">
<span className="font-bold">B</span>
</Toggle>
<Toggle aria-label="Toggle italic">
<span className="italic">I</span>
</Toggle>
<Toggle aria-label="Toggle underline">
<span className="underline">U</span>
</Toggle>
</div>
</div>
<Separator />
<div className="space-y-2">
<Label>Toggle Group</Label>
<ToggleGroup type="multiple">
<ToggleGroupItem value="bold" aria-label="Toggle bold">
<span className="font-bold">B</span>
</ToggleGroupItem>
<ToggleGroupItem value="italic" aria-label="Toggle italic">
<span className="italic">I</span>
</ToggleGroupItem>
<ToggleGroupItem
value="underline"
aria-label="Toggle underline"
>
<span className="underline">U</span>
</ToggleGroupItem>
</ToggleGroup>
</div>
</CardContent>
</Card>
</section>
{/* Aspect Ratio & Scroll Area Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Layout Components</h3>
<Card>
<CardContent className="pt-6 space-y-6">
<div className="space-y-2">
<Label>Aspect Ratio (16/9)</Label>
<AspectRatio ratio={16 / 9} className="bg-muted">
<div className="flex h-full items-center justify-center">
<p className="text-muted-foreground">16:9 Aspect Ratio</p>
</div>
</AspectRatio>
</div>
<Separator />
<div className="space-y-2">
<Label>Scroll Area</Label>
<ScrollArea className="h-[200px] w-full rounded-md border overflow-hidden">
<div className="p-4">
<div className="space-y-4">
{Array.from({ length: 20 }).map((_, i) => (
<div key={i} className="text-sm">
Item {i + 1}: This is a scrollable content area
</div>
))}
</div>
</div>
</ScrollArea>
</div>
</CardContent>
</Card>
</section>
{/* Resizable Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Resizable Panels</h3>
<Card>
<CardContent className="pt-6">
<ResizablePanelGroup
direction="horizontal"
className="min-h-[200px] rounded-lg border"
>
<ResizablePanel defaultSize={50}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Panel One</span>
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<div className="flex h-full items-center justify-center p-6">
<span className="font-semibold">Panel Two</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</CardContent>
</Card>
</section>
{/* Toast Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">Toast</h3>
<Card>
<CardContent className="pt-6 space-y-4">
<div className="space-y-2">
<Label>Sonner Toast</Label>
<div className="flex flex-wrap gap-2">
<Button
variant="outline"
onClick={() => {
sonnerToast.success("Operation successful", {
description: "Your changes have been saved",
});
}}
>
Success
</Button>
<Button
variant="outline"
onClick={() => {
sonnerToast.error("Operation failed", {
description:
"Cannot complete operation, please try again",
});
}}
>
Error
</Button>
<Button
variant="outline"
onClick={() => {
sonnerToast.info("Information", {
description: "This is an information message",
});
}}
>
Info
</Button>
<Button
variant="outline"
onClick={() => {
sonnerToast.warning("Warning", {
description:
"Please note the impact of this operation",
});
}}
>
Warning
</Button>
<Button
variant="outline"
onClick={() => {
sonnerToast.loading("Loading", {
description: "Please wait",
});
}}
>
Loading
</Button>
<Button
variant="outline"
onClick={() => {
const promise = new Promise(resolve =>
setTimeout(resolve, 2000)
);
sonnerToast.promise(promise, {
loading: "Processing...",
success: "Processing complete!",
error: "Processing failed",
});
}}
>
Promise
</Button>
</div>
</div>
</CardContent>
</Card>
</section>
{/* AI ChatBox Section */}
<section className="space-y-4">
<h3 className="text-2xl font-semibold">AI ChatBox</h3>
<Card>
<CardContent className="pt-6">
<div className="space-y-4">
<div className="text-sm text-muted-foreground">
<p>
A ready-to-use chat interface component that integrates with the LLM system.
Features markdown rendering, auto-scrolling, and loading states.
</p>
<p className="mt-2">
This is a demo with simulated responses. In a real app, you'd connect it to a tRPC mutation.
</p>
</div>
<AIChatBox
messages={chatMessages}
onSendMessage={handleChatSend}
isLoading={isChatLoading}
placeholder="Try sending a message..."
height="500px"
emptyStateMessage="How can I help you today?"
suggestedPrompts={[
"What is React?",
"Explain TypeScript",
"How to use tRPC?",
"Best practices for web development",
]}
/>
</div>
</CardContent>
</Card>
</section>
</div>
</main>
<footer className="border-t py-6 mt-12">
<div className="container text-center text-sm text-muted-foreground">
<p>Shadcn/ui Component Showcase</p>
</div>
</footer>
</div>
);
}