ERP-node/frontend/lib/registry/components/v2-rack-structure/RackStructureConfigPanel.tsx

384 lines
13 KiB
TypeScript

"use client";
import React, { useState, useEffect, useMemo } from "react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { RackStructureComponentConfig, FieldMapping } from "./types";
import { applyLocationPattern, DEFAULT_CODE_PATTERN, DEFAULT_NAME_PATTERN, PATTERN_VARIABLES } from "./patternUtils";
// 패턴 미리보기 서브 컴포넌트
const PatternPreview: React.FC<{
codePattern?: string;
namePattern?: string;
}> = ({ codePattern, namePattern }) => {
const sampleVars = {
warehouse: "WH002",
warehouseName: "2창고",
floor: "2층",
zone: "A구역",
row: 1,
level: 3,
};
const previewCode = useMemo(
() => applyLocationPattern(codePattern || DEFAULT_CODE_PATTERN, sampleVars),
[codePattern],
);
const previewName = useMemo(
() => applyLocationPattern(namePattern || DEFAULT_NAME_PATTERN, sampleVars),
[namePattern],
);
return (
<div className="rounded-md border border-primary/20 bg-primary/5 p-2.5">
<div className="mb-1.5 text-[10px] font-medium text-primary"> (2 / 2 / A구역 / 1 / 3)</div>
<div className="space-y-1">
<div className="flex items-center gap-2 text-xs">
<span className="w-14 shrink-0 text-muted-foreground">:</span>
<code className="rounded bg-background px-1.5 py-0.5 font-mono text-foreground">{previewCode}</code>
</div>
<div className="flex items-center gap-2 text-xs">
<span className="w-14 shrink-0 text-muted-foreground">:</span>
<code className="rounded bg-background px-1.5 py-0.5 font-mono text-foreground">{previewName}</code>
</div>
</div>
</div>
);
};
interface RackStructureConfigPanelProps {
config: RackStructureComponentConfig;
onChange: (config: RackStructureComponentConfig) => void;
// 화면관리에서 전달하는 테이블 컬럼 정보
tables?: Array<{
tableName: string;
tableLabel?: string;
columns: Array<{
columnName: string;
columnLabel?: string;
dataType?: string;
}>;
}>;
}
export const RackStructureConfigPanel: React.FC<RackStructureConfigPanelProps> = ({
config,
onChange,
tables = [],
}) => {
// 사용 가능한 컬럼 목록 추출
const [availableColumns, setAvailableColumns] = useState<
Array<{ value: string; label: string }>
>([]);
useEffect(() => {
// 모든 테이블의 컬럼을 플랫하게 추출
const columns: Array<{ value: string; label: string }> = [];
tables.forEach((table) => {
table.columns.forEach((col) => {
columns.push({
value: col.columnName,
label: col.columnLabel || col.columnName,
});
});
});
setAvailableColumns(columns);
}, [tables]);
const handleChange = (key: keyof RackStructureComponentConfig, value: any) => {
onChange({ ...config, [key]: value });
};
const handleFieldMappingChange = (field: keyof FieldMapping, value: string) => {
const currentMapping = config.fieldMapping || {};
onChange({
...config,
fieldMapping: {
...currentMapping,
[field]: value === "__none__" ? undefined : value,
},
});
};
const fieldMapping = config.fieldMapping || {};
return (
<div className="space-y-4">
{/* 필드 매핑 섹션 */}
<div className="space-y-3">
<div className="text-sm font-medium text-foreground"> </div>
<p className="text-xs text-muted-foreground">
</p>
{/* 창고 코드 필드 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={fieldMapping.warehouseCodeField || "__none__"}
onValueChange={(v) => handleFieldMappingChange("warehouseCodeField", v)}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="필드 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"> </SelectItem>
{availableColumns.map((col) => (
<SelectItem key={col.value} value={col.value}>
{col.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 창고명 필드 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={fieldMapping.warehouseNameField || "__none__"}
onValueChange={(v) => handleFieldMappingChange("warehouseNameField", v)}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="필드 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"> </SelectItem>
{availableColumns.map((col) => (
<SelectItem key={col.value} value={col.value}>
{col.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 층 필드 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={fieldMapping.floorField || "__none__"}
onValueChange={(v) => handleFieldMappingChange("floorField", v)}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="필드 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"> </SelectItem>
{availableColumns.map((col) => (
<SelectItem key={col.value} value={col.value}>
{col.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 구역 필드 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={fieldMapping.zoneField || "__none__"}
onValueChange={(v) => handleFieldMappingChange("zoneField", v)}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="필드 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"> </SelectItem>
{availableColumns.map((col) => (
<SelectItem key={col.value} value={col.value}>
{col.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 위치 유형 필드 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={fieldMapping.locationTypeField || "__none__"}
onValueChange={(v) => handleFieldMappingChange("locationTypeField", v)}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="필드 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"> </SelectItem>
{availableColumns.map((col) => (
<SelectItem key={col.value} value={col.value}>
{col.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* 사용 여부 필드 */}
<div>
<Label className="text-xs"> </Label>
<Select
value={fieldMapping.statusField || "__none__"}
onValueChange={(v) => handleFieldMappingChange("statusField", v)}
>
<SelectTrigger className="h-8">
<SelectValue placeholder="필드 선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"> </SelectItem>
{availableColumns.map((col) => (
<SelectItem key={col.value} value={col.value}>
{col.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* 위치코드 패턴 설정 */}
<div className="space-y-3 border-t pt-3">
<div className="text-sm font-medium text-foreground">/ </div>
<p className="text-xs text-muted-foreground">
</p>
{/* 위치코드 패턴 */}
<div>
<Label className="text-xs"> </Label>
<Input
value={config.codePattern || ""}
onChange={(e) => handleChange("codePattern", e.target.value || undefined)}
placeholder="{warehouse}-{floor}{zone}-{row:02}-{level}"
className="h-8 font-mono text-xs"
/>
<p className="mt-1 text-[10px] text-muted-foreground">
: {"{warehouse}-{floor}{zone}-{row:02}-{level}"}
</p>
</div>
{/* 위치명 패턴 */}
<div>
<Label className="text-xs"> </Label>
<Input
value={config.namePattern || ""}
onChange={(e) => handleChange("namePattern", e.target.value || undefined)}
placeholder="{zone}-{row:02}열-{level}단"
className="h-8 font-mono text-xs"
/>
<p className="mt-1 text-[10px] text-muted-foreground">
: {"{zone}-{row:02}열-{level}단"}
</p>
</div>
{/* 실시간 미리보기 */}
<PatternPreview
codePattern={config.codePattern}
namePattern={config.namePattern}
/>
{/* 사용 가능한 변수 목록 */}
<div className="rounded-md border bg-muted/50 p-2">
<div className="mb-1 text-[10px] font-medium text-foreground"> </div>
<div className="grid grid-cols-2 gap-x-3 gap-y-0.5">
{PATTERN_VARIABLES.map((v) => (
<div key={v.token} className="flex items-center gap-1 text-[10px]">
<code className="rounded bg-primary/10 px-1 font-mono text-primary">{v.token}</code>
<span className="text-muted-foreground">{v.description}</span>
</div>
))}
</div>
</div>
</div>
{/* 제한 설정 */}
<div className="space-y-3 border-t pt-3">
<div className="text-sm font-medium text-foreground"> </div>
<div>
<Label className="text-xs"> </Label>
<Input
type="number"
min={1}
max={20}
value={config.maxConditions || 10}
onChange={(e) => handleChange("maxConditions", parseInt(e.target.value) || 10)}
className="h-8"
/>
</div>
<div>
<Label className="text-xs"> </Label>
<Input
type="number"
min={1}
max={999}
value={config.maxRows || 99}
onChange={(e) => handleChange("maxRows", parseInt(e.target.value) || 99)}
className="h-8"
/>
</div>
<div>
<Label className="text-xs"> </Label>
<Input
type="number"
min={1}
max={99}
value={config.maxLevels || 20}
onChange={(e) => handleChange("maxLevels", parseInt(e.target.value) || 20)}
className="h-8"
/>
</div>
</div>
{/* UI 설정 */}
<div className="space-y-3 border-t pt-3">
<div className="text-sm font-medium text-foreground">UI </div>
<div className="flex items-center justify-between">
<Label className="text-xs">릿 </Label>
<Switch
checked={config.showTemplates ?? true}
onCheckedChange={(checked) => handleChange("showTemplates", checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label className="text-xs"> </Label>
<Switch
checked={config.showPreview ?? true}
onCheckedChange={(checked) => handleChange("showPreview", checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label className="text-xs"> </Label>
<Switch
checked={config.showStatistics ?? true}
onCheckedChange={(checked) => handleChange("showStatistics", checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label className="text-xs"> </Label>
<Switch
checked={config.readonly ?? false}
onCheckedChange={(checked) => handleChange("readonly", checked)}
/>
</div>
</div>
</div>
);
};