Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into common/feat/dashboard-map

This commit is contained in:
dohyeons
2025-11-25 09:53:36 +09:00
47 changed files with 7416 additions and 1372 deletions

View File

@@ -78,37 +78,52 @@ export default function HierarchyConfigPanel({
const [loadingColumns, setLoadingColumns] = useState(false);
const [columnsCache, setColumnsCache] = useState<{ [tableName: string]: ColumnInfo[] }>({});
// 외부에서 변경된 경우 동기화 및 컬럼 자동 로드
// 외부에서 변경된 경우 동기화 및 컬럼 자동 로드
useEffect(() => {
if (hierarchyConfig) {
setLocalConfig(hierarchyConfig);
// 저장된 설정이 있으면 해당 테이블들 컬럼 자동 로드
// 저장된 설정 테이블들에 대한 컬럼 자동 로드
const loadSavedColumns = async () => {
// 창고 테이블 컬럼 로드
const tablesToLoad: string[] = [];
// 창고 테이블
if (hierarchyConfig.warehouse?.tableName) {
await handleTableChange(hierarchyConfig.warehouse.tableName, "warehouse");
tablesToLoad.push(hierarchyConfig.warehouse.tableName);
}
// 레벨 테이블 컬럼 로드
if (hierarchyConfig.levels) {
for (const level of hierarchyConfig.levels) {
if (level.tableName) {
await handleTableChange(level.tableName, level.level);
// 계층 레벨 테이블
hierarchyConfig.levels?.forEach((level) => {
if (level.tableName) {
tablesToLoad.push(level.tableName);
}
});
// 자재 테이블
if (hierarchyConfig.material?.tableName) {
tablesToLoad.push(hierarchyConfig.material.tableName);
}
// 중복 제거 후 로드
const uniqueTables = [...new Set(tablesToLoad)];
for (const tableName of uniqueTables) {
if (!columnsCache[tableName]) {
try {
const columns = await onLoadColumns(tableName);
setColumnsCache((prev) => ({ ...prev, [tableName]: columns }));
} catch (error) {
console.error(`컬럼 로드 실패 (${tableName}):`, error);
}
}
}
// 자재 테이블 컬럼 로드
if (hierarchyConfig.material?.tableName) {
await handleTableChange(hierarchyConfig.material.tableName, "material");
}
};
loadSavedColumns();
if (externalDbConnectionId) {
loadSavedColumns();
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hierarchyConfig]);
}, [hierarchyConfig, externalDbConnectionId]);
// 테이블 선택 시 컬럼 로드
const handleTableChange = async (tableName: string, type: "warehouse" | "material" | number) => {
@@ -229,7 +244,7 @@ export default function HierarchyConfigPanel({
<div className="flex flex-col">
<span>{table.table_name}</span>
{table.description && (
<span className="text-[9px] text-muted-foreground">{table.description}</span>
<span className="text-muted-foreground text-[9px]">{table.description}</span>
)}
</div>
</SelectItem>
@@ -237,7 +252,7 @@ export default function HierarchyConfigPanel({
</SelectContent>
</Select>
{!localConfig.warehouse?.tableName && (
<p className="mt-1 text-[9px] text-muted-foreground">
<p className="text-muted-foreground mt-1 text-[9px]">
"설정 적용"
</p>
)}
@@ -261,7 +276,7 @@ export default function HierarchyConfigPanel({
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[8px] text-muted-foreground">{col.description}</span>
<span className="text-muted-foreground text-[8px]">{col.description}</span>
)}
</div>
</SelectItem>
@@ -285,7 +300,7 @@ export default function HierarchyConfigPanel({
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[8px] text-muted-foreground">{col.description}</span>
<span className="text-muted-foreground text-[8px]">{col.description}</span>
)}
</div>
</SelectItem>
@@ -349,7 +364,7 @@ export default function HierarchyConfigPanel({
<div className="flex flex-col">
<span>{table.table_name}</span>
{table.description && (
<span className="text-[10px] text-muted-foreground">{table.description}</span>
<span className="text-muted-foreground text-[10px]">{table.description}</span>
)}
</div>
</SelectItem>
@@ -360,52 +375,52 @@ export default function HierarchyConfigPanel({
{level.tableName && columnsCache[level.tableName] && (
<>
<div>
<Label className="text-[10px]">ID </Label>
<Select
value={level.keyColumn || ""}
onValueChange={(val) => handleLevelChange(level.level, "keyColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[level.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className="text-[10px]">ID </Label>
<Select
value={level.keyColumn || ""}
onValueChange={(val) => handleLevelChange(level.level, "keyColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[level.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className="text-[10px]"> </Label>
<Select
value={level.nameColumn || ""}
onValueChange={(val) => handleLevelChange(level.level, "nameColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[level.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
<div>
<Label className="text-[10px]"> </Label>
<Select
value={level.nameColumn || ""}
onValueChange={(val) => handleLevelChange(level.level, "nameColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[level.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
@@ -423,7 +438,7 @@ export default function HierarchyConfigPanel({
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
@@ -450,7 +465,7 @@ export default function HierarchyConfigPanel({
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
@@ -496,7 +511,7 @@ export default function HierarchyConfigPanel({
<div className="flex flex-col">
<span>{table.table_name}</span>
{table.description && (
<span className="text-[10px] text-muted-foreground">{table.description}</span>
<span className="text-muted-foreground text-[10px]">{table.description}</span>
)}
</div>
</SelectItem>
@@ -507,102 +522,102 @@ export default function HierarchyConfigPanel({
{localConfig.material?.tableName && columnsCache[localConfig.material.tableName] && (
<>
<div>
<Label className="text-[10px]">ID </Label>
<Select
value={localConfig.material.keyColumn || ""}
onValueChange={(val) => handleMaterialChange("keyColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className="text-[10px]"> </Label>
<Select
value={localConfig.material.locationKeyColumn || ""}
onValueChange={(val) => handleMaterialChange("locationKeyColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
<div>
<Label className="text-[10px]">ID </Label>
<Select
value={localConfig.material.keyColumn || ""}
onValueChange={(val) => handleMaterialChange("keyColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className="text-[10px]"> ()</Label>
<Select
<div>
<Label className="text-[10px]"> </Label>
<Select
value={localConfig.material.locationKeyColumn || ""}
onValueChange={(val) => handleMaterialChange("locationKeyColumn", val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className="text-[10px]"> ()</Label>
<Select
value={localConfig.material.layerColumn || "__none__"}
onValueChange={(val) => handleMaterialChange("layerColumn", val === "__none__" ? undefined : val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue placeholder="레이어 컬럼" />
</SelectTrigger>
<SelectContent>
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue placeholder="레이어 컬럼" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"></SelectItem>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label className="text-[10px]"> ()</Label>
<Select
<div>
<Label className="text-[10px]"> ()</Label>
<Select
value={localConfig.material.quantityColumn || "__none__"}
onValueChange={(val) => handleMaterialChange("quantityColumn", val === "__none__" ? undefined : val)}
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue placeholder="수량 컬럼" />
</SelectTrigger>
<SelectContent>
>
<SelectTrigger className="h-7 text-[10px]">
<SelectValue placeholder="수량 컬럼" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"></SelectItem>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-[9px] text-muted-foreground">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
{columnsCache[localConfig.material.tableName].map((col) => (
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
<div className="flex flex-col">
<span>{col.column_name}</span>
{col.description && (
<span className="text-muted-foreground text-[9px]">{col.description}</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Separator className="my-3" />
@@ -634,7 +649,7 @@ export default function HierarchyConfigPanel({
<div className="flex w-24 shrink-0 flex-col">
<span className="text-[10px]">{col.column_name}</span>
{col.description && (
<span className="text-[8px] text-muted-foreground">{col.description}</span>
<span className="text-muted-foreground text-[8px]">{col.description}</span>
)}
</div>
{isSelected && (