DynamicItemForm 개선: - 품목코드 자동생성 기능 추가 - 조건부 표시 로직 개선 - 불필요한 컴포넌트 정리 (DynamicField, DynamicSection 등) - 타입 시스템 단순화 새로운 기능: - Sales 페이지 마이그레이션 (견적관리, 거래처관리) - 공통 컴포넌트 추가 (atoms, molecules, organisms, templates) 문서화: - 구현 문서 및 참조 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { LucideIcon, Eye, Edit, Trash2, Copy, Download, MoreHorizontal } from "lucide-react";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
|
|
export interface TableAction {
|
|
type: "view" | "edit" | "delete" | "copy" | "download" | "custom";
|
|
label?: string;
|
|
icon?: LucideIcon;
|
|
onClick: () => void;
|
|
variant?: "default" | "ghost" | "outline" | "destructive";
|
|
hidden?: boolean;
|
|
}
|
|
|
|
interface TableActionsProps {
|
|
actions: TableAction[];
|
|
layout?: "buttons" | "dropdown" | "auto";
|
|
}
|
|
|
|
const defaultIcons: Record<string, LucideIcon> = {
|
|
view: Eye,
|
|
edit: Edit,
|
|
delete: Trash2,
|
|
copy: Copy,
|
|
download: Download
|
|
};
|
|
|
|
export const TableActions = ({ actions, layout = "auto" }: TableActionsProps) => {
|
|
const visibleActions = actions.filter(a => !a.hidden);
|
|
|
|
// 자동 레이아웃: 3개 이하는 버튼, 4개 이상은 드롭다운
|
|
const useDropdown = layout === "dropdown" || (layout === "auto" && visibleActions.length > 3);
|
|
|
|
if (visibleActions.length === 0) return null;
|
|
|
|
// 드롭다운 레이아웃
|
|
if (useDropdown) {
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="sm">
|
|
<MoreHorizontal className="w-4 h-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
{visibleActions.map((action, index) => {
|
|
const Icon = action.icon || (action.type !== "custom" ? defaultIcons[action.type] : null);
|
|
return (
|
|
<DropdownMenuItem
|
|
key={index}
|
|
onClick={action.onClick}
|
|
className={action.variant === "destructive" ? "text-destructive" : ""}
|
|
>
|
|
{Icon && <Icon className="w-4 h-4 mr-2" />}
|
|
{action.label || action.type.charAt(0).toUpperCase() + action.type.slice(1)}
|
|
</DropdownMenuItem>
|
|
);
|
|
})}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
}
|
|
|
|
// 버튼 레이아웃
|
|
return (
|
|
<div className="flex gap-1">
|
|
{visibleActions.map((action, index) => {
|
|
const Icon = action.icon || (action.type !== "custom" ? defaultIcons[action.type] : null);
|
|
return (
|
|
<Button
|
|
key={index}
|
|
variant={action.variant || "ghost"}
|
|
size="sm"
|
|
onClick={action.onClick}
|
|
title={action.label || action.type}
|
|
>
|
|
{Icon && <Icon className="w-4 h-4" />}
|
|
</Button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}; |