Adapters
impulso-digital-site / client /src /pages /ComponentShowcase.tsx
Manus
Checkpoint: Complete Impulso Digital website with:
da20cd7
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>
);
}