feat: update shipping-plan page and FieldDetailSettingsModal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kmh 2026-03-25 14:36:57 +09:00
parent 48af85c713
commit e67e43cd7d
2 changed files with 151 additions and 9 deletions

View File

@ -315,15 +315,15 @@ export default function ShippingPlanPage() {
onCheckedChange={handleCheckAll}
/>
</TableHead>
<TableHead className="w-[160px]"></TableHead>
<TableHead className="w-[100px] text-center"></TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[100px]"></TableHead>
<TableHead></TableHead>
<TableHead className="w-[80px] text-right"></TableHead>
<TableHead className="w-[80px] text-right"></TableHead>
<TableHead className="w-[100px] text-center"></TableHead>
<TableHead className="w-[80px] text-center"></TableHead>
<TableHead className="w-[10%]"></TableHead>
<TableHead className="w-[8%] text-center"></TableHead>
<TableHead className="w-[12%]"></TableHead>
<TableHead className="w-[20%]"></TableHead>
<TableHead className="w-[20%]"></TableHead>
<TableHead className="w-[7%] text-right"></TableHead>
<TableHead className="w-[7%] text-right"></TableHead>
<TableHead className="w-[8%] text-center"></TableHead>
<TableHead className="w-[6%] text-center"></TableHead>
</TableRow>
</TableHeader>
<TableBody>

View File

@ -109,6 +109,11 @@ export function FieldDetailSettingsModal({
const [cascadingRelationOpen, setCascadingRelationOpen] = useState(false);
const [parentFieldOpen, setParentFieldOpen] = useState(false);
// 기본 선택값용 옵션 목록 상태
const [defaultValueCategoryValues, setDefaultValueCategoryValues] = useState<{value: string; label: string}[]>([]);
const [defaultValueTableOptions, setDefaultValueTableOptions] = useState<{value: string; label: string}[]>([]);
const [loadingDefaultValueOptions, setLoadingDefaultValueOptions] = useState(false);
// Combobox 열림 상태
const [sourceTableOpen, setSourceTableOpen] = useState(false);
const [targetColumnOpenMap, setTargetColumnOpenMap] = useState<Record<number, boolean>>({});
@ -209,6 +214,69 @@ export function FieldDetailSettingsModal({
loadCascadingRelations();
}, [open]);
// 기본 선택값용: code 타입 카테고리 값 로드
useEffect(() => {
const loadCategoryValues = async () => {
const categoryKey = localField.selectOptions?.categoryKey;
if (!open || localField.selectOptions?.type !== "code" || !categoryKey) {
setDefaultValueCategoryValues([]);
return;
}
setLoadingDefaultValueOptions(true);
try {
const [tableName, columnName] = categoryKey.split(".");
const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values`);
if (response.data?.success && response.data?.data) {
setDefaultValueCategoryValues(
response.data.data.map((item: any) => ({
value: item.valueCode || item.value_code,
label: item.valueLabel || item.value_label,
}))
);
} else {
setDefaultValueCategoryValues([]);
}
} catch {
setDefaultValueCategoryValues([]);
} finally {
setLoadingDefaultValueOptions(false);
}
};
loadCategoryValues();
}, [open, localField.selectOptions?.type, localField.selectOptions?.categoryKey]);
// 기본 선택값용: table 타입 옵션 로드
useEffect(() => {
const loadTableOptions = async () => {
const opts = localField.selectOptions;
if (!open || opts?.type !== "table" || !opts?.tableName || !opts?.valueColumn || !opts?.labelColumn) {
setDefaultValueTableOptions([]);
return;
}
setLoadingDefaultValueOptions(true);
try {
const response = await apiClient.post(`/table-management/tables/${opts.tableName}/data`, {
page: 1,
size: 200,
autoFilter: { enabled: true, filterColumn: "company_code" },
});
const dataArray = response.data?.data?.data || response.data?.data || [];
setDefaultValueTableOptions(
dataArray.map((row: any) => ({
value: String(row[opts.valueColumn!] || ""),
label: String(row[opts.labelColumn!] || ""),
}))
);
} catch {
setDefaultValueTableOptions([]);
} finally {
setLoadingDefaultValueOptions(false);
}
};
loadTableOptions();
}, [open, localField.selectOptions?.type, localField.selectOptions?.tableName,
localField.selectOptions?.valueColumn, localField.selectOptions?.labelColumn]);
// 관계 코드 선택 시 상세 설정 자동 채움
const handleRelationCodeSelect = async (relationCode: string) => {
if (!relationCode) return;
@ -1181,6 +1249,80 @@ export function FieldDetailSettingsModal({
</div>
</div>
)}
{/* 기본 선택값 설정 (cascading 제외) */}
{(() => {
const effectiveType = localField.selectOptions?.type || "static";
if (effectiveType === "cascading") return null;
return (
<div className="border-t pt-3 mt-3">
<div className="flex items-center justify-between">
<span className="text-[10px] font-medium"> </span>
{/* static 타입 */}
{effectiveType === "static" && (localField.selectOptions?.staticOptions?.length || 0) > 0 && (
<Select
value={localField.defaultValue || "_none_"}
onValueChange={(value) => updateField({ defaultValue: value === "_none_" ? "" : value })}
>
<SelectTrigger className="h-7 w-[180px] text-xs">
<SelectValue placeholder="선택 안함" />
</SelectTrigger>
<SelectContent>
<SelectItem value="_none_"> </SelectItem>
{(localField.selectOptions?.staticOptions || []).map((opt, idx) => (
<SelectItem key={`default-${idx}`} value={opt.value}>
{opt.label || opt.value}
</SelectItem>
))}
</SelectContent>
</Select>
)}
{/* code 타입 */}
{effectiveType === "code" && defaultValueCategoryValues.length > 0 && (
<Select
value={localField.defaultValue || "_none_"}
onValueChange={(value) => updateField({ defaultValue: value === "_none_" ? "" : value })}
>
<SelectTrigger className="h-7 w-[180px] text-xs">
<SelectValue placeholder="선택 안함" />
</SelectTrigger>
<SelectContent>
<SelectItem value="_none_"> </SelectItem>
{defaultValueCategoryValues.map((cv) => (
<SelectItem key={cv.value} value={cv.value}>
{cv.label}
</SelectItem>
))}
</SelectContent>
</Select>
)}
{/* table 타입 */}
{effectiveType === "table" && defaultValueTableOptions.length > 0 && (
<Select
value={localField.defaultValue || "_none_"}
onValueChange={(value) => updateField({ defaultValue: value === "_none_" ? "" : value })}
>
<SelectTrigger className="h-7 w-[180px] text-xs">
<SelectValue placeholder="선택 안함" />
</SelectTrigger>
<SelectContent>
<SelectItem value="_none_"> </SelectItem>
{defaultValueTableOptions.map((opt, idx) => (
<SelectItem key={`default-table-${idx}`} value={opt.value}>
{opt.label} ({opt.value})
</SelectItem>
))}
</SelectContent>
</Select>
)}
{loadingDefaultValueOptions && (
<span className="text-[9px] text-muted-foreground"> ...</span>
)}
</div>
<HelpText> </HelpText>
</div>
);
})()}
</AccordionContent>
</AccordionItem>
)}