diff --git a/frontend/components/report/designer/ReportPreviewModal.tsx b/frontend/components/report/designer/ReportPreviewModal.tsx index e5fb16f7..e7575128 100644 --- a/frontend/components/report/designer/ReportPreviewModal.tsx +++ b/frontend/components/report/designer/ReportPreviewModal.tsx @@ -244,9 +244,9 @@ function BarcodePreview({ component.qrUseMultiField && component.qrDataFields && component.qrDataFields.length > 0 && - component.queryId + component.query_id ) { - const queryResult = getQueryResult(component.queryId); + const queryResult = getQueryResult(component.query_id); if (queryResult && queryResult.rows && queryResult.rows.length > 0) { if (component.qrIncludeAllRows) { const allRowsData: Record[] = []; @@ -275,8 +275,8 @@ function BarcodePreview({ } // 단일 필드 바인딩 - if (component.barcodeFieldName && component.queryId) { - const queryResult = getQueryResult(component.queryId); + if (component.barcodeFieldName && component.query_id) { + const queryResult = getQueryResult(component.query_id); if (queryResult && queryResult.rows && queryResult.rows.length > 0) { if (isQR && component.qrIncludeAllRows) { const allValues = queryResult.rows @@ -367,14 +367,14 @@ function BarcodePreview({ } export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) { - const { layoutConfig, getQueryResult, reportDetail } = useReportDesigner(); + const { layout_config: layoutConfig, getQueryResult, report_detail: reportDetail } = useReportDesigner(); const [isExporting, setIsExporting] = useState(false); const { toast } = useToast(); // 컴포넌트의 실제 표시 값 가져오기 const getComponentValue = (component: any): string => { - if (component.queryId && component.fieldName) { - const queryResult = getQueryResult(component.queryId); + if (component.query_id && component.fieldName) { + const queryResult = getQueryResult(component.query_id); if (queryResult && queryResult.rows.length > 0) { const value = queryResult.rows[0][component.fieldName]; if (value !== null && value !== undefined) { @@ -398,9 +398,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) component.qrUseMultiField && component.qrDataFields && component.qrDataFields.length > 0 && - component.queryId + component.query_id ) { - const queryResult = getQueryResult(component.queryId); + const queryResult = getQueryResult(component.query_id); if (queryResult && queryResult.rows && queryResult.rows.length > 0) { if (component.qrIncludeAllRows) { const allRowsData: Record[] = []; @@ -428,8 +428,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) } } - if (component.barcodeFieldName && component.queryId) { - const queryResult = getQueryResult(component.queryId); + if (component.barcodeFieldName && component.query_id) { + const queryResult = getQueryResult(component.query_id); if (queryResult && queryResult.rows && queryResult.rows.length > 0) { if (isQR && component.qrIncludeAllRows) { const allValues = queryResult.rows @@ -586,39 +586,39 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) ): string => { const componentsHTML = pageComponents .map((component) => { - const queryResult = component.queryId ? getQueryResult(component.queryId) : null; + const queryResult = component.query_id ? getQueryResult(component.query_id) : null; let content = ""; // Text/Label 컴포넌트 if (component.type === "text" || component.type === "label") { const displayValue = getComponentValue(component); - content = `
${displayValue}
`; + content = `
${displayValue}
`; } // Image 컴포넌트 - else if (component.type === "image" && component.imageUrl) { - const imageUrl = component.imageUrl.startsWith("data:") - ? component.imageUrl - : getFullImageUrl(component.imageUrl); - content = ``; + else if (component.type === "image" && component.image_url) { + const imageUrl = component.image_url.startsWith("data:") + ? component.image_url + : getFullImageUrl(component.image_url); + content = ``; } // Divider 컴포넌트 else if (component.type === "divider") { - const width = component.orientation === "horizontal" ? "100%" : `${component.lineWidth || 1}px`; - const height = component.orientation === "vertical" ? "100%" : `${component.lineWidth || 1}px`; - content = `
`; + const width = component.orientation === "horizontal" ? "100%" : `${component.line_width || 1}px`; + const height = component.orientation === "vertical" ? "100%" : `${component.line_width || 1}px`; + content = `
`; } // Signature 컴포넌트 else if (component.type === "signature") { - const labelPosition = component.labelPosition || "left"; - const showLabel = component.showLabel !== false; - const labelText = component.labelText || "서명:"; - const imageUrl = component.imageUrl - ? component.imageUrl.startsWith("data:") - ? component.imageUrl - : getFullImageUrl(component.imageUrl) + const labelPosition = component.label_position || "left"; + const showLabel = component.show_label !== false; + const labelText = component.label_text || "서명:"; + const imageUrl = component.image_url + ? component.image_url.startsWith("data:") + ? component.image_url + : getFullImageUrl(component.image_url) : ""; if (labelPosition === "left" || labelPosition === "right") { @@ -626,7 +626,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
${showLabel ? `
${labelText}
` : ""}
- ${imageUrl ? `` : ""} + ${imageUrl ? `` : ""}
`; } else { @@ -634,7 +634,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
${showLabel && labelPosition === "top" ? `
${labelText}
` : ""}
- ${imageUrl ? `` : ""} + ${imageUrl ? `` : ""}
${showLabel && labelPosition === "bottom" ? `
${labelText}
` : ""}
`; @@ -643,20 +643,20 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) // Stamp 컴포넌트 else if (component.type === "stamp") { - const showLabel = component.showLabel !== false; - const labelText = component.labelText || "(인)"; - const personName = component.personName || ""; - const imageUrl = component.imageUrl - ? component.imageUrl.startsWith("data:") - ? component.imageUrl - : getFullImageUrl(component.imageUrl) + const showLabel = component.show_label !== false; + const labelText = component.label_text || "(인)"; + const personName = component.person_name || ""; + const imageUrl = component.image_url + ? component.image_url.startsWith("data:") + ? component.image_url + : getFullImageUrl(component.image_url) : ""; content = `
${personName ? `
${personName}
` : ""}
- ${imageUrl ? `` : ""} + ${imageUrl ? `` : ""} ${showLabel ? `
${labelText}
` : ""}
`; @@ -664,7 +664,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) // PageNumber 컴포넌트 else if (component.type === "pageNumber") { - const format = component.pageNumberFormat || "number"; + const format = component.page_number_format || "number"; let pageNumberText = ""; switch (format) { case "number": @@ -679,22 +679,22 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) default: pageNumberText = `${pageIndex + 1}`; } - content = `
${pageNumberText}
`; + content = `
${pageNumberText}
`; } // Card 컴포넌트 else if (component.type === "card") { - const cardTitle = component.cardTitle || "정보 카드"; - const cardItems = component.cardItems || []; - const labelWidth = component.labelWidth || 80; - const showCardTitle = component.showCardTitle !== false; - const titleFontSize = component.titleFontSize || 14; - const labelFontSize = component.labelFontSize || 13; - const valueFontSize = component.valueFontSize || 13; - const titleColor = component.titleColor || "#1e40af"; - const labelColor = component.labelColor || "#374151"; - const valueColor = component.valueColor || "#000000"; - const borderColor = component.borderColor || "#e5e7eb"; + const cardTitle = component.card_title || "정보 카드"; + const cardItems = component.card_items || []; + const labelWidth = component.label_width || 80; + const showCardTitle = component.show_card_title !== false; + const titleFontSize = component.title_font_size || 14; + const labelFontSize = component.label_font_size || 13; + const valueFontSize = component.value_font_size || 13; + const titleColor = component.title_color || "#1e40af"; + const labelColor = component.label_color || "#374151"; + const valueColor = component.value_color || "#000000"; + const borderColor = component.border_color || "#e5e7eb"; // 쿼리 바인딩된 값 가져오기 const getCardValue = (item: { label: string; value: string; fieldName?: string }) => { @@ -736,18 +736,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) // 계산 컴포넌트 else if (component.type === "calculation") { - const calcItems = component.calcItems || []; - const resultLabel = component.resultLabel || "합계"; - const calcLabelWidth = component.labelWidth || 120; - const calcLabelFontSize = component.labelFontSize || 13; - const calcValueFontSize = component.valueFontSize || 13; - const calcResultFontSize = component.resultFontSize || 16; - const calcLabelColor = component.labelColor || "#374151"; - const calcValueColor = component.valueColor || "#000000"; - const calcResultColor = component.resultColor || "#2563eb"; - const numberFormat = component.numberFormat || "currency"; - const currencySuffix = component.currencySuffix || "원"; - const borderColor = component.borderColor || "#374151"; + const calcItems = component.calc_items || []; + const resultLabel = component.result_label || "합계"; + const calcLabelWidth = component.label_width || 120; + const calcLabelFontSize = component.label_font_size || 13; + const calcValueFontSize = component.value_font_size || 13; + const calcResultFontSize = component.result_font_size || 16; + const calcLabelColor = component.label_color || "#374151"; + const calcValueColor = component.value_color || "#000000"; + const calcResultColor = component.result_color || "#2563eb"; + const numberFormat = component.number_format || "currency"; + const currencySuffix = component.currency_suffix || "원"; + const borderColor = component.border_color || "#374151"; // 숫자 포맷팅 함수 const formatNumber = (num: number): string => { @@ -832,16 +832,16 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) // 체크박스 컴포넌트 (인쇄용) else if (component.type === "checkbox") { - const checkboxSize = component.checkboxSize || 18; - const checkboxColor = component.checkboxColor || "#2563eb"; - const checkboxBorderColor = component.checkboxBorderColor || "#6b7280"; - const checkboxLabel = component.checkboxLabel || ""; - const checkboxLabelPosition = component.checkboxLabelPosition || "right"; + const checkboxSize = component.checkbox_size || 18; + const checkboxColor = component.checkbox_color || "#2563eb"; + const checkboxBorderColor = component.checkbox_border_color || "#6b7280"; + const checkboxLabel = component.checkbox_label || ""; + const checkboxLabelPosition = component.checkbox_label_position || "right"; // 체크 상태 결정 - let isChecked = component.checkboxChecked === true; - if (component.checkboxFieldName && queryResult && queryResult.rows && queryResult.rows.length > 0) { - const val = queryResult.rows[0][component.checkboxFieldName]; + let isChecked = component.checkbox_checked === true; + if (component.checkbox_field_name && queryResult && queryResult.rows && queryResult.rows.length > 0) { + const val = queryResult.rows[0][component.checkbox_field_name]; isChecked = val === true || val === "Y" || val === "1" || val === 1 || val === "true"; } @@ -862,8 +862,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) // Table 컴포넌트 else if (component.type === "table" && queryResult && queryResult.rows.length > 0) { const columns = - component.tableColumns && component.tableColumns.length > 0 - ? component.tableColumns + component.table_columns && component.table_columns.length > 0 + ? component.table_columns : queryResult.fields.map((field) => ({ field, header: field, @@ -875,17 +875,17 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) .map( (row) => ` - ${columns.map((col: { field: string; align?: string }) => `${String(row[col.field] ?? "")}`).join("")} + ${columns.map((col: { field: string; align?: string }) => `${String(row[col.field] ?? "")}`).join("")} `, ) .join(""); content = ` - +
- - ${columns.map((col: { header: string; align?: string; width?: number }) => ``).join("")} + + ${columns.map((col: { header: string; align?: string; width?: number }) => ``).join("")} @@ -902,7 +902,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) const heightMm = component.height / MM_TO_PX; return ` -
+
${content}
`; }) @@ -1064,9 +1064,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) const componentsWithBase64 = await Promise.all( (Array.isArray(page.components) ? page.components : []).map(async (component) => { // 이미지가 있는 컴포넌트는 Base64로 변환 - if (component.imageUrl) { + if (component.image_url) { try { - const base64 = await imageUrlToBase64(component.imageUrl); + const base64 = await imageUrlToBase64(component.image_url); return { ...component, imageBase64: base64 }; } catch { return component; @@ -1093,10 +1093,10 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) for (const page of layoutConfig.pages) { const pageComponents = Array.isArray(page.components) ? page.components : []; for (const component of pageComponents) { - if (component.queryId) { - const result = getQueryResult(component.queryId); + if (component.query_id) { + const result = getQueryResult(component.query_id); if (result) { - queryResults[component.queryId] = result; + queryResults[component.query_id] = result; } } } @@ -1182,7 +1182,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) )} {(Array.isArray(page.components) ? page.components : []).map((component) => { const displayValue = getComponentValue(component); - const queryResult = component.queryId ? getQueryResult(component.queryId) : null; + const queryResult = component.query_id ? getQueryResult(component.query_id) : null; return (
@@ -1217,10 +1217,10 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) {component.type === "label" && (
@@ -1232,8 +1232,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) (() => { // tableColumns가 없으면 자동 생성 const columns = - component.tableColumns && component.tableColumns.length > 0 - ? component.tableColumns + component.table_columns && component.table_columns.length > 0 + ? component.table_columns : queryResult.fields.map((field) => ({ field, header: field, @@ -1245,22 +1245,22 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
${col.header}
${col.header}
{columns.map((col) => (
{String(row[col.field] ?? "")} @@ -1298,14 +1298,14 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
쿼리를 실행해주세요
) : null} - {component.type === "image" && component.imageUrl && ( + {component.type === "image" && component.image_url && ( 이미지 )} @@ -1314,34 +1314,34 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
@@ -1353,18 +1353,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) display: "flex", gap: "8px", flexDirection: - component.labelPosition === "top" || component.labelPosition === "bottom" + component.label_position === "top" || component.label_position === "bottom" ? "column" : "row", - ...(component.labelPosition === "right" || component.labelPosition === "bottom" + ...(component.label_position === "right" || component.label_position === "bottom" ? { flexDirection: - component.labelPosition === "right" ? "row-reverse" : "column-reverse", + component.label_position === "right" ? "row-reverse" : "column-reverse", } : {}), }} > - {component.showLabel !== false && ( + {component.show_label !== false && (
- {component.labelText || "서명:"} + {component.label_text || "서명:"}
)}
- {component.imageUrl && ( + {component.image_url && ( 서명 )} @@ -1406,7 +1406,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) height: "100%", }} > - {component.personName && ( + {component.person_name && (
- {component.personName} + {component.person_name}
)}
- {component.imageUrl && ( + {component.image_url && ( 도장 )} - {component.showLabel !== false && ( + {component.show_label !== false && (
- {component.labelText || "(인)"} + {component.label_text || "(인)"}
)}
@@ -1459,7 +1459,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) )} {component.type === "pageNumber" && (() => { - const format = component.pageNumberFormat || "number"; + const format = component.page_number_format || "number"; const pageIndex = layoutConfig.pages .sort((a, b) => a.page_order - b.page_order) .findIndex((p) => p.page_id === page.page_id); @@ -1486,9 +1486,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) justifyContent: "center", width: "100%", height: "100%", - fontSize: `${component.fontSize}px`, - color: component.fontColor, - fontWeight: component.fontWeight, + fontSize: `${component.font_size}px`, + color: component.font_color, + fontWeight: component.font_weight, }} > {pageNumberText} @@ -1498,22 +1498,22 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) {/* Card 컴포넌트 */} {component.type === "card" && (() => { - const cardTitle = component.cardTitle || "정보 카드"; - const cardItems = component.cardItems || []; - const labelWidth = component.labelWidth || 80; - const showCardTitle = component.showCardTitle !== false; - const titleFontSize = component.titleFontSize || 14; - const labelFontSize = component.labelFontSize || 13; - const valueFontSize = component.valueFontSize || 13; - const titleColor = component.titleColor || "#1e40af"; - const labelColor = component.labelColor || "#374151"; - const valueColor = component.valueColor || "#000000"; - const borderColor = component.borderColor || "#e5e7eb"; + const cardTitle = component.card_title || "정보 카드"; + const cardItems = component.card_items || []; + const labelWidth = component.label_width || 80; + const showCardTitle = component.show_card_title !== false; + const titleFontSize = component.title_font_size || 14; + const labelFontSize = component.label_font_size || 13; + const valueFontSize = component.value_font_size || 13; + const titleColor = component.title_color || "#1e40af"; + const labelColor = component.label_color || "#374151"; + const valueColor = component.value_color || "#000000"; + const borderColor = component.border_color || "#e5e7eb"; // 쿼리 바인딩된 값 가져오기 const getCardValue = (item: { label: string; value: string; fieldName?: string }) => { - if (item.fieldName && component.queryId) { - const qResult = getQueryResult(component.queryId); + if (item.fieldName && component.query_id) { + const qResult = getQueryResult(component.query_id); if (qResult && qResult.rows && qResult.rows.length > 0) { const row = qResult.rows[0]; return row[item.fieldName] !== undefined ? String(row[item.fieldName]) : item.value; @@ -1585,18 +1585,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) {/* 계산 컴포넌트 */} {component.type === "calculation" && (() => { - const calcItems = component.calcItems || []; - const resultLabel = component.resultLabel || "합계"; - const calcLabelWidth = component.labelWidth || 120; - const calcLabelFontSize = component.labelFontSize || 13; - const calcValueFontSize = component.valueFontSize || 13; - const calcResultFontSize = component.resultFontSize || 16; - const calcLabelColor = component.labelColor || "#374151"; - const calcValueColor = component.valueColor || "#000000"; - const calcResultColor = component.resultColor || "#2563eb"; - const numberFormat = component.numberFormat || "currency"; - const currencySuffix = component.currencySuffix || "원"; - const borderColor = component.borderColor || "#374151"; + const calcItems = component.calc_items || []; + const resultLabel = component.result_label || "합계"; + const calcLabelWidth = component.label_width || 120; + const calcLabelFontSize = component.label_font_size || 13; + const calcValueFontSize = component.value_font_size || 13; + const calcResultFontSize = component.result_font_size || 16; + const calcLabelColor = component.label_color || "#374151"; + const calcValueColor = component.value_color || "#000000"; + const calcResultColor = component.result_color || "#2563eb"; + const numberFormat = component.number_format || "currency"; + const currencySuffix = component.currency_suffix || "원"; + const borderColor = component.border_color || "#374151"; // 숫자 포맷팅 함수 const formatNumber = (num: number): string => { @@ -1608,8 +1608,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) // 쿼리 바인딩된 값 가져오기 const getCalcItemValue = (item: { label: string; value: number | string; operator: string; fieldName?: string }): number => { - if (item.fieldName && component.queryId) { - const qResult = getQueryResult(component.queryId); + if (item.fieldName && component.query_id) { + const qResult = getQueryResult(component.query_id); if (qResult && qResult.rows && qResult.rows.length > 0) { const row = qResult.rows[0]; const val = row[item.fieldName]; @@ -1715,18 +1715,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) {/* 체크박스 컴포넌트 */} {component.type === "checkbox" && (() => { - const checkboxSize = component.checkboxSize || 18; - const checkboxColor = component.checkboxColor || "#2563eb"; - const checkboxBorderColor = component.checkboxBorderColor || "#6b7280"; - const checkboxLabel = component.checkboxLabel || ""; - const checkboxLabelPosition = component.checkboxLabelPosition || "right"; + const checkboxSize = component.checkbox_size || 18; + const checkboxColor = component.checkbox_color || "#2563eb"; + const checkboxBorderColor = component.checkbox_border_color || "#6b7280"; + const checkboxLabel = component.checkbox_label || ""; + const checkboxLabelPosition = component.checkbox_label_position || "right"; // 체크 상태 결정 - let isChecked = component.checkboxChecked === true; - if (component.checkboxFieldName && component.queryId) { - const qResult = getQueryResult(component.queryId); + let isChecked = component.checkbox_checked === true; + if (component.checkbox_field_name && component.query_id) { + const qResult = getQueryResult(component.query_id); if (qResult && qResult.rows && qResult.rows.length > 0) { - const val = qResult.rows[0][component.checkboxFieldName]; + const val = qResult.rows[0][component.checkbox_field_name]; isChecked = val === true || val === "Y" || val === "1" || val === 1 || val === "true"; } } diff --git a/frontend/components/screen-embedding/ScreenSplitPanel.tsx b/frontend/components/screen-embedding/ScreenSplitPanel.tsx index 1fb412d3..6518dc4d 100644 --- a/frontend/components/screen-embedding/ScreenSplitPanel.tsx +++ b/frontend/components/screen-embedding/ScreenSplitPanel.tsx @@ -35,28 +35,28 @@ export function ScreenSplitPanel({ screenId, config, initialFormData, groupedDat const leftEmbedding = config?.leftScreenId ? { id: 1, - parentScreenId: screenId || 0, - childScreenId: config.leftScreenId, + parent_screen_id: screenId || 0, + child_screen_id: config.leftScreenId, position: "left" as const, mode: "view" as const, config: {}, - companyCode: "*", - createdAt: now, - updatedAt: now, + company_code: "*", + created_at: now, + updated_at: now, } : null; const rightEmbedding = config?.rightScreenId ? { id: 2, - parentScreenId: screenId || 0, - childScreenId: config.rightScreenId, + parent_screen_id: screenId || 0, + child_screen_id: config.rightScreenId, position: "right" as const, mode: "view" as const, config: {}, - companyCode: "*", - createdAt: now, - updatedAt: now, + company_code: "*", + created_at: now, + updated_at: now, } : null; @@ -94,12 +94,12 @@ export function ScreenSplitPanel({ screenId, config, initialFormData, groupedDat return ( (a.displayOrder || 0) - (b.displayOrder || 0)); for (const { screenData: screen, displayOrder, screenRole } of screensWithOrder) { + if (!screen) continue; try { // 미리 생성된 화면 코드 사용 const newScreenCode = screenCodes[codeIndex.current]; codeIndex.current++; // 진행률 업데이트 - setCopyProgress({ - current: stats.screens + 1, - total: totalScreenCount, + setCopyProgress({ + current: stats.screens + 1, + total: totalScreenCount, message: `화면 복제 중: ${screen.screen_name}` }); @@ -926,7 +927,7 @@ export default function CopyScreenModal({ parent_group_id: groupParentId, target_company_code: finalCompanyCode, display_order: groupDisplayOrder, // 사용자가 입력한 정렬 순서 - }); + } as any); if (!newGroupResponse.success || !newGroupResponse.data) { throw new Error(newGroupResponse.error || "그룹 생성 실패"); @@ -980,6 +981,7 @@ export default function CopyScreenModal({ screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0)); for (const { screenData: screen, displayOrder, screenRole } of screensWithOrder) { + if (!screen) continue; try { // 미리 생성된 화면 코드 사용 const newScreenCode = screenCodes[codeIndex.current]; diff --git a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx index 2e3257ea..a20e71dd 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx @@ -46,7 +46,7 @@ export function SortableColumnRow({
{isEntityJoin ? ( - + ) : ( #{index + 1} )} diff --git a/frontend/lib/registry/components/v2-table-grouped/TableGroupedComponent.tsx b/frontend/lib/registry/components/v2-table-grouped/TableGroupedComponent.tsx index c9f26bf7..d47cbf90 100644 --- a/frontend/lib/registry/components/v2-table-grouped/TableGroupedComponent.tsx +++ b/frontend/lib/registry/components/v2-table-grouped/TableGroupedComponent.tsx @@ -275,10 +275,10 @@ export function TableGroupedComponent({ // TABLE_SELECTION_CHANGE 이벤트 발송 (선택 데이터 변경 시 다른 컴포넌트에 알림) v2EventBus.emit(V2_EVENTS.TABLE_SELECTION_CHANGE, { - componentId: componentId || tableId, tableName: config.selectedTable || "", selectedRows: selectedItems, - selectedCount: selectedItems.length, + selectedRowIds: selectedItems.map((item: any) => item.id).filter(Boolean), + source: componentId || tableId, }); console.log("[TableGroupedComponent] 선택 변경 이벤트 발송:", { diff --git a/frontend/lib/registry/components/v2-table-grouped/TableGroupedConfigPanel.tsx b/frontend/lib/registry/components/v2-table-grouped/TableGroupedConfigPanel.tsx index 16aae2ea..8ee96647 100644 --- a/frontend/lib/registry/components/v2-table-grouped/TableGroupedConfigPanel.tsx +++ b/frontend/lib/registry/components/v2-table-grouped/TableGroupedConfigPanel.tsx @@ -35,7 +35,8 @@ import { } from "@/components/ui/command"; import { cn } from "@/lib/utils"; import { tableTypeApi } from "@/lib/api/screen"; -import { TableGroupedConfig, ColumnConfig, LinkedFilterConfig } from "./types"; +import { TableGroupedConfig, LinkedFilterConfig } from "./types"; +import { ColumnConfig } from "../v2-table-list/types"; import { groupHeaderStyleOptions, checkboxModeOptions, diff --git a/frontend/lib/registry/components/v2-table-list/SingleTableWithSticky.tsx b/frontend/lib/registry/components/v2-table-list/SingleTableWithSticky.tsx index 3a7b4dad..abce88fd 100644 --- a/frontend/lib/registry/components/v2-table-list/SingleTableWithSticky.tsx +++ b/frontend/lib/registry/components/v2-table-list/SingleTableWithSticky.tsx @@ -396,7 +396,7 @@ export const SingleTableWithSticky: React.FC = ({ ref={editInputRef as React.RefObject} value={editingValue ?? ""} onChange={(e) => onEditingValueChange?.(e.target.value)} - onKeyDown={onEditKeyDown} + onKeyDown={onEditKeyDown as React.KeyboardEventHandler} onBlur={handleBlurSave} className={cn(commonInputClass, "h-8")} onClick={(e) => e.stopPropagation()} @@ -417,7 +417,7 @@ export const SingleTableWithSticky: React.FC = ({ return ( onEditingValueChange?.(v)} + onChange={(v: string) => onEditingValueChange?.(v)} onSave={() => { handleBlurSave(); }} diff --git a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx index 46c4a82b..1d521117 100644 --- a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx @@ -2,7 +2,6 @@ import React, { useState, useEffect, useMemo, useCallback, useRef } from "react"; import { TableListConfig, ColumnConfig } from "./types"; -import { WebType } from "@/types/common"; import { tableTypeApi } from "@/lib/api/screen"; import { entityJoinApi } from "@/lib/api/entityJoin"; import { codeCache } from "@/lib/caching/codeCache"; @@ -60,7 +59,7 @@ const TableCellImage: React.FC<{ value: string }> = React.memo(({ value }) => { // 각 objid의 대표 여부를 확인 for (const objid of objids) { const info = await getFileInfoByObjid(objid); - if (info.success && info.data?.isRepresentative) { + if (info.success && info.data?.is_representative) { representativeId = objid; break; } @@ -173,9 +172,9 @@ const TableCellFile: React.FC<{ value: string }> = React.memo(({ value }) => { if (res.success && res.data) { return { objid: oid, - name: res.data.realFileName || "파일", - ext: res.data.fileExt || "", - size: res.data.fileSize || 0, + name: res.data.real_file_name || "파일", + ext: res.data.file_ext || "", + size: res.data.file_size || 0, }; } } catch {} @@ -544,6 +543,7 @@ export interface TableListComponentProps { parentTabsComponentId?: string; // 부모 탭 컴포넌트 ID // 🆕 프리뷰용 회사 코드 (DynamicComponentRenderer에서 전달, 최고 관리자만 오버라이드 가능) companyCode?: string; + renderer?: any; } // ======================================== @@ -715,14 +715,14 @@ export const TableListComponent: React.FC = ({ // columnVisibility 변경 시 컬럼 순서, 가시성, 너비 적용 useEffect(() => { if (columnVisibility.length > 0) { - const newOrder = columnVisibility.map((cv) => cv.columnName).filter((name) => name !== "__checkbox__"); // 체크박스 제외 + const newOrder = columnVisibility.map((cv) => cv.column_name).filter((name) => name !== "__checkbox__"); // 체크박스 제외 setColumnOrder(newOrder); // 너비 적용 const newWidths: Record = {}; columnVisibility.forEach((cv) => { if (cv.width) { - newWidths[cv.columnName] = cv.width; + newWidths[cv.column_name] = cv.width; } }); if (Object.keys(newWidths).length > 0) { @@ -757,7 +757,7 @@ export const TableListComponent: React.FC = ({ // columnVisibility가 있으면 가시성 적용 if (columnVisibility.length > 0) { cols = cols.filter((col) => { - const visibilityConfig = columnVisibility.find((cv) => cv.columnName === col.columnName); + const visibilityConfig = columnVisibility.find((cv) => cv.column_name === col.columnName); return visibilityConfig ? visibilityConfig.visible : true; }); } @@ -1167,10 +1167,10 @@ export const TableListComponent: React.FC = ({ return (tableConfig.columns || []) .filter((col) => col.additionalJoinInfo) .map((col) => ({ - sourceTable: col.additionalJoinInfo!.sourceTable || tableConfig.selectedTable, - sourceColumn: col.additionalJoinInfo!.sourceColumn, - joinAlias: col.additionalJoinInfo!.joinAlias, - referenceTable: col.additionalJoinInfo!.referenceTable, + source_table: col.additionalJoinInfo!.sourceTable || tableConfig.selectedTable, + source_column: col.additionalJoinInfo!.sourceColumn, + join_alias: col.additionalJoinInfo!.joinAlias, + reference_table: col.additionalJoinInfo!.referenceTable, })); }, }; @@ -1354,8 +1354,8 @@ export const TableListComponent: React.FC = ({ table_name: tableConfig.selectedTable, data_count: totalItems || data.length, // 초기 데이터 건수 포함 columns: columnsToRegister.map((col) => ({ - column_name: col.columnName || col.field, - column_label: columnLabels[col.columnName] || col.displayName || col.label || col.columnName || col.field, + column_name: col.columnName, + column_label: columnLabels[col.columnName] || col.displayName || col.columnName, input_type: columnMeta[col.columnName]?.inputType || "text", visible: col.visible !== false, width: columnWidths[col.columnName] || col.width || 150, @@ -1373,7 +1373,7 @@ export const TableListComponent: React.FC = ({ setFrozenColumnCount(count); const visibleCols = columnsToRegister .filter((col) => col.visible !== false) - .map((col) => col.columnName || col.field); + .map((col) => col.columnName); setFrozenColumns(visibleCols.slice(0, count)); }, // 탭 관련 정보 (탭 내부의 테이블인 경우) @@ -1477,21 +1477,21 @@ export const TableListComponent: React.FC = ({ tableConfig.selectedTable, initialData, parsedOrder.filter((col) => col !== "__checkbox__"), - sortColumn, + sortColumn ?? null, sortDirection, { - filterConditions: Object.keys(searchValues).length > 0 ? searchValues : undefined, - searchTerm: searchTerm || undefined, - visibleColumns: cols.map((col) => col.columnName), - columnLabels: labels, - currentPage: currentPage, - pageSize: localPageSize, - totalItems: totalItems, + filter_conditions: Object.keys(searchValues).length > 0 ? searchValues : undefined, + search_term: searchTerm || undefined, + visible_columns: cols.map((col) => col.columnName), + column_labels: labels, + current_page: currentPage, + page_size: localPageSize, + total_items: totalItems, }, ); } - onSelectedRowsChange([], [], sortColumn, sortDirection, parsedOrder, initialData); + onSelectedRowsChange([], [], sortColumn || undefined, sortDirection, parsedOrder, initialData); } } catch (error) { console.error("❌ 컬럼 순서 파싱 실패:", error); @@ -1871,16 +1871,16 @@ export const TableListComponent: React.FC = ({ if (splitPanelContext) { // 연결 필터 설정 여부 확인 (현재 테이블에 해당하는 필터가 있는지) - const linkedFiltersConfig = splitPanelContext.linkedFilters || []; + const linkedFiltersConfig = splitPanelContext.linked_filters || []; hasLinkedFiltersConfigured = linkedFiltersConfig.some( - (filter) => + (filter: any) => filter.targetColumn?.startsWith(tableConfig.selectedTable + ".") || filter.targetColumn === tableConfig.selectedTable, ); // 좌측 데이터 선택 여부 확인 hasSelectedLeftData = - splitPanelContext.selected_left_data && Object.keys(splitPanelContext.selected_left_data).length > 0; + !!(splitPanelContext.selected_left_data && Object.keys(splitPanelContext.selected_left_data).length > 0); const allLinkedFilters = splitPanelContext.getLinkedFilterValues(); @@ -2065,7 +2065,7 @@ export const TableListComponent: React.FC = ({ screenEntityConfigs: Object.keys(screenEntityConfigs).length > 0 ? screenEntityConfigs : undefined, // 🎯 화면별 엔티티 설정 전달 dataFilter: tableConfig.dataFilter, // 🆕 데이터 필터 전달 excludeFilter: excludeFilterParam, // 🆕 제외 필터 전달 - companyCodeOverride: companyCode, // 🆕 프리뷰용 회사 코드 오버라이드 (최고 관리자만) + company_code_override: companyCode, // 🆕 프리뷰용 회사 코드 오버라이드 (최고 관리자만) }); // 실제 데이터의 item_number만 추출하여 중복 확인 @@ -2096,16 +2096,16 @@ export const TableListComponent: React.FC = ({ tableConfig.selectedTable, response.data || [], cols.map((col) => col.columnName), - sortBy, + sortBy ?? null, sortOrder, { - filterConditions: filters, - searchTerm: search, - visibleColumns: cols.map((col) => col.columnName), - columnLabels: labels, - currentPage: page, - pageSize: pageSize, - totalItems: response.total || 0, + filter_conditions: filters, + search_term: search, + visible_columns: cols.map((col) => col.columnName), + column_labels: labels, + current_page: page, + page_size: pageSize, + total_items: response.total || 0, }, ); } @@ -2131,7 +2131,7 @@ export const TableListComponent: React.FC = ({ // 🆕 우측 화면일 때만 selectedLeftData 변경에 반응 (좌측 테이블은 재조회 불필요) splitPanelPosition, currentSplitPosition, - splitPanelContext?.selectedLeftData, + splitPanelContext?.selected_left_data, // 🆕 RelatedDataButtons 필터 추가 relatedButtonFilter, isRelatedButtonTarget, @@ -2383,8 +2383,8 @@ export const TableListComponent: React.FC = ({ import("@/stores/modalDataStore").then(({ useModalDataStore }) => { const modalItems = selectedRowsData.map((row, idx) => ({ id: getRowKey(row, idx), - originalData: row, - additionalData: {}, + original_data: row, + additional_data: {}, })); useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems); }); @@ -2429,8 +2429,8 @@ export const TableListComponent: React.FC = ({ import("@/stores/modalDataStore").then(({ useModalDataStore }) => { const modalItems = filteredData.map((row, idx) => ({ id: getRowKey(row, idx), - originalData: row, - additionalData: {}, + original_data: row, + additional_data: {}, })); useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems); diff --git a/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx b/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx index d903b08c..b9413937 100644 --- a/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx +++ b/frontend/lib/registry/components/v2-table-list/TableListConfigPanel.tsx @@ -469,13 +469,13 @@ export const TableListConfigPanel: React.FC = ({ // 🎯 joinTables에서 sourceColumn 찾기 const joinTableInfo = entityJoinColumns.joinTables?.find((jt: any) => jt.tableName === joinColumn.tableName); - const sourceColumn = joinTableInfo?.joinConfig?.sourceColumn || ""; + const sourceColumn = (joinTableInfo as any)?.joinConfig?.sourceColumn || ""; console.log("🔍 조인 정보 추출:", { tableName: joinColumn.tableName, foundJoinTable: !!joinTableInfo, sourceColumn, - joinConfig: joinTableInfo?.joinConfig, + joinConfig: (joinTableInfo as any)?.joinConfig, }); // 조인 탭에서 추가하는 컬럼들은 일반 컬럼으로 처리 (isEntityJoin: false) @@ -1540,7 +1540,7 @@ export const TableListConfigPanel: React.FC = ({ )} - {column.inputType || column.dataType} + {(column as any).inputType || column.dataType}
); diff --git a/frontend/lib/registry/components/v2-table-list/config-panels/ColumnsConfigPanel.tsx b/frontend/lib/registry/components/v2-table-list/config-panels/ColumnsConfigPanel.tsx index d8905f76..7da47c56 100644 --- a/frontend/lib/registry/components/v2-table-list/config-panels/ColumnsConfigPanel.tsx +++ b/frontend/lib/registry/components/v2-table-list/config-panels/ColumnsConfigPanel.tsx @@ -489,7 +489,7 @@ export const ColumnsConfigPanel: React.FC = ({ )} - {column.inputType || column.dataType} + {(column as any).inputType || column.dataType} ); diff --git a/frontend/lib/registry/components/v2-table-search-widget/index.tsx b/frontend/lib/registry/components/v2-table-search-widget/index.tsx index a0525bf9..24ca4f5e 100644 --- a/frontend/lib/registry/components/v2-table-search-widget/index.tsx +++ b/frontend/lib/registry/components/v2-table-search-widget/index.tsx @@ -18,18 +18,6 @@ ComponentRegistry.registerComponent({ web_type: "custom" as any, default_size: { width: 1920, height: 80 }, // 픽셀 단위: 전체 너비 × 80px 높이 component: TableSearchWidget, - defaultProps: { - title: "테이블 검색", - style: { - width: "100%", - height: "80px", - padding: "0.75rem", - }, - componentConfig: { - autoSelectFirstTable: true, - showTableSelector: true, - }, - }, renderer: TableSearchWidgetRenderer.render as any, config_panel: V2TableSearchWidgetConfigPanel, version: "1.0.0", diff --git a/frontend/lib/registry/components/v2-tabs-widget/TabsRenderer.tsx b/frontend/lib/registry/components/v2-tabs-widget/TabsRenderer.tsx index fd98bda6..057751fd 100644 --- a/frontend/lib/registry/components/v2-tabs-widget/TabsRenderer.tsx +++ b/frontend/lib/registry/components/v2-tabs-widget/TabsRenderer.tsx @@ -2,12 +2,12 @@ import React from "react"; import { ComponentData } from "@/types/screen"; -import { componentRegistry, ComponentRenderer } from "../DynamicComponentRenderer"; +import { componentRegistry, ComponentRenderer } from "../../DynamicComponentRenderer"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; // 탭 컴포넌트 렌더러 const TabsRenderer: ComponentRenderer = ({ component, children, ...props }) => { - const config = component.componentConfig || {}; + const config = component.component_config || {}; const { tabs = [ { id: "tab1", label: "탭 1", content: "첫 번째 탭 내용" }, diff --git a/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx b/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx index 2a042772..1d5ddfb0 100644 --- a/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx +++ b/frontend/lib/registry/components/v2-tabs-widget/tabs-component.tsx @@ -432,7 +432,7 @@ const TabsDesignEditor: React.FC<{ return { ...t, components: (t.components || []).map((c) => - c.id === comp.id ? { ...c, componentConfig: updated.componentConfig || updated.overrides || c.componentConfig } : c + c.id === comp.id ? { ...c, component_config: updated.componentConfig || updated.overrides || c.component_config } : c ), }; }); @@ -600,131 +600,6 @@ ComponentRegistry.registerComponent({ height: 600, }, - // 에디터 모드에서의 렌더링 - 탭 선택 및 컴포넌트 드롭 지원 - renderEditor: ({ - component, - isSelected, - onClick, - onDragStart, - onDragEnd, - }: any) => { - const tabsConfig = (component as any).componentConfig || {}; - const tabs: TabItem[] = tabsConfig.tabs || []; - - // 에디터 모드에서 선택된 탭 상태 관리 - const [activeTabId, setActiveTabId] = useState( - tabs[0]?.id || "" - ); - - const activeTab = tabs.find((t) => t.id === activeTabId); - - // 탭 스타일 클래스 - const getTabStyle = (tab: TabItem) => { - const isActive = tab.id === activeTabId; - return cn( - "px-4 py-2 text-sm font-medium cursor-pointer transition-colors", - isActive - ? "bg-primary/10 border-b-2 border-primary text-primary font-semibold" - : "text-foreground/70 hover:text-foreground hover:bg-muted/50" - ); - }; - - return ( -
- {/* 탭 헤더 */} -
- {tabs.length > 0 ? ( - tabs.map((tab) => ( -
{ - e.stopPropagation(); - setActiveTabId(tab.id); - }} - > - {tab.label || "탭"} -
- )) - ) : ( -
- 탭이 없습니다 -
- )} -
- - {/* 탭 컨텐츠 영역 - 드롭 영역 */} -
- {activeTab ? ( -
- {activeTab.components && activeTab.components.length > 0 ? ( -
- {activeTab.components.map((comp: TabInlineComponent) => ( -
-
- - {comp.label || comp.component_type} - - - {comp.component_type} - -
-
- ))} -
- ) : ( -
- -

- 컴포넌트를 드래그하여 추가 -

-

- 좌측 패널에서 컴포넌트를 이 영역에 드롭하세요 -

-
- )} -
- ) : ( -
-

- 설정 패널에서 탭을 추가하세요 -

-
- )} -
- - {/* 선택 표시 */} - {isSelected && ( -
- )} -
- ); - }, - - // 인터랙티브 모드에서의 렌더링 - renderInteractive: ({ component }: any) => { - return null; - }, - // 설정 패널 config_panel: React.lazy(() => import("@/components/screen/config-panels/TabsConfigPanel").then( @@ -733,28 +608,4 @@ ComponentRegistry.registerComponent({ }) ) ), - - // 검증 함수 - validate: (component: any) => { - const tabsConfig = (component as any).componentConfig || {}; - const tabs: TabItem[] = tabsConfig.tabs || []; - const errors: string[] = []; - - if (!tabs || tabs.length === 0) { - errors.push("최소 1개 이상의 탭이 필요합니다."); - } - - if (tabs) { - const tabIds = tabs.map((t) => t.id); - const uniqueIds = new Set(tabIds); - if (tabIds.length !== uniqueIds.size) { - errors.push("탭 ID가 중복되었습니다."); - } - } - - return { - isValid: errors.length === 0, - errors, - }; - }, }); diff --git a/frontend/lib/registry/layouts/card-layout/CardLayoutRenderer.tsx b/frontend/lib/registry/layouts/card-layout/CardLayoutRenderer.tsx index 28f5988c..571803fe 100644 --- a/frontend/lib/registry/layouts/card-layout/CardLayoutRenderer.tsx +++ b/frontend/lib/registry/layouts/card-layout/CardLayoutRenderer.tsx @@ -110,7 +110,7 @@ export class CardLayoutRenderer extends AutoRegisteringLayoutRenderer { * 카드 컨테이너 스타일 계산 */ getCardContainerStyle(): React.CSSProperties { - const cardConfig = this.props.layout.layout_config?.cardLayout || { + const cardConfig = (this.props.layout.layout_config as any)?.cardLayout || { columns: 3, gap: 16, }; @@ -171,7 +171,7 @@ export class CardLayoutRenderer extends AutoRegisteringLayoutRenderer { * 그리드 위치 계산 */ getGridPosition(index: number): { row: number; column: number } { - const columns = this.props.layout.layout_config?.cardLayout?.columns || 3; + const columns = (this.props.layout.layout_config as any)?.cardLayout?.columns || 3; return { row: Math.floor(index / columns), column: index % columns, diff --git a/frontend/lib/registry/layouts/hero-section/HeroSectionLayout.tsx b/frontend/lib/registry/layouts/hero-section/HeroSectionLayout.tsx index 6c2c09a0..afe3afb2 100644 --- a/frontend/lib/registry/layouts/hero-section/HeroSectionLayout.tsx +++ b/frontend/lib/registry/layouts/hero-section/HeroSectionLayout.tsx @@ -21,18 +21,18 @@ export const HeroSectionLayout: React.FC = ({ onZoneClick, ...props }) => { - if (!layout.layoutConfig.heroSection) { + if (!(layout.layout_config as any)?.heroSection) { return (
heroSection 설정이 없습니다.
-
layoutConfig.heroSection가 필요합니다.
+
layout_config.heroSection가 필요합니다.
); } - const heroSectionConfig = layout.layoutConfig.heroSection; + const heroSectionConfig = (layout.layout_config as any)?.heroSection; const containerStyle = renderer.getLayoutContainerStyle(); // heroSection 컨테이너 스타일 diff --git a/frontend/lib/registry/layouts/index.ts b/frontend/lib/registry/layouts/index.ts index be4fed96..5ea4a760 100644 --- a/frontend/lib/registry/layouts/index.ts +++ b/frontend/lib/registry/layouts/index.ts @@ -69,7 +69,7 @@ async function initializeLegacyLayouts() { variant: "default", size: "md", closable: false, - defaultTab: "tab1", + default_tab: "tab1", }, }, default_zones: [ diff --git a/frontend/lib/registry/layouts/split/SplitLayout.tsx b/frontend/lib/registry/layouts/split/SplitLayout.tsx index 7a3bcb13..5613cba3 100644 --- a/frontend/lib/registry/layouts/split/SplitLayout.tsx +++ b/frontend/lib/registry/layouts/split/SplitLayout.tsx @@ -21,18 +21,18 @@ export const SplitLayout: React.FC = ({ onZoneClick, ...props }) => { - if (!layout.layoutConfig.split) { + if (!layout.layout_config?.split) { return (
split 설정이 없습니다.
-
layoutConfig.split가 필요합니다.
+
layout_config.split가 필요합니다.
); } - const splitConfig = layout.layoutConfig.split; + const splitConfig = layout.layout_config!.split!; const containerStyle = renderer.getLayoutContainerStyle(); // split 컨테이너 스타일 diff --git a/frontend/lib/registry/pop-components/pop-card-list/PopCardListComponent.tsx b/frontend/lib/registry/pop-components/pop-card-list/PopCardListComponent.tsx index 9441f1bb..c0d92620 100644 --- a/frontend/lib/registry/pop-components/pop-card-list/PopCardListComponent.tsx +++ b/frontend/lib/registry/pop-components/pop-card-list/PopCardListComponent.tsx @@ -513,7 +513,7 @@ export function PopCardListComponent({ const cartListMode = config!.cartListMode!; // 원본 화면 미선택 시 데이터 조회하지 않음 - if (!cartListMode.sourceScreenId) { + if (!cartListMode.source_screen_id) { setLoading(false); setRows([]); return; @@ -525,7 +525,7 @@ export function PopCardListComponent({ try { // 원본 화면 레이아웃에서 설정 전체 상속 (cardTemplate, inputField, packageConfig, cardSize 등) try { - const layoutJson = await screenApi.getLayoutPop(cartListMode.sourceScreenId!); + const layoutJson = await screenApi.getLayoutPop(cartListMode.source_screen_id!); const componentsMap = layoutJson?.components || {}; const componentList = Object.values(componentsMap) as any[]; const matched = cartListMode.sourceComponentId @@ -541,8 +541,8 @@ export function PopCardListComponent({ const cartFilters: Record = { status: cartListMode.statusFilter || "in_cart", }; - if (cartListMode.sourceScreenId) { - cartFilters.screen_id = String(cartListMode.sourceScreenId); + if (cartListMode.source_screen_id) { + cartFilters.screen_id = String(cartListMode.source_screen_id); } const result = await dataApi.getTableData("cart_items", { size: 500, @@ -758,7 +758,7 @@ export function PopCardListComponent({ ref={containerRef} className={`flex h-full w-full flex-col ${className || ""}`} > - {isCartListMode && !config?.cartListMode?.sourceScreenId ? ( + {isCartListMode && !config?.cartListMode?.source_screen_id ? (

원본 화면을 선택해주세요. diff --git a/frontend/lib/registry/pop-components/pop-dashboard/items/ChartItem.tsx b/frontend/lib/registry/pop-components/pop-dashboard/items/ChartItem.tsx index fc828925..ef064796 100644 --- a/frontend/lib/registry/pop-components/pop-dashboard/items/ChartItem.tsx +++ b/frontend/lib/registry/pop-components/pop-dashboard/items/ChartItem.tsx @@ -163,10 +163,10 @@ export function ChartItemComponent({ cy="50%" outerRadius={containerWidth > 400 ? "70%" : "80%"} label={ - containerWidth > 250 + (containerWidth > 250 ? ({ name, value, percent }: { name: string; value: number; percent: number }) => `${name} ${abbreviateNumber(value)} (${(percent * 100).toFixed(0)}%)` - : false + : undefined) as any } labelLine={containerWidth > 250} >