ERP-node/frontend/lib/registry/components/customer-item-mapping/CustomerItemMappingComponen...

259 lines
9.3 KiB
TypeScript
Raw Normal View History

"use client";
import React, { useState, useEffect } from "react";
import { CustomerItemMappingConfig } from "./types";
import { Checkbox } from "@/components/ui/checkbox";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";
export interface CustomerItemMappingComponentProps {
component: any;
isDesignMode?: boolean;
isSelected?: boolean;
isInteractive?: boolean;
config?: CustomerItemMappingConfig;
className?: string;
style?: React.CSSProperties;
onClick?: (e?: React.MouseEvent) => void;
onDragStart?: (e: React.DragEvent) => void;
onDragEnd?: (e: React.DragEvent) => void;
}
export const CustomerItemMappingComponent: React.FC<CustomerItemMappingComponentProps> = ({
component,
isDesignMode = false,
isSelected = false,
config,
className,
style,
onClick,
onDragStart,
onDragEnd,
}) => {
const finalConfig = {
...config,
...component.config,
} as CustomerItemMappingConfig;
const [data, setData] = useState<any[]>([]);
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
const [isAllSelected, setIsAllSelected] = useState(false);
// 데이터 로드 (실제 구현 시 API 호출)
useEffect(() => {
if (!isDesignMode && finalConfig.selectedTable) {
// TODO: API 호출로 데이터 로드
setData([]);
}
}, [finalConfig.selectedTable, isDesignMode]);
const handleSelectAll = (checked: boolean) => {
if (checked) {
const allIds = data.map((_, index) => `row-${index}`);
setSelectedRows(new Set(allIds));
setIsAllSelected(true);
} else {
setSelectedRows(new Set());
setIsAllSelected(false);
}
};
const handleRowSelection = (rowId: string, checked: boolean) => {
const newSelected = new Set(selectedRows);
if (checked) {
newSelected.add(rowId);
} else {
newSelected.delete(rowId);
}
setSelectedRows(newSelected);
setIsAllSelected(newSelected.size === data.length && data.length > 0);
};
const columns = finalConfig.columns || [];
const showCheckbox = finalConfig.checkbox?.enabled !== false;
// 스타일 계산
const componentStyle: React.CSSProperties = {
position: "relative",
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
backgroundColor: "hsl(var(--background))",
overflow: "hidden",
boxSizing: "border-box",
};
// 이벤트 핸들러
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
onClick?.();
};
return (
<div
className={cn("w-full h-full", className)}
style={componentStyle}
onClick={handleClick}
onDragStart={isDesignMode ? onDragStart : undefined}
onDragEnd={isDesignMode ? onDragEnd : undefined}
draggable={isDesignMode}
>
{/* 헤더 */}
<div className="w-full border-border bg-muted flex h-12 flex-shrink-0 items-center justify-between border-b px-4 sm:h-14 sm:px-6">
<h3 className="text-sm font-semibold sm:text-base">
- {finalConfig.selectedTable || "[테이블 선택]"}
</h3>
<button className="hover:bg-muted-foreground/10 rounded p-1">
<X className="h-4 w-4 sm:h-5 sm:w-5" />
</button>
</div>
{/* 검색/카테고리 영역 - 회색 배경 */}
{finalConfig.showSearchArea && (
<div
className="w-full border-border bg-muted flex-shrink-0 border-b"
style={{ height: finalConfig.searchAreaHeight || 80 }}
>
<div className="flex h-full w-full items-center justify-center p-4">
<div className="text-muted-foreground flex flex-col items-center gap-2 text-center">
<div className="bg-background/50 rounded-full p-3">
<svg
className="h-5 w-5 sm:h-6 sm:w-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
</div>
<p className="text-xs font-medium sm:text-sm">
</p>
<p className="text-[10px] text-muted-foreground/70 sm:text-xs">
</p>
</div>
</div>
</div>
)}
{/* 목록 헤더 */}
<div className="w-full border-border bg-muted/50 flex h-10 flex-shrink-0 items-center justify-between border-b px-4 sm:h-12 sm:px-6">
<span className="text-xs font-semibold sm:text-sm"> </span>
<div className="flex items-center gap-3 sm:gap-6">
{showCheckbox && finalConfig.checkbox?.selectAll && (
<label className="flex cursor-pointer items-center gap-2">
<Checkbox checked={isAllSelected} onCheckedChange={handleSelectAll} />
<span className="text-xs sm:text-sm"> </span>
</label>
)}
<span className="text-muted-foreground text-xs font-medium sm:text-sm">
: {selectedRows.size}
</span>
</div>
</div>
{/* 테이블 컨테이너 */}
<div className="flex w-full flex-1 flex-col overflow-hidden">
{/* 테이블 헤더 */}
{columns.length > 0 && (
<div className="border-border flex-shrink-0 border-b">
<div className="overflow-x-auto">
<table className="w-full" style={{ minWidth: "100%" }}>
<thead>
<tr className="bg-muted/30 h-10 sm:h-12">
{showCheckbox && (
<th className="border-border w-12 border-r px-2 text-center sm:w-16 sm:px-3"></th>
)}
{columns.map((col, index) => (
<th
key={col}
className={cn(
"border-border text-foreground px-3 text-left text-xs font-semibold sm:px-6 sm:text-sm",
index < columns.length - 1 && "border-r"
)}
>
{col}
</th>
))}
</tr>
</thead>
</table>
</div>
</div>
)}
{/* 데이터 영역 */}
<div className="flex-1 overflow-y-auto">
{data.length === 0 ? (
<div className="flex h-full flex-col items-center justify-center gap-3 p-8 text-center sm:gap-4 sm:p-12">
<div className="bg-muted/50 flex h-16 w-16 items-center justify-center rounded-full sm:h-20 sm:w-20">
<svg
className="text-muted-foreground h-8 w-8 sm:h-10 sm:w-10"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
/>
</svg>
</div>
<div className="space-y-1 sm:space-y-2">
<p className="text-foreground text-base font-semibold sm:text-lg">
{finalConfig.emptyMessage || "데이터가 없습니다"}
</p>
<p className="text-muted-foreground text-xs sm:text-sm">
{finalConfig.emptyDescription || "품목 데이터가 추가되면 여기에 표시됩니다"}
</p>
</div>
</div>
) : (
<div className="overflow-x-auto">
<table className="w-full" style={{ minWidth: "100%" }}>
<tbody>
{data.map((row, index) => (
<tr key={index} className="hover:bg-muted/50 border-b transition-colors">
{showCheckbox && (
<td className="border-border w-12 border-r px-2 text-center sm:w-16 sm:px-3">
<Checkbox
checked={selectedRows.has(`row-${index}`)}
onCheckedChange={(checked) =>
handleRowSelection(`row-${index}`, checked as boolean)
}
/>
</td>
)}
{columns.map((col, colIndex) => (
<td
key={col}
className={cn(
"border-border px-3 py-2 text-xs sm:px-6 sm:py-3 sm:text-sm",
colIndex < columns.length - 1 && "border-r"
)}
>
{row[col] || "-"}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
</div>
);
};