feat: Enhance entity options retrieval with additional fields support
- Updated the `getEntityOptions` function to accept an optional `fields` parameter, allowing clients to specify additional columns to be retrieved. - Implemented logic to dynamically include extra columns in the SQL query based on the provided `fields`, improving flexibility in data retrieval. - Enhanced the response to indicate whether extra fields were included, facilitating better client-side handling of the data. - Added logging for authentication failures in the `AuthGuard` component to improve debugging and user experience. - Integrated auto-fill functionality in the `V2Select` component to automatically populate fields based on selected entity references, enhancing user interaction. - Updated the `ItemSearchModal` to support multi-selection of items, improving usability in item management scenarios.
This commit is contained in:
@@ -622,6 +622,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
config: configProp,
|
||||
value,
|
||||
onChange,
|
||||
onFormDataChange,
|
||||
tableName,
|
||||
columnName,
|
||||
isDesignMode, // 🔧 디자인 모드 (클릭 방지)
|
||||
@@ -630,6 +631,9 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
// config가 없으면 기본값 사용
|
||||
const config = configProp || { mode: "dropdown" as const, source: "static" as const, options: [] };
|
||||
|
||||
// 엔티티 자동 채움: 같은 폼의 다른 컴포넌트 중 참조 테이블 컬럼을 자동 감지
|
||||
const allComponents = (props as any).allComponents as any[] | undefined;
|
||||
|
||||
const [options, setOptions] = useState<SelectOption[]>(config.options || []);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [optionsLoaded, setOptionsLoaded] = useState(false);
|
||||
@@ -742,10 +746,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
const valueCol = entityValueColumn || "id";
|
||||
const labelCol = entityLabelColumn || "name";
|
||||
const response = await apiClient.get(`/entity/${entityTable}/options`, {
|
||||
params: {
|
||||
value: valueCol,
|
||||
label: labelCol,
|
||||
},
|
||||
params: { value: valueCol, label: labelCol },
|
||||
});
|
||||
const data = response.data;
|
||||
if (data.success && data.data) {
|
||||
@@ -819,6 +820,70 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
loadOptions();
|
||||
}, [source, entityTable, entityValueColumn, entityLabelColumn, codeGroup, table, valueColumn, labelColumn, apiEndpoint, staticOptions, optionsLoaded, hierarchical, parentValue]);
|
||||
|
||||
// 같은 폼에서 참조 테이블(entityTable) 컬럼을 사용하는 다른 컴포넌트 자동 감지
|
||||
const autoFillTargets = useMemo(() => {
|
||||
if (source !== "entity" || !entityTable || !allComponents) return [];
|
||||
|
||||
const targets: Array<{ sourceField: string; targetColumnName: string }> = [];
|
||||
for (const comp of allComponents) {
|
||||
if (comp.id === id) continue;
|
||||
|
||||
// overrides 구조 지원 (DB에서 로드 시 overrides 안에 데이터가 있음)
|
||||
const ov = (comp as any).overrides || {};
|
||||
const compColumnName = comp.columnName || ov.columnName || comp.componentConfig?.columnName || "";
|
||||
|
||||
// 방법1: entityJoinTable 속성이 있는 경우
|
||||
const joinTable = comp.entityJoinTable || ov.entityJoinTable || comp.componentConfig?.entityJoinTable;
|
||||
const joinColumn = comp.entityJoinColumn || ov.entityJoinColumn || comp.componentConfig?.entityJoinColumn;
|
||||
if (joinTable === entityTable && joinColumn) {
|
||||
targets.push({ sourceField: joinColumn, targetColumnName: compColumnName });
|
||||
continue;
|
||||
}
|
||||
|
||||
// 방법2: columnName이 "테이블명.컬럼명" 형식인 경우 (예: item_info.unit)
|
||||
if (compColumnName.includes(".")) {
|
||||
const [prefix, actualColumn] = compColumnName.split(".");
|
||||
if (prefix === entityTable && actualColumn) {
|
||||
targets.push({ sourceField: actualColumn, targetColumnName: compColumnName });
|
||||
}
|
||||
}
|
||||
}
|
||||
return targets;
|
||||
}, [source, entityTable, allComponents, id]);
|
||||
|
||||
// 엔티티 autoFill 적용 래퍼
|
||||
const handleChangeWithAutoFill = useCallback((newValue: string | string[]) => {
|
||||
onChange?.(newValue);
|
||||
|
||||
if (autoFillTargets.length === 0 || !onFormDataChange || !entityTable) return;
|
||||
|
||||
const selectedKey = typeof newValue === "string" ? newValue : newValue[0];
|
||||
if (!selectedKey) return;
|
||||
|
||||
const valueCol = entityValueColumn || "id";
|
||||
|
||||
apiClient.get(`/table-management/tables/${entityTable}/data-with-joins`, {
|
||||
params: {
|
||||
page: 1,
|
||||
size: 1,
|
||||
search: JSON.stringify({ [valueCol]: selectedKey }),
|
||||
autoFilter: JSON.stringify({ enabled: true, filterColumn: "company_code", userField: "companyCode" }),
|
||||
},
|
||||
}).then((res) => {
|
||||
const responseData = res.data?.data;
|
||||
const rows = responseData?.data || responseData?.rows || [];
|
||||
if (rows.length > 0) {
|
||||
const fullData = rows[0];
|
||||
for (const target of autoFillTargets) {
|
||||
const sourceValue = fullData[target.sourceField];
|
||||
if (sourceValue !== undefined) {
|
||||
onFormDataChange(target.targetColumnName, sourceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).catch((err) => console.error("autoFill 조회 실패:", err));
|
||||
}, [onChange, autoFillTargets, onFormDataChange, entityTable, entityValueColumn]);
|
||||
|
||||
// 모드별 컴포넌트 렌더링
|
||||
const renderSelect = () => {
|
||||
if (loading) {
|
||||
@@ -876,12 +941,12 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
|
||||
switch (config.mode) {
|
||||
case "dropdown":
|
||||
case "combobox": // 🔧 콤보박스는 검색 가능한 드롭다운
|
||||
case "combobox":
|
||||
return (
|
||||
<DropdownSelect
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onChange={handleChangeWithAutoFill}
|
||||
placeholder="선택"
|
||||
searchable={config.mode === "combobox" ? true : config.searchable}
|
||||
multiple={config.multiple}
|
||||
@@ -897,18 +962,18 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
<RadioSelect
|
||||
options={options}
|
||||
value={typeof value === "string" ? value : value?.[0]}
|
||||
onChange={(v) => onChange?.(v)}
|
||||
onChange={(v) => handleChangeWithAutoFill(v)}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
|
||||
case "check":
|
||||
case "checkbox": // 🔧 기존 저장된 값 호환
|
||||
case "checkbox":
|
||||
return (
|
||||
<CheckSelect
|
||||
options={options}
|
||||
value={Array.isArray(value) ? value : value ? [value] : []}
|
||||
onChange={onChange}
|
||||
onChange={handleChangeWithAutoFill}
|
||||
maxSelect={config.maxSelect}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
@@ -919,7 +984,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
<TagSelect
|
||||
options={options}
|
||||
value={Array.isArray(value) ? value : value ? [value] : []}
|
||||
onChange={onChange}
|
||||
onChange={handleChangeWithAutoFill}
|
||||
maxSelect={config.maxSelect}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
@@ -930,7 +995,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
<TagboxSelect
|
||||
options={options}
|
||||
value={Array.isArray(value) ? value : value ? [value] : []}
|
||||
onChange={onChange}
|
||||
onChange={handleChangeWithAutoFill}
|
||||
placeholder={config.placeholder || "선택하세요"}
|
||||
maxSelect={config.maxSelect}
|
||||
disabled={isDisabled}
|
||||
@@ -943,7 +1008,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
<ToggleSelect
|
||||
options={options}
|
||||
value={typeof value === "string" ? value : value?.[0]}
|
||||
onChange={(v) => onChange?.(v)}
|
||||
onChange={(v) => handleChangeWithAutoFill(v)}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
);
|
||||
@@ -953,7 +1018,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
<SwapSelect
|
||||
options={options}
|
||||
value={Array.isArray(value) ? value : value ? [value] : []}
|
||||
onChange={onChange}
|
||||
onChange={handleChangeWithAutoFill}
|
||||
maxSelect={config.maxSelect}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
@@ -964,7 +1029,7 @@ export const V2Select = forwardRef<HTMLDivElement, V2SelectProps>(
|
||||
<DropdownSelect
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onChange={handleChangeWithAutoFill}
|
||||
disabled={isDisabled}
|
||||
style={heightStyle}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user