ERP-node/frontend/app/(main)/admin/ui-components-demo/page.tsx

838 lines
33 KiB
TypeScript

"use client";
import { useState } from "react";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { CustomCalendar } from "@/components/ui/custom-calendar";
import { ExampleFormDialog } from "@/components/examples/ExampleFormDialog";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
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 {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Progress } from "@/components/ui/progress";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
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 {
AlertCircle,
Check,
ChevronDown,
Info,
Loader2,
MoreHorizontal,
Plus,
Search,
Trash2,
User,
} from "lucide-react";
import { toast } from "sonner";
export default function UIComponentsDemoPage() {
const [date, setDate] = useState<Date | undefined>(new Date());
const [progress, setProgress] = useState(45);
const [switchOn, setSwitchOn] = useState(false);
const [checkboxChecked, setCheckboxChecked] = useState(false);
const [sliderValue, setSliderValue] = useState([50]);
const [radioValue, setRadioValue] = useState("option1");
return (
<div className="bg-background min-h-screen p-8">
<div className="mx-auto max-w-7xl space-y-8">
{/* 헤더 */}
<div className="space-y-4">
<div className="space-y-2">
<h1 className="text-4xl font-bold">shadcn/ui </h1>
<p className="text-muted-foreground text-lg"> UI </p>
</div>
{/* 실전 예시 폼 */}
<Card className="bg-primary/5 border-primary/20">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<span className="text-primary">🎯</span>
예시: 완전한
</CardTitle>
<CardDescription>
shadcn/ui . , ,
.
</CardDescription>
</CardHeader>
<CardContent>
<ExampleFormDialog />
</CardContent>
</Card>
</div>
{/* 버튼 섹션 */}
<Card>
<CardHeader>
<CardTitle>Button ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-2">
<Button variant="default">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>
</div>
<Separator />
<div className="flex flex-wrap gap-2">
<Button size="lg">Large</Button>
<Button size="default">Default</Button>
<Button size="sm">Small</Button>
<Button size="icon">
<Plus className="h-4 w-4" />
</Button>
</div>
<Separator />
<div className="flex flex-wrap gap-2">
<Button disabled>Disabled</Button>
<Button>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Loading
</Button>
<Button>
<Plus className="mr-2 h-4 w-4" />
With Icon
</Button>
</div>
</CardContent>
</Card>
{/* Badge 섹션 */}
<Card>
<CardHeader>
<CardTitle>Badge ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<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>
</CardContent>
</Card>
{/* Alert 섹션 */}
<Card>
<CardHeader>
<CardTitle>Alert ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Alert>
<Info className="h-4 w-4" />
<AlertTitle> </AlertTitle>
<AlertDescription>
. .
</AlertDescription>
</Alert>
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle> </AlertTitle>
<AlertDescription> . .</AlertDescription>
</Alert>
</CardContent>
</Card>
{/* Input & Form 섹션 */}
<Card>
<CardHeader>
<CardTitle>Input & Form ( )</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Text Input */}
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input id="email" type="email" placeholder="example@email.com" />
</div>
{/* Textarea */}
<div className="space-y-2">
<Label htmlFor="message"></Label>
<Textarea id="message" placeholder="메시지를 입력하세요..." rows={4} />
</div>
{/* Select */}
<div className="space-y-2">
<Label></Label>
<Select>
<SelectTrigger className="w-full">
<SelectValue placeholder="옵션을 선택하세요" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1"> 1</SelectItem>
<SelectItem value="option2"> 2</SelectItem>
<SelectItem value="option3"> 3</SelectItem>
</SelectContent>
</Select>
</div>
{/* Checkbox */}
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={checkboxChecked}
onCheckedChange={(checked) => setCheckboxChecked(checked as boolean)}
/>
<Label htmlFor="terms" className="cursor-pointer">
</Label>
</div>
{/* Radio Group */}
<div className="space-y-2">
<Label> </Label>
<RadioGroup value={radioValue} onValueChange={setRadioValue}>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option1" id="r1" />
<Label htmlFor="r1" className="cursor-pointer">
1
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option2" id="r2" />
<Label htmlFor="r2" className="cursor-pointer">
2
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option3" id="r3" />
<Label htmlFor="r3" className="cursor-pointer">
3
</Label>
</div>
</RadioGroup>
</div>
{/* Switch */}
<div className="flex items-center space-x-2">
<Switch id="airplane" checked={switchOn} onCheckedChange={setSwitchOn} />
<Label htmlFor="airplane" className="cursor-pointer">
{switchOn ? "켜짐" : "꺼짐"}
</Label>
</div>
{/* Slider */}
<div className="space-y-2">
<Label> (: {sliderValue[0]})</Label>
<Slider value={sliderValue} onValueChange={setSliderValue} max={100} step={1} />
</div>
</CardContent>
</Card>
{/* Dialog & Modal 섹션 */}
<Card>
<CardHeader>
<CardTitle>Dialog & Modal ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-2">
{/* Dialog */}
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Dialog </Button>
</DialogTrigger>
<DialogContent className="max-w-[95vw] sm:max-w-[500px]">
<DialogHeader>
<DialogTitle className="text-base sm:text-lg"> </DialogTitle>
<DialogDescription className="text-xs sm:text-sm">
.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name"></Label>
<Input id="name" placeholder="이름을 입력하세요" />
</div>
</div>
<DialogFooter className="gap-2 sm:gap-0">
<Button variant="outline" className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm">
</Button>
<Button className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"></Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Alert Dialog */}
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive">Alert Dialog </Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle> ?</AlertDialogTitle>
<AlertDialogDescription> . ?</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction></AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</CardContent>
</Card>
{/* Dropdown & Popover 섹션 */}
<Card>
<CardHeader>
<CardTitle>Dropdown & Popover ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-2">
{/* Dropdown Menu */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel> </DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem></DropdownMenuItem>
<DropdownMenuItem></DropdownMenuItem>
<DropdownMenuItem></DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive"></DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{/* Popover */}
<Popover>
<PopoverTrigger asChild>
<Button variant="outline"> </Button>
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="space-y-2">
<h4 className="leading-none font-medium"> </h4>
<p className="text-muted-foreground text-sm"> .</p>
</div>
</PopoverContent>
</Popover>
</div>
</CardContent>
</Card>
{/* Command 섹션 */}
<Card>
<CardHeader>
<CardTitle>Command ( )</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<Command className="rounded-lg border shadow-md">
<CommandInput placeholder="명령어 검색..." />
<CommandList>
<CommandEmpty> .</CommandEmpty>
<CommandGroup heading="제안">
<CommandItem>
<Search className="mr-2 h-4 w-4" />
<span></span>
</CommandItem>
<CommandItem>
<User className="mr-2 h-4 w-4" />
<span></span>
</CommandItem>
<CommandItem>
<Plus className="mr-2 h-4 w-4" />
<span> </span>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</CardContent>
</Card>
{/* Tabs 섹션 */}
<Card>
<CardHeader>
<CardTitle>Tabs ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="tab1" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="tab1"> 1</TabsTrigger>
<TabsTrigger value="tab2"> 2</TabsTrigger>
<TabsTrigger value="tab3"> 3</TabsTrigger>
</TabsList>
<TabsContent value="tab1" className="space-y-4">
<p className="text-muted-foreground text-sm"> .</p>
</TabsContent>
<TabsContent value="tab2" className="space-y-4">
<p className="text-muted-foreground text-sm"> .</p>
</TabsContent>
<TabsContent value="tab3" className="space-y-4">
<p className="text-muted-foreground text-sm"> .</p>
</TabsContent>
</Tabs>
</CardContent>
</Card>
{/* Accordion 섹션 */}
<Card>
<CardHeader>
<CardTitle>Accordion ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger> </AccordionTrigger>
<AccordionContent> . .</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger> </AccordionTrigger>
<AccordionContent> . .</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger> </AccordionTrigger>
<AccordionContent> . .</AccordionContent>
</AccordionItem>
</Accordion>
</CardContent>
</Card>
{/* Collapsible 섹션 */}
<Card>
<CardHeader>
<CardTitle>Collapsible ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<Collapsible>
<CollapsibleTrigger asChild>
<Button variant="ghost" className="w-full justify-between">
<span> </span>
<ChevronDown className="h-4 w-4" />
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="mt-2 space-y-2">
<div className="rounded-md border px-4 py-2 text-sm"> 1</div>
<div className="rounded-md border px-4 py-2 text-sm"> 2</div>
<div className="rounded-md border px-4 py-2 text-sm"> 3</div>
</CollapsibleContent>
</Collapsible>
</CardContent>
</Card>
{/* Progress 섹션 */}
<Card>
<CardHeader>
<CardTitle>Progress ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground"> ...</span>
<span className="font-medium">{progress}%</span>
</div>
<Progress value={progress} />
</div>
<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>
</CardContent>
</Card>
{/* Avatar 섹션 */}
<Card>
<CardHeader>
<CardTitle>Avatar ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<div className="flex gap-4">
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>KJ</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>
<User className="h-4 w-4" />
</AvatarFallback>
</Avatar>
</div>
</CardContent>
</Card>
{/* Table 섹션 */}
<Card>
<CardHeader>
<CardTitle>Table ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableCaption> </TableCaption>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">001</TableCell>
<TableCell></TableCell>
<TableCell>
<Badge></Badge>
</TableCell>
<TableCell className="text-right">100,000</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">002</TableCell>
<TableCell></TableCell>
<TableCell>
<Badge variant="secondary"></Badge>
</TableCell>
<TableCell className="text-right">250,000</TableCell>
</TableRow>
<TableRow>
<TableCell className="font-medium">003</TableCell>
<TableCell></TableCell>
<TableCell>
<Badge variant="destructive"></Badge>
</TableCell>
<TableCell className="text-right">50,000</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
{/* ScrollArea 섹션 */}
<Card>
<CardHeader>
<CardTitle>Scroll Area ( )</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<ScrollArea className="h-72 w-full rounded-md border p-4">
<div className="space-y-2">
{Array.from({ length: 50 }).map((_, i) => (
<div key={i} className="text-muted-foreground text-sm">
{i + 1}
</div>
))}
</div>
</ScrollArea>
</CardContent>
</Card>
{/* Calendar & Date Picker 섹션 */}
<Card>
<CardHeader>
<CardTitle>Calendar & Date Picker ( )</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Date Picker (권장 방식 - Custom Calendar 사용) */}
<div className="space-y-2">
<Label>Date Picker (Popover - )</Label>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="w-[280px] justify-start text-left font-normal">
{date ? (
date.toLocaleDateString("ko-KR", {
year: "numeric",
month: "long",
day: "numeric",
})
) : (
<span className="text-muted-foreground"> </span>
)}
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="ml-auto h-4 w-4 opacity-50"
>
<rect width="18" height="18" x="3" y="4" rx="2" ry="2" />
<line x1="16" x2="16" y1="2" y2="6" />
<line x1="8" x2="8" y1="2" y2="6" />
<line x1="3" x2="21" y1="10" y2="10" />
</svg>
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<CustomCalendar mode="single" selected={date} onSelect={setDate} />
</PopoverContent>
</Popover>
{date && <p className="text-muted-foreground text-xs"> : {date.toISOString().split("T")[0]}</p>}
</div>
<Separator />
{/* 커스텀 Calendar (shadcn/ui 스타일) */}
<div className="space-y-2">
<Label>Custom Calendar ( - shadcn/ui )</Label>
<CustomCalendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-md border shadow-sm"
/>
</div>
<Separator />
{/* 기존 react-day-picker Calendar */}
<div className="space-y-2">
<Label>React-Day-Picker Calendar ()</Label>
<Calendar mode="single" selected={date} onSelect={setDate} className="rounded-md border shadow-sm" />
</div>
</CardContent>
</Card>
{/* Separator 섹션 */}
<Card>
<CardHeader>
<CardTitle>Separator ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<p className="text-sm"> </p>
<Separator className="my-4" />
<p className="text-sm"> </p>
</div>
<div className="flex h-20 items-center space-x-4">
<div className="text-sm"></div>
<Separator orientation="vertical" />
<div className="text-sm"></div>
</div>
</CardContent>
</Card>
{/* Toast 버튼 섹션 */}
<Card>
<CardHeader>
<CardTitle>Toast ( )</CardTitle>
<CardDescription>Sonner ( )</CardDescription>
</CardHeader>
<CardContent>
<div className="flex flex-wrap gap-2">
<Button
onClick={() =>
toast.success("성공", {
description: "작업이 성공적으로 완료되었습니다.",
})
}
>
</Button>
<Button
variant="destructive"
onClick={() =>
toast.error("오류", {
description: "작업 중 오류가 발생했습니다.",
})
}
>
</Button>
<Button
variant="outline"
onClick={() =>
toast("알림", {
description: "일반 알림 메시지입니다.",
})
}
>
</Button>
<Button
variant="secondary"
onClick={() => {
const promise = new Promise((resolve) => setTimeout(resolve, 2000));
toast.promise(promise, {
loading: "처리 중...",
success: "완료!",
error: "실패",
});
}}
>
Promise
</Button>
</div>
</CardContent>
</Card>
{/* Lucide Icons 섹션 */}
<Card>
<CardHeader>
<CardTitle>Lucide Icons ()</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-6 gap-4 sm:grid-cols-8 md:grid-cols-12">
{[
{ Icon: Plus, name: "Plus" },
{ Icon: Trash2, name: "Trash2" },
{ Icon: Search, name: "Search" },
{ Icon: User, name: "User" },
{ Icon: Check, name: "Check" },
{ Icon: ChevronDown, name: "ChevronDown" },
{ Icon: AlertCircle, name: "AlertCircle" },
{ Icon: Info, name: "Info" },
{ Icon: Loader2, name: "Loader2" },
{ Icon: MoreHorizontal, name: "MoreHorizontal" },
].map(({ Icon, name }) => (
<div
key={name}
className="hover:bg-accent flex flex-col items-center gap-2 rounded-lg border p-3"
title={name}
>
<Icon className="h-6 w-6" />
<span className="text-muted-foreground text-[10px]">{name}</span>
</div>
))}
</div>
</CardContent>
</Card>
{/* 컬러 팔레트 */}
<Card>
<CardHeader>
<CardTitle>Color Palette ( )</CardTitle>
<CardDescription> </CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
<div className="space-y-2">
<div className="bg-primary h-20 rounded-lg" />
<p className="text-xs font-medium">Primary</p>
<code className="text-muted-foreground text-[10px]">bg-primary</code>
</div>
<div className="space-y-2">
<div className="bg-secondary h-20 rounded-lg" />
<p className="text-xs font-medium">Secondary</p>
<code className="text-muted-foreground text-[10px]">bg-secondary</code>
</div>
<div className="space-y-2">
<div className="bg-destructive h-20 rounded-lg" />
<p className="text-xs font-medium">Destructive</p>
<code className="text-muted-foreground text-[10px]">bg-destructive</code>
</div>
<div className="space-y-2">
<div className="bg-muted h-20 rounded-lg" />
<p className="text-xs font-medium">Muted</p>
<code className="text-muted-foreground text-[10px]">bg-muted</code>
</div>
<div className="space-y-2">
<div className="bg-accent h-20 rounded-lg" />
<p className="text-xs font-medium">Accent</p>
<code className="text-muted-foreground text-[10px]">bg-accent</code>
</div>
<div className="space-y-2">
<div className="bg-card h-20 rounded-lg border" />
<p className="text-xs font-medium">Card</p>
<code className="text-muted-foreground text-[10px]">bg-card</code>
</div>
<div className="space-y-2">
<div className="bg-popover h-20 rounded-lg border" />
<p className="text-xs font-medium">Popover</p>
<code className="text-muted-foreground text-[10px]">bg-popover</code>
</div>
<div className="space-y-2">
<div className="bg-background h-20 rounded-lg border" />
<p className="text-xs font-medium">Background</p>
<code className="text-muted-foreground text-[10px]">bg-background</code>
</div>
</div>
</CardContent>
</Card>
{/* 푸터 */}
<Card>
<CardContent className="pt-6">
<p className="text-muted-foreground text-center text-sm">
{" "}
<a
href="https://ui.shadcn.com"
target="_blank"
rel="noopener noreferrer"
className="hover:text-foreground underline"
>
shadcn/ui
</a>
.
</p>
</CardContent>
</Card>
</div>
</div>
);
}