스타일 수정중
This commit is contained in:
837
frontend/app/(main)/admin/ui-components-demo/page.tsx
Normal file
837
frontend/app/(main)/admin/ui-components-demo/page.tsx
Normal file
@@ -0,0 +1,837 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user