ERP-node/frontend/lib/registry/components/conditional-container/ConditionalContainerConfigP...

344 lines
12 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Plus, Trash2, GripVertical, Loader2 } from "lucide-react";
import { ConditionalContainerConfig, ConditionalSection } from "./types";
import { screenApi } from "@/lib/api/screen";
interface ConditionalContainerConfigPanelProps {
config: ConditionalContainerConfig;
onConfigChange: (config: ConditionalContainerConfig) => void;
}
export function ConditionalContainerConfigPanel({
config,
onConfigChange,
}: ConditionalContainerConfigPanelProps) {
const [localConfig, setLocalConfig] = useState<ConditionalContainerConfig>({
controlField: config.controlField || "condition",
controlLabel: config.controlLabel || "조건 선택",
sections: config.sections || [],
defaultValue: config.defaultValue || "",
showBorder: config.showBorder ?? true,
spacing: config.spacing || "normal",
});
// 화면 목록 상태
const [screens, setScreens] = useState<any[]>([]);
const [screensLoading, setScreensLoading] = useState(false);
// 화면 목록 로드
useEffect(() => {
const loadScreens = async () => {
setScreensLoading(true);
try {
const response = await screenApi.getScreens({ page: 1, size: 1000 });
if (response.data) {
setScreens(response.data);
}
} catch (error) {
console.error("화면 목록 로드 실패:", error);
} finally {
setScreensLoading(false);
}
};
loadScreens();
}, []);
// 설정 업데이트 헬퍼
const updateConfig = (updates: Partial<ConditionalContainerConfig>) => {
const newConfig = { ...localConfig, ...updates };
setLocalConfig(newConfig);
onConfigChange(newConfig);
};
// 새 섹션 추가
const addSection = () => {
const newSection: ConditionalSection = {
id: `section_${Date.now()}`,
condition: `condition_${localConfig.sections.length + 1}`,
label: `조건 ${localConfig.sections.length + 1}`,
screenId: null,
screenName: undefined,
};
updateConfig({
sections: [...localConfig.sections, newSection],
});
};
// 섹션 삭제
const removeSection = (sectionId: string) => {
updateConfig({
sections: localConfig.sections.filter((s) => s.id !== sectionId),
});
};
// 섹션 업데이트
const updateSection = (
sectionId: string,
updates: Partial<ConditionalSection>
) => {
updateConfig({
sections: localConfig.sections.map((s) =>
s.id === sectionId ? { ...s, ...updates } : s
),
});
};
return (
<div className="space-y-6 p-4">
<div>
<h3 className="text-sm font-semibold mb-4"> </h3>
{/* 제어 필드 설정 */}
<div className="space-y-4 mb-6">
<div className="space-y-2">
<Label htmlFor="controlField" className="text-xs">
</Label>
<Input
id="controlField"
value={localConfig.controlField}
onChange={(e) => updateConfig({ controlField: e.target.value })}
placeholder="예: inputMode"
className="h-8 text-xs"
/>
<p className="text-[10px] text-muted-foreground">
formData에
</p>
</div>
<div className="space-y-2">
<Label htmlFor="controlLabel" className="text-xs">
</Label>
<Input
id="controlLabel"
value={localConfig.controlLabel}
onChange={(e) => updateConfig({ controlLabel: e.target.value })}
placeholder="예: 입력 방식"
className="h-8 text-xs"
/>
</div>
</div>
{/* 조건별 섹션 설정 */}
<div className="space-y-4">
<div className="flex items-center justify-between">
<Label className="text-xs font-semibold"> </Label>
<Button
onClick={addSection}
size="sm"
variant="outline"
className="h-7 text-xs"
>
<Plus className="h-3 w-3 mr-1" />
</Button>
</div>
{localConfig.sections.length === 0 ? (
<div className="text-center py-8 border-2 border-dashed rounded-lg">
<p className="text-xs text-muted-foreground">
</p>
</div>
) : (
<div className="space-y-3">
{localConfig.sections.map((section, index) => (
<div
key={section.id}
className="p-3 border rounded-lg space-y-3 bg-muted/20"
>
{/* 섹션 헤더 */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<GripVertical className="h-4 w-4 text-muted-foreground" />
<span className="text-xs font-medium">
{index + 1}
</span>
</div>
<Button
onClick={() => removeSection(section.id)}
size="sm"
variant="ghost"
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
>
<Trash2 className="h-3 w-3" />
</Button>
</div>
{/* 조건 값 */}
<div className="space-y-1.5">
<Label className="text-[10px] text-muted-foreground">
()
</Label>
<Input
value={section.condition}
onChange={(e) =>
updateSection(section.id, { condition: e.target.value })
}
placeholder="예: customer_first"
className="h-7 text-xs"
/>
</div>
{/* 조건 라벨 */}
<div className="space-y-1.5">
<Label className="text-[10px] text-muted-foreground">
</Label>
<Input
value={section.label}
onChange={(e) =>
updateSection(section.id, { label: e.target.value })
}
placeholder="예: 거래처 우선"
className="h-7 text-xs"
/>
</div>
{/* 화면 선택 */}
<div className="space-y-1">
<Label className="text-[10px] text-muted-foreground">
</Label>
{screensLoading ? (
<div className="flex items-center gap-2 text-xs text-muted-foreground h-7 px-3 border rounded">
<Loader2 className="h-3 w-3 animate-spin" />
...
</div>
) : (
<Select
value={section.screenId?.toString() || "none"}
onValueChange={(value) => {
if (value === "none") {
updateSection(section.id, {
screenId: null,
screenName: undefined,
});
} else {
const screenId = parseInt(value);
const selectedScreen = screens.find(
(s) => s.screenId === screenId
);
updateSection(section.id, {
screenId,
screenName: selectedScreen?.screenName,
});
}
}}
>
<SelectTrigger className="h-7 text-xs">
<SelectValue placeholder="화면 선택..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="none"> </SelectItem>
{screens.map((screen) => (
<SelectItem
key={screen.screenId}
value={screen.screenId.toString()}
>
{screen.screenName}
{screen.description && (
<span className="text-[10px] text-muted-foreground ml-1">
({screen.description})
</span>
)}
</SelectItem>
))}
</SelectContent>
</Select>
)}
{section.screenId && (
<div className="text-[10px] text-muted-foreground">
ID: {section.screenId}
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
{/* 기본값 설정 */}
{localConfig.sections.length > 0 && (
<div className="space-y-2 mt-4">
<Label htmlFor="defaultValue" className="text-xs">
</Label>
<Select
value={localConfig.defaultValue || ""}
onValueChange={(value) => updateConfig({ defaultValue: value })}
>
<SelectTrigger className="h-8 text-xs">
<SelectValue placeholder="기본값 선택" />
</SelectTrigger>
<SelectContent>
{localConfig.sections.map((section) => (
<SelectItem key={section.id} value={section.condition}>
{section.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{/* 스타일 설정 */}
<div className="space-y-4 mt-6 pt-6 border-t">
<Label className="text-xs font-semibold"> </Label>
{/* 테두리 표시 */}
<div className="flex items-center justify-between">
<Label htmlFor="showBorder" className="text-xs">
</Label>
<Switch
id="showBorder"
checked={localConfig.showBorder}
onCheckedChange={(checked) =>
updateConfig({ showBorder: checked })
}
/>
</div>
{/* 간격 설정 */}
<div className="space-y-2">
<Label htmlFor="spacing" className="text-xs">
</Label>
<Select
value={localConfig.spacing || "normal"}
onValueChange={(value: any) => updateConfig({ spacing: value })}
>
<SelectTrigger className="h-8 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="tight"></SelectItem>
<SelectItem value="normal"></SelectItem>
<SelectItem value="loose"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
</div>
);
}