fix: 컴포넌트 등록 및 entity-search 검증 개선
- 컴포넌트 등록을 useEffect → 즉시 실행으로 변경 (3개 컴포넌트) - entity-search-input tableName 검증 추가 (FE/BE) - DynamicComponentRenderer에서 componentConfig props 전달 수정 - EntitySearchModal key prop 경고 해결 - 불필요한 ScreenDesigner 렌더링 코드 제거 Fixes: 컴포넌트 미등록 에러, tableName undefined 500 에러, React key 경고
This commit is contained in:
parent
6839deac97
commit
23cd677413
|
|
@ -17,6 +17,15 @@ export async function searchEntity(req: Request, res: Response) {
|
|||
limit = "20",
|
||||
} = req.query;
|
||||
|
||||
// tableName 유효성 검증
|
||||
if (!tableName || tableName === "undefined" || tableName === "null") {
|
||||
logger.warn("엔티티 검색 실패: 테이블명이 없음", { tableName });
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "테이블명이 지정되지 않았습니다. 컴포넌트 설정에서 sourceTable을 확인해주세요.",
|
||||
});
|
||||
}
|
||||
|
||||
// 멀티테넌시
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
|
|
|
|||
|
|
@ -70,9 +70,6 @@ import { toast } from "sonner";
|
|||
import { MenuAssignmentModal } from "./MenuAssignmentModal";
|
||||
import { FileAttachmentDetailModal } from "./FileAttachmentDetailModal";
|
||||
import { initializeComponents } from "@/lib/registry/components";
|
||||
import { AutocompleteSearchInputRenderer } from "@/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputRenderer";
|
||||
import { EntitySearchInputRenderer } from "@/lib/registry/components/entity-search-input/EntitySearchInputRenderer";
|
||||
import { ModalRepeaterTableRenderer } from "@/lib/registry/components/modal-repeater-table/ModalRepeaterTableRenderer";
|
||||
import { ScreenFileAPI } from "@/lib/api/screenFile";
|
||||
import { safeMigrateLayout, needsMigration } from "@/lib/utils/widthToColumnSpan";
|
||||
|
||||
|
|
@ -4967,12 +4964,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
|||
)}
|
||||
</div>
|
||||
</TableOptionsProvider>
|
||||
{/* 숨겨진 컴포넌트 렌더러들 (레지스트리 등록용) */}
|
||||
<div style={{ display: "none" }}>
|
||||
<AutocompleteSearchInputRenderer />
|
||||
<EntitySearchInputRenderer />
|
||||
<ModalRepeaterTableRenderer />
|
||||
</div>
|
||||
</ScreenPreviewProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,14 +79,14 @@ export const CategoryValueAddDialog: React.FC<
|
|||
const valueCode = generateCode(valueLabel);
|
||||
|
||||
onAdd({
|
||||
tableName: "",
|
||||
columnName: "",
|
||||
tableName: "", // CategoryValueManager에서 오버라이드됨
|
||||
columnName: "", // CategoryValueManager에서 오버라이드됨
|
||||
valueCode,
|
||||
valueLabel: valueLabel.trim(),
|
||||
description: description.trim(),
|
||||
color: color,
|
||||
description: description.trim() || undefined, // 빈 문자열 대신 undefined
|
||||
color: color === "none" ? undefined : color, // "none"은 undefined로
|
||||
isDefault: false,
|
||||
});
|
||||
} as TableCategoryValue);
|
||||
|
||||
// 초기화
|
||||
setValueLabel("");
|
||||
|
|
|
|||
|
|
@ -308,6 +308,8 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
|||
style: finalStyle, // size를 포함한 최종 style
|
||||
config: component.componentConfig,
|
||||
componentConfig: component.componentConfig,
|
||||
// componentConfig의 모든 속성을 props로 spread (tableName, displayField 등)
|
||||
...(component.componentConfig || {}),
|
||||
value: currentValue, // formData에서 추출한 현재 값 전달
|
||||
// 새로운 기능들 전달
|
||||
autoGeneration: component.autoGeneration || component.componentConfig?.autoGeneration,
|
||||
|
|
|
|||
|
|
@ -1,19 +1,9 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { ComponentRegistry } from "../../ComponentRegistry";
|
||||
import { AutocompleteSearchInputDefinition } from "./index";
|
||||
|
||||
export function AutocompleteSearchInputRenderer() {
|
||||
useEffect(() => {
|
||||
// 파일 로드 시 즉시 등록
|
||||
ComponentRegistry.registerComponent(AutocompleteSearchInputDefinition);
|
||||
console.log("✅ AutocompleteSearchInput 컴포넌트 등록 완료");
|
||||
|
||||
return () => {
|
||||
// 컴포넌트 언마운트 시 해제하지 않음 (싱글톤 패턴)
|
||||
};
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,9 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { ComponentRegistry } from "../../ComponentRegistry";
|
||||
import { EntitySearchInputDefinition } from "./index";
|
||||
|
||||
export function EntitySearchInputRenderer() {
|
||||
useEffect(() => {
|
||||
// 파일 로드 시 즉시 등록
|
||||
ComponentRegistry.registerComponent(EntitySearchInputDefinition);
|
||||
console.log("✅ EntitySearchInput 컴포넌트 등록 완료");
|
||||
|
||||
return () => {
|
||||
// 컴포넌트 언마운트 시 해제하지 않음 (싱글톤 패턴)
|
||||
};
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -155,14 +155,16 @@ export function EntitySearchModal({
|
|||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
results.map((item, index) => (
|
||||
results.map((item, index) => {
|
||||
const uniqueKey = item[valueField] !== undefined ? `${item[valueField]}` : `row-${index}`;
|
||||
return (
|
||||
<tr
|
||||
key={item[valueField] || index}
|
||||
key={uniqueKey}
|
||||
className="border-t hover:bg-accent cursor-pointer transition-colors"
|
||||
onClick={() => handleSelect(item)}
|
||||
>
|
||||
{displayColumns.map((col, colIndex) => (
|
||||
<td key={`${item[valueField] || index}-${col}-${colIndex}`} className="px-4 py-2">
|
||||
{displayColumns.map((col) => (
|
||||
<td key={`${uniqueKey}-${col}`} className="px-4 py-2">
|
||||
{item[col] || "-"}
|
||||
</td>
|
||||
))}
|
||||
|
|
@ -180,7 +182,8 @@ export function EntitySearchModal({
|
|||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,13 @@ export function useEntitySearch({
|
|||
|
||||
const search = useCallback(
|
||||
async (text: string, page: number = 1) => {
|
||||
// tableName 유효성 검증
|
||||
if (!tableName || tableName === "undefined" || tableName === "null") {
|
||||
console.warn("엔티티 검색 건너뜀: tableName이 없음", { tableName });
|
||||
setError("테이블명이 설정되지 않았습니다. 컴포넌트 설정을 확인해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
|
|
|||
|
|
@ -46,9 +46,9 @@ import "./table-search-widget"; // 🆕 테이블 검색 필터 위젯
|
|||
import "./customer-item-mapping/CustomerItemMappingRenderer"; // 🆕 거래처별 품목정보
|
||||
|
||||
// 🆕 수주 등록 관련 컴포넌트들
|
||||
import { AutocompleteSearchInputRenderer } from "./autocomplete-search-input/AutocompleteSearchInputRenderer";
|
||||
import { EntitySearchInputRenderer } from "./entity-search-input/EntitySearchInputRenderer";
|
||||
import { ModalRepeaterTableRenderer } from "./modal-repeater-table/ModalRepeaterTableRenderer";
|
||||
import "./autocomplete-search-input/AutocompleteSearchInputRenderer";
|
||||
import "./entity-search-input/EntitySearchInputRenderer";
|
||||
import "./modal-repeater-table/ModalRepeaterTableRenderer";
|
||||
import "./order-registration-modal/OrderRegistrationModalRenderer";
|
||||
|
||||
// 🆕 조건부 컨테이너 컴포넌트
|
||||
|
|
|
|||
|
|
@ -1,19 +1,9 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { ComponentRegistry } from "../../ComponentRegistry";
|
||||
import { ModalRepeaterTableDefinition } from "./index";
|
||||
|
||||
export function ModalRepeaterTableRenderer() {
|
||||
useEffect(() => {
|
||||
// 파일 로드 시 즉시 등록
|
||||
ComponentRegistry.registerComponent(ModalRepeaterTableDefinition);
|
||||
console.log("✅ ModalRepeaterTable 컴포넌트 등록 완료");
|
||||
|
||||
return () => {
|
||||
// 컴포넌트 언마운트 시 해제하지 않음 (싱글톤 패턴)
|
||||
};
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue