테이블 템플릿 제작

This commit is contained in:
kjs
2025-09-03 15:23:12 +09:00
parent 55a7e1dc89
commit 4a0c42d80c
14 changed files with 3757 additions and 42 deletions

View File

@@ -9,10 +9,12 @@ import { Separator } from "@/components/ui/separator";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { Settings, Move, Type, Trash2, Copy, Group, Ungroup } from "lucide-react";
import { ComponentData, WebType, WidgetComponent, GroupComponent } from "@/types/screen";
import { ComponentData, WebType, WidgetComponent, GroupComponent, DataTableComponent, TableInfo } from "@/types/screen";
import DataTableConfigPanel from "./DataTableConfigPanel";
interface PropertiesPanelProps {
selectedComponent?: ComponentData;
tables?: TableInfo[];
onUpdateProperty: (path: string, value: unknown) => void;
onDeleteComponent: () => void;
onCopyComponent: () => void;
@@ -43,6 +45,7 @@ const webTypeOptions: { value: WebType; label: string }[] = [
export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
selectedComponent,
tables = [],
onUpdateProperty,
onDeleteComponent,
onCopyComponent,
@@ -71,6 +74,7 @@ export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
labelMarginBottom: selectedComponent?.style?.labelMarginBottom || "4px",
required: (selectedComponent?.type === "widget" ? (selectedComponent as WidgetComponent).required : false) || false,
readonly: (selectedComponent?.type === "widget" ? (selectedComponent as WidgetComponent).readonly : false) || false,
labelDisplay: selectedComponent?.style?.labelDisplay !== false,
});
useEffect(() => {
@@ -83,6 +87,18 @@ export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
if (selectedComponent) {
const widget = selectedComponent.type === "widget" ? (selectedComponent as WidgetComponent) : null;
const group = selectedComponent.type === "group" ? (selectedComponent as GroupComponent) : null;
console.log("🔄 PropertiesPanel: 컴포넌트 변경 감지", {
componentId: selectedComponent.id,
componentType: selectedComponent.type,
currentValues: {
placeholder: widget?.placeholder,
title: group?.title,
positionX: selectedComponent.position.x,
labelText: selectedComponent.style?.labelText || selectedComponent.label,
},
});
setLocalInputs({
placeholder: widget?.placeholder || "",
title: group?.title || "",
@@ -98,9 +114,16 @@ export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
labelMarginBottom: selectedComponent.style?.labelMarginBottom || "4px",
required: widget?.required || false,
readonly: widget?.readonly || false,
labelDisplay: selectedComponent.style?.labelDisplay !== false,
});
}
}, [selectedComponent]);
}, [
selectedComponent,
selectedComponent?.position,
selectedComponent?.size,
selectedComponent?.style,
selectedComponent?.label,
]);
if (!selectedComponent) {
return (
@@ -112,6 +135,65 @@ export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
);
}
// 데이터 테이블 컴포넌트인 경우 전용 패널 사용
if (selectedComponent.type === "datatable") {
return (
<div className="flex h-full flex-col">
{/* 헤더 */}
<div className="border-b border-gray-200 p-4">
<div className="mb-3 flex items-center justify-between">
<div className="flex items-center space-x-2">
<Settings className="h-5 w-5 text-gray-600" />
<span className="text-lg font-semibold"> </span>
</div>
<Badge variant="secondary" className="text-xs">
{selectedComponent.type}
</Badge>
</div>
{/* 액션 버튼들 */}
<div className="flex flex-wrap gap-2">
<Button variant="outline" size="sm" onClick={onCopyComponent}>
<Copy className="mr-1 h-4 w-4" />
</Button>
<Button variant="destructive" size="sm" onClick={onDeleteComponent}>
<Trash2 className="mr-1 h-4 w-4" />
</Button>
</div>
</div>
{/* 데이터 테이블 설정 패널 */}
<div className="flex-1 overflow-y-auto">
<DataTableConfigPanel
key={`datatable-${selectedComponent.id}-${selectedComponent.columns.length}-${selectedComponent.filters.length}-${JSON.stringify(selectedComponent.columns.map((c) => c.id))}-${JSON.stringify(selectedComponent.filters.map((f) => f.columnName))}`}
component={selectedComponent as DataTableComponent}
tables={tables}
onUpdateComponent={(updates) => {
console.log("🔄 DataTable 컴포넌트 업데이트:", updates);
console.log("🔄 업데이트 항목들:", Object.keys(updates));
// 각 속성을 개별적으로 업데이트
Object.entries(updates).forEach(([key, value]) => {
console.log(` - ${key}:`, value);
if (key === "columns") {
console.log(` 컬럼 개수: ${Array.isArray(value) ? value.length : 0}`);
}
if (key === "filters") {
console.log(` 필터 개수: ${Array.isArray(value) ? value.length : 0}`);
}
onUpdateProperty(key, value);
});
console.log("✅ DataTable 컴포넌트 업데이트 완료");
}}
/>
</div>
</div>
);
}
return (
<div className="flex h-full flex-col">
{/* 헤더 */}
@@ -210,6 +292,7 @@ export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
value={localInputs.placeholder}
onChange={(e) => {
const newValue = e.target.value;
console.log("🔄 placeholder 변경:", newValue);
setLocalInputs((prev) => ({ ...prev, placeholder: newValue }));
onUpdateProperty("placeholder", newValue);
}}
@@ -394,8 +477,12 @@ export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
</Label>
<Checkbox
id="labelDisplay"
checked={selectedComponent.style?.labelDisplay !== false}
onCheckedChange={(checked) => onUpdateProperty("style.labelDisplay", checked)}
checked={localInputs.labelDisplay}
onCheckedChange={(checked) => {
console.log("🔄 라벨 표시 변경:", checked);
setLocalInputs((prev) => ({ ...prev, labelDisplay: checked as boolean }));
onUpdateProperty("style.labelDisplay", checked);
}}
/>
</div>
@@ -409,6 +496,7 @@ export const PropertiesPanel: React.FC<PropertiesPanelProps> = ({
value={localInputs.labelText}
onChange={(e) => {
const newValue = e.target.value;
console.log("🔄 라벨 텍스트 변경:", newValue);
setLocalInputs((prev) => ({ ...prev, labelText: newValue }));
// 기본 라벨과 스타일 라벨을 모두 업데이트
onUpdateProperty("label", newValue);