연쇄관계 관리
This commit is contained in:
@@ -7,9 +7,12 @@ import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Plus, Trash2, ChevronDown, List } from "lucide-react";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Plus, Trash2, List, Link2, ExternalLink } from "lucide-react";
|
||||
import { WebTypeConfigPanelProps } from "@/lib/registry/types";
|
||||
import { WidgetComponent, SelectTypeConfig } from "@/types/screen";
|
||||
import { cascadingRelationApi, CascadingRelation } from "@/lib/api/cascadingRelation";
|
||||
import Link from "next/link";
|
||||
|
||||
interface SelectOption {
|
||||
label: string;
|
||||
@@ -38,7 +41,18 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
required: config.required || false,
|
||||
readonly: config.readonly || false,
|
||||
emptyMessage: config.emptyMessage || "선택 가능한 옵션이 없습니다",
|
||||
cascadingRelationCode: config.cascadingRelationCode,
|
||||
cascadingParentField: config.cascadingParentField,
|
||||
});
|
||||
|
||||
// 연쇄 드롭다운 설정 상태
|
||||
const [cascadingEnabled, setCascadingEnabled] = useState(!!config.cascadingRelationCode);
|
||||
const [selectedRelationCode, setSelectedRelationCode] = useState(config.cascadingRelationCode || "");
|
||||
const [selectedParentField, setSelectedParentField] = useState(config.cascadingParentField || "");
|
||||
|
||||
// 연쇄 관계 목록
|
||||
const [relationList, setRelationList] = useState<CascadingRelation[]>([]);
|
||||
const [loadingRelations, setLoadingRelations] = useState(false);
|
||||
|
||||
// 새 옵션 추가용 상태
|
||||
const [newOptionLabel, setNewOptionLabel] = useState("");
|
||||
@@ -66,6 +80,7 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
required: currentConfig.required || false,
|
||||
readonly: currentConfig.readonly || false,
|
||||
emptyMessage: currentConfig.emptyMessage || "선택 가능한 옵션이 없습니다",
|
||||
cascadingRelationCode: currentConfig.cascadingRelationCode,
|
||||
});
|
||||
|
||||
// 입력 필드 로컬 상태도 동기화
|
||||
@@ -73,7 +88,34 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
placeholder: currentConfig.placeholder || "",
|
||||
emptyMessage: currentConfig.emptyMessage || "",
|
||||
});
|
||||
|
||||
// 연쇄 드롭다운 설정 동기화
|
||||
setCascadingEnabled(!!currentConfig.cascadingRelationCode);
|
||||
setSelectedRelationCode(currentConfig.cascadingRelationCode || "");
|
||||
setSelectedParentField(currentConfig.cascadingParentField || "");
|
||||
}, [widget.webTypeConfig]);
|
||||
|
||||
// 연쇄 관계 목록 로드
|
||||
useEffect(() => {
|
||||
if (cascadingEnabled && relationList.length === 0) {
|
||||
loadRelationList();
|
||||
}
|
||||
}, [cascadingEnabled]);
|
||||
|
||||
// 연쇄 관계 목록 로드 함수
|
||||
const loadRelationList = async () => {
|
||||
setLoadingRelations(true);
|
||||
try {
|
||||
const response = await cascadingRelationApi.getList("Y");
|
||||
if (response.success && response.data) {
|
||||
setRelationList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("연쇄 관계 목록 로드 실패:", error);
|
||||
} finally {
|
||||
setLoadingRelations(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 설정 업데이트 핸들러
|
||||
const updateConfig = (field: keyof SelectTypeConfig, value: any) => {
|
||||
@@ -82,6 +124,38 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
onUpdateProperty("webTypeConfig", newConfig);
|
||||
};
|
||||
|
||||
// 연쇄 드롭다운 활성화/비활성화
|
||||
const handleCascadingToggle = (enabled: boolean) => {
|
||||
setCascadingEnabled(enabled);
|
||||
|
||||
if (!enabled) {
|
||||
// 비활성화 시 관계 코드 제거
|
||||
setSelectedRelationCode("");
|
||||
const newConfig = { ...localConfig, cascadingRelationCode: undefined };
|
||||
setLocalConfig(newConfig);
|
||||
onUpdateProperty("webTypeConfig", newConfig);
|
||||
} else {
|
||||
// 활성화 시 관계 목록 로드
|
||||
loadRelationList();
|
||||
}
|
||||
};
|
||||
|
||||
// 연쇄 관계 선택
|
||||
const handleRelationSelect = (code: string) => {
|
||||
setSelectedRelationCode(code);
|
||||
const newConfig = { ...localConfig, cascadingRelationCode: code || undefined };
|
||||
setLocalConfig(newConfig);
|
||||
onUpdateProperty("webTypeConfig", newConfig);
|
||||
};
|
||||
|
||||
// 부모 필드 선택
|
||||
const handleParentFieldChange = (field: string) => {
|
||||
setSelectedParentField(field);
|
||||
const newConfig = { ...localConfig, cascadingParentField: field || undefined };
|
||||
setLocalConfig(newConfig);
|
||||
onUpdateProperty("webTypeConfig", newConfig);
|
||||
};
|
||||
|
||||
// 옵션 추가
|
||||
const addOption = () => {
|
||||
if (!newOptionLabel.trim() || !newOptionValue.trim()) return;
|
||||
@@ -167,6 +241,9 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
updateConfig("options", defaultOptionSets[setName]);
|
||||
};
|
||||
|
||||
// 선택된 관계 정보
|
||||
const selectedRelation = relationList.find(r => r.relation_code === selectedRelationCode);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -238,23 +315,122 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 기본 옵션 세트 */}
|
||||
{/* 연쇄 드롭다운 설정 */}
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium">기본 옵션 세트</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button size="sm" variant="outline" onClick={() => applyDefaultSet("yesno")} className="text-xs">
|
||||
예/아니오
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => applyDefaultSet("status")} className="text-xs">
|
||||
상태
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => applyDefaultSet("priority")} className="text-xs">
|
||||
우선순위
|
||||
</Button>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Link2 className="h-4 w-4" />
|
||||
<h4 className="text-sm font-medium">연쇄 드롭다운</h4>
|
||||
</div>
|
||||
<Switch
|
||||
checked={cascadingEnabled}
|
||||
onCheckedChange={handleCascadingToggle}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
다른 필드의 값에 따라 옵션이 동적으로 변경됩니다. (예: 창고 선택 → 해당 창고의 위치만 표시)
|
||||
</p>
|
||||
|
||||
{cascadingEnabled && (
|
||||
<div className="space-y-3 rounded-md border p-3">
|
||||
{/* 관계 선택 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs">연쇄 관계 선택</Label>
|
||||
<Select
|
||||
value={selectedRelationCode}
|
||||
onValueChange={handleRelationSelect}
|
||||
>
|
||||
<SelectTrigger className="text-xs">
|
||||
<SelectValue placeholder={loadingRelations ? "로딩 중..." : "관계 선택"} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{relationList.map((relation) => (
|
||||
<SelectItem key={relation.relation_code} value={relation.relation_code}>
|
||||
<div className="flex flex-col">
|
||||
<span>{relation.relation_name}</span>
|
||||
<span className="text-muted-foreground text-xs">
|
||||
{relation.parent_table} → {relation.child_table}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
미리 정의된 관계를 선택하세요.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 부모 필드 설정 */}
|
||||
{selectedRelationCode && (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs">부모 필드 (화면 내 필드명)</Label>
|
||||
<Input
|
||||
value={selectedParentField}
|
||||
onChange={(e) => handleParentFieldChange(e.target.value)}
|
||||
placeholder="예: warehouse_code"
|
||||
className="text-xs"
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
이 드롭다운의 옵션을 결정할 부모 필드의 컬럼명을 입력하세요.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 선택된 관계 정보 표시 */}
|
||||
{selectedRelation && (
|
||||
<div className="bg-muted/50 space-y-2 rounded-md p-2">
|
||||
<div className="text-xs">
|
||||
<span className="text-muted-foreground">부모 테이블:</span>{" "}
|
||||
<span className="font-medium">{selectedRelation.parent_table}</span>
|
||||
<span className="text-muted-foreground"> ({selectedRelation.parent_value_column})</span>
|
||||
</div>
|
||||
<div className="text-xs">
|
||||
<span className="text-muted-foreground">자식 테이블:</span>{" "}
|
||||
<span className="font-medium">{selectedRelation.child_table}</span>
|
||||
<span className="text-muted-foreground">
|
||||
{" "}({selectedRelation.child_filter_column} → {selectedRelation.child_value_column})
|
||||
</span>
|
||||
</div>
|
||||
{selectedRelation.description && (
|
||||
<div className="text-muted-foreground text-xs">{selectedRelation.description}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 관계 관리 페이지 링크 */}
|
||||
<div className="flex justify-end">
|
||||
<Link href="/admin/cascading-relations" target="_blank">
|
||||
<Button variant="link" size="sm" className="h-auto p-0 text-xs">
|
||||
<ExternalLink className="mr-1 h-3 w-3" />
|
||||
관계 관리
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 옵션 관리 */}
|
||||
{/* 기본 옵션 세트 (연쇄 드롭다운 비활성화 시에만 표시) */}
|
||||
{!cascadingEnabled && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium">기본 옵션 세트</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button size="sm" variant="outline" onClick={() => applyDefaultSet("yesno")} className="text-xs">
|
||||
예/아니오
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => applyDefaultSet("status")} className="text-xs">
|
||||
상태
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => applyDefaultSet("priority")} className="text-xs">
|
||||
우선순위
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 옵션 관리 (연쇄 드롭다운 비활성화 시에만 표시) */}
|
||||
{!cascadingEnabled && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium">옵션 관리</h4>
|
||||
|
||||
@@ -337,8 +513,10 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 기본값 설정 */}
|
||||
{/* 기본값 설정 (연쇄 드롭다운 비활성화 시에만 표시) */}
|
||||
{!cascadingEnabled && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium">기본값</h4>
|
||||
|
||||
@@ -361,6 +539,7 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 상태 설정 */}
|
||||
<div className="space-y-3">
|
||||
@@ -395,7 +574,8 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 미리보기 */}
|
||||
{/* 미리보기 (연쇄 드롭다운 비활성화 시에만 표시) */}
|
||||
{!cascadingEnabled && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium">미리보기</h4>
|
||||
<div className="bg-muted/50 rounded-md border p-3">
|
||||
@@ -422,11 +602,10 @@ export const SelectConfigPanel: React.FC<WebTypeConfigPanelProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
SelectConfigPanel.displayName = "SelectConfigPanel";
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user