[agent-pipeline] pipe-20260329160157-3bqb round-2
This commit is contained in:
@@ -244,9 +244,9 @@ function BarcodePreview({
|
|||||||
component.qrUseMultiField &&
|
component.qrUseMultiField &&
|
||||||
component.qrDataFields &&
|
component.qrDataFields &&
|
||||||
component.qrDataFields.length > 0 &&
|
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 (queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
||||||
if (component.qrIncludeAllRows) {
|
if (component.qrIncludeAllRows) {
|
||||||
const allRowsData: Record<string, string>[] = [];
|
const allRowsData: Record<string, string>[] = [];
|
||||||
@@ -275,8 +275,8 @@ function BarcodePreview({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 단일 필드 바인딩
|
// 단일 필드 바인딩
|
||||||
if (component.barcodeFieldName && component.queryId) {
|
if (component.barcodeFieldName && component.query_id) {
|
||||||
const queryResult = getQueryResult(component.queryId);
|
const queryResult = getQueryResult(component.query_id);
|
||||||
if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
||||||
if (isQR && component.qrIncludeAllRows) {
|
if (isQR && component.qrIncludeAllRows) {
|
||||||
const allValues = queryResult.rows
|
const allValues = queryResult.rows
|
||||||
@@ -367,14 +367,14 @@ function BarcodePreview({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) {
|
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 [isExporting, setIsExporting] = useState(false);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
// 컴포넌트의 실제 표시 값 가져오기
|
// 컴포넌트의 실제 표시 값 가져오기
|
||||||
const getComponentValue = (component: any): string => {
|
const getComponentValue = (component: any): string => {
|
||||||
if (component.queryId && component.fieldName) {
|
if (component.query_id && component.fieldName) {
|
||||||
const queryResult = getQueryResult(component.queryId);
|
const queryResult = getQueryResult(component.query_id);
|
||||||
if (queryResult && queryResult.rows.length > 0) {
|
if (queryResult && queryResult.rows.length > 0) {
|
||||||
const value = queryResult.rows[0][component.fieldName];
|
const value = queryResult.rows[0][component.fieldName];
|
||||||
if (value !== null && value !== undefined) {
|
if (value !== null && value !== undefined) {
|
||||||
@@ -398,9 +398,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
component.qrUseMultiField &&
|
component.qrUseMultiField &&
|
||||||
component.qrDataFields &&
|
component.qrDataFields &&
|
||||||
component.qrDataFields.length > 0 &&
|
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 (queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
||||||
if (component.qrIncludeAllRows) {
|
if (component.qrIncludeAllRows) {
|
||||||
const allRowsData: Record<string, string>[] = [];
|
const allRowsData: Record<string, string>[] = [];
|
||||||
@@ -428,8 +428,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.barcodeFieldName && component.queryId) {
|
if (component.barcodeFieldName && component.query_id) {
|
||||||
const queryResult = getQueryResult(component.queryId);
|
const queryResult = getQueryResult(component.query_id);
|
||||||
if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
||||||
if (isQR && component.qrIncludeAllRows) {
|
if (isQR && component.qrIncludeAllRows) {
|
||||||
const allValues = queryResult.rows
|
const allValues = queryResult.rows
|
||||||
@@ -586,39 +586,39 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
): string => {
|
): string => {
|
||||||
const componentsHTML = pageComponents
|
const componentsHTML = pageComponents
|
||||||
.map((component) => {
|
.map((component) => {
|
||||||
const queryResult = component.queryId ? getQueryResult(component.queryId) : null;
|
const queryResult = component.query_id ? getQueryResult(component.query_id) : null;
|
||||||
let content = "";
|
let content = "";
|
||||||
|
|
||||||
// Text/Label 컴포넌트
|
// Text/Label 컴포넌트
|
||||||
if (component.type === "text" || component.type === "label") {
|
if (component.type === "text" || component.type === "label") {
|
||||||
const displayValue = getComponentValue(component);
|
const displayValue = getComponentValue(component);
|
||||||
content = `<div style="font-size: ${component.fontSize || 13}px; color: ${component.fontColor || "#000000"}; font-weight: ${component.fontWeight || "normal"}; text-align: ${component.textAlign || "left"}; white-space: pre-wrap;">${displayValue}</div>`;
|
content = `<div style="font-size: ${component.font_size || 13}px; color: ${component.font_color || "#000000"}; font-weight: ${component.font_weight || "normal"}; text-align: ${component.text_align || "left"}; white-space: pre-wrap;">${displayValue}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image 컴포넌트
|
// Image 컴포넌트
|
||||||
else if (component.type === "image" && component.imageUrl) {
|
else if (component.type === "image" && component.image_url) {
|
||||||
const imageUrl = component.imageUrl.startsWith("data:")
|
const imageUrl = component.image_url.startsWith("data:")
|
||||||
? component.imageUrl
|
? component.image_url
|
||||||
: getFullImageUrl(component.imageUrl);
|
: getFullImageUrl(component.image_url);
|
||||||
content = `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.objectFit || "contain"};" />`;
|
content = `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.object_fit || "contain"};" />`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Divider 컴포넌트
|
// Divider 컴포넌트
|
||||||
else if (component.type === "divider") {
|
else if (component.type === "divider") {
|
||||||
const width = component.orientation === "horizontal" ? "100%" : `${component.lineWidth || 1}px`;
|
const width = component.orientation === "horizontal" ? "100%" : `${component.line_width || 1}px`;
|
||||||
const height = component.orientation === "vertical" ? "100%" : `${component.lineWidth || 1}px`;
|
const height = component.orientation === "vertical" ? "100%" : `${component.line_width || 1}px`;
|
||||||
content = `<div style="width: ${width}; height: ${height}; background-color: ${component.lineColor || "#000000"};"></div>`;
|
content = `<div style="width: ${width}; height: ${height}; background-color: ${component.line_color || "#000000"};"></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signature 컴포넌트
|
// Signature 컴포넌트
|
||||||
else if (component.type === "signature") {
|
else if (component.type === "signature") {
|
||||||
const labelPosition = component.labelPosition || "left";
|
const labelPosition = component.label_position || "left";
|
||||||
const showLabel = component.showLabel !== false;
|
const showLabel = component.show_label !== false;
|
||||||
const labelText = component.labelText || "서명:";
|
const labelText = component.label_text || "서명:";
|
||||||
const imageUrl = component.imageUrl
|
const imageUrl = component.image_url
|
||||||
? component.imageUrl.startsWith("data:")
|
? component.image_url.startsWith("data:")
|
||||||
? component.imageUrl
|
? component.image_url
|
||||||
: getFullImageUrl(component.imageUrl)
|
: getFullImageUrl(component.image_url)
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
if (labelPosition === "left" || labelPosition === "right") {
|
if (labelPosition === "left" || labelPosition === "right") {
|
||||||
@@ -626,7 +626,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
<div style="display: flex; align-items: center; flex-direction: ${labelPosition === "right" ? "row-reverse" : "row"}; gap: 8px; height: 100%;">
|
<div style="display: flex; align-items: center; flex-direction: ${labelPosition === "right" ? "row-reverse" : "row"}; gap: 8px; height: 100%;">
|
||||||
${showLabel ? `<div style="font-size: 12px; white-space: nowrap;">${labelText}</div>` : ""}
|
${showLabel ? `<div style="font-size: 12px; white-space: nowrap;">${labelText}</div>` : ""}
|
||||||
<div style="flex: 1; position: relative;">
|
<div style="flex: 1; position: relative;">
|
||||||
${imageUrl ? `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.objectFit || "contain"};" />` : ""}
|
${imageUrl ? `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.object_fit || "contain"};" />` : ""}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
} else {
|
} else {
|
||||||
@@ -634,7 +634,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
<div style="display: flex; flex-direction: column; align-items: center; height: 100%;">
|
<div style="display: flex; flex-direction: column; align-items: center; height: 100%;">
|
||||||
${showLabel && labelPosition === "top" ? `<div style="font-size: 12px;">${labelText}</div>` : ""}
|
${showLabel && labelPosition === "top" ? `<div style="font-size: 12px;">${labelText}</div>` : ""}
|
||||||
<div style="flex: 1; width: 100%; position: relative;">
|
<div style="flex: 1; width: 100%; position: relative;">
|
||||||
${imageUrl ? `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.objectFit || "contain"};" />` : ""}
|
${imageUrl ? `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.object_fit || "contain"};" />` : ""}
|
||||||
</div>
|
</div>
|
||||||
${showLabel && labelPosition === "bottom" ? `<div style="font-size: 12px;">${labelText}</div>` : ""}
|
${showLabel && labelPosition === "bottom" ? `<div style="font-size: 12px;">${labelText}</div>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -643,20 +643,20 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
|
|
||||||
// Stamp 컴포넌트
|
// Stamp 컴포넌트
|
||||||
else if (component.type === "stamp") {
|
else if (component.type === "stamp") {
|
||||||
const showLabel = component.showLabel !== false;
|
const showLabel = component.show_label !== false;
|
||||||
const labelText = component.labelText || "(인)";
|
const labelText = component.label_text || "(인)";
|
||||||
const personName = component.personName || "";
|
const personName = component.person_name || "";
|
||||||
const imageUrl = component.imageUrl
|
const imageUrl = component.image_url
|
||||||
? component.imageUrl.startsWith("data:")
|
? component.image_url.startsWith("data:")
|
||||||
? component.imageUrl
|
? component.image_url
|
||||||
: getFullImageUrl(component.imageUrl)
|
: getFullImageUrl(component.image_url)
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
content = `
|
content = `
|
||||||
<div style="display: flex; align-items: center; gap: 8px; width: 100%; height: 100%;">
|
<div style="display: flex; align-items: center; gap: 8px; width: 100%; height: 100%;">
|
||||||
${personName ? `<div style="font-size: 12px; white-space: nowrap;">${personName}</div>` : ""}
|
${personName ? `<div style="font-size: 12px; white-space: nowrap;">${personName}</div>` : ""}
|
||||||
<div style="position: relative; flex: 1; height: 100%;">
|
<div style="position: relative; flex: 1; height: 100%;">
|
||||||
${imageUrl ? `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.objectFit || "contain"}; border-radius: 50%;" />` : ""}
|
${imageUrl ? `<img src="${imageUrl}" style="width: 100%; height: 100%; object-fit: ${component.object_fit || "contain"}; border-radius: 50%;" />` : ""}
|
||||||
${showLabel ? `<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 12px; font-weight: bold; color: #dc2626;">${labelText}</div>` : ""}
|
${showLabel ? `<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 12px; font-weight: bold; color: #dc2626;">${labelText}</div>` : ""}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -664,7 +664,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
|
|
||||||
// PageNumber 컴포넌트
|
// PageNumber 컴포넌트
|
||||||
else if (component.type === "pageNumber") {
|
else if (component.type === "pageNumber") {
|
||||||
const format = component.pageNumberFormat || "number";
|
const format = component.page_number_format || "number";
|
||||||
let pageNumberText = "";
|
let pageNumberText = "";
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case "number":
|
case "number":
|
||||||
@@ -679,22 +679,22 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
default:
|
default:
|
||||||
pageNumberText = `${pageIndex + 1}`;
|
pageNumberText = `${pageIndex + 1}`;
|
||||||
}
|
}
|
||||||
content = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; font-size: ${component.fontSize}px; color: ${component.fontColor}; font-weight: ${component.fontWeight}; text-align: ${component.textAlign};">${pageNumberText}</div>`;
|
content = `<div style="display: flex; align-items: center; justify-content: center; height: 100%; font-size: ${component.font_size}px; color: ${component.font_color}; font-weight: ${component.font_weight}; text-align: ${component.text_align};">${pageNumberText}</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Card 컴포넌트
|
// Card 컴포넌트
|
||||||
else if (component.type === "card") {
|
else if (component.type === "card") {
|
||||||
const cardTitle = component.cardTitle || "정보 카드";
|
const cardTitle = component.card_title || "정보 카드";
|
||||||
const cardItems = component.cardItems || [];
|
const cardItems = component.card_items || [];
|
||||||
const labelWidth = component.labelWidth || 80;
|
const labelWidth = component.label_width || 80;
|
||||||
const showCardTitle = component.showCardTitle !== false;
|
const showCardTitle = component.show_card_title !== false;
|
||||||
const titleFontSize = component.titleFontSize || 14;
|
const titleFontSize = component.title_font_size || 14;
|
||||||
const labelFontSize = component.labelFontSize || 13;
|
const labelFontSize = component.label_font_size || 13;
|
||||||
const valueFontSize = component.valueFontSize || 13;
|
const valueFontSize = component.value_font_size || 13;
|
||||||
const titleColor = component.titleColor || "#1e40af";
|
const titleColor = component.title_color || "#1e40af";
|
||||||
const labelColor = component.labelColor || "#374151";
|
const labelColor = component.label_color || "#374151";
|
||||||
const valueColor = component.valueColor || "#000000";
|
const valueColor = component.value_color || "#000000";
|
||||||
const borderColor = component.borderColor || "#e5e7eb";
|
const borderColor = component.border_color || "#e5e7eb";
|
||||||
|
|
||||||
// 쿼리 바인딩된 값 가져오기
|
// 쿼리 바인딩된 값 가져오기
|
||||||
const getCardValue = (item: { label: string; value: string; fieldName?: string }) => {
|
const getCardValue = (item: { label: string; value: string; fieldName?: string }) => {
|
||||||
@@ -736,18 +736,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
|
|
||||||
// 계산 컴포넌트
|
// 계산 컴포넌트
|
||||||
else if (component.type === "calculation") {
|
else if (component.type === "calculation") {
|
||||||
const calcItems = component.calcItems || [];
|
const calcItems = component.calc_items || [];
|
||||||
const resultLabel = component.resultLabel || "합계";
|
const resultLabel = component.result_label || "합계";
|
||||||
const calcLabelWidth = component.labelWidth || 120;
|
const calcLabelWidth = component.label_width || 120;
|
||||||
const calcLabelFontSize = component.labelFontSize || 13;
|
const calcLabelFontSize = component.label_font_size || 13;
|
||||||
const calcValueFontSize = component.valueFontSize || 13;
|
const calcValueFontSize = component.value_font_size || 13;
|
||||||
const calcResultFontSize = component.resultFontSize || 16;
|
const calcResultFontSize = component.result_font_size || 16;
|
||||||
const calcLabelColor = component.labelColor || "#374151";
|
const calcLabelColor = component.label_color || "#374151";
|
||||||
const calcValueColor = component.valueColor || "#000000";
|
const calcValueColor = component.value_color || "#000000";
|
||||||
const calcResultColor = component.resultColor || "#2563eb";
|
const calcResultColor = component.result_color || "#2563eb";
|
||||||
const numberFormat = component.numberFormat || "currency";
|
const numberFormat = component.number_format || "currency";
|
||||||
const currencySuffix = component.currencySuffix || "원";
|
const currencySuffix = component.currency_suffix || "원";
|
||||||
const borderColor = component.borderColor || "#374151";
|
const borderColor = component.border_color || "#374151";
|
||||||
|
|
||||||
// 숫자 포맷팅 함수
|
// 숫자 포맷팅 함수
|
||||||
const formatNumber = (num: number): string => {
|
const formatNumber = (num: number): string => {
|
||||||
@@ -832,16 +832,16 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
|
|
||||||
// 체크박스 컴포넌트 (인쇄용)
|
// 체크박스 컴포넌트 (인쇄용)
|
||||||
else if (component.type === "checkbox") {
|
else if (component.type === "checkbox") {
|
||||||
const checkboxSize = component.checkboxSize || 18;
|
const checkboxSize = component.checkbox_size || 18;
|
||||||
const checkboxColor = component.checkboxColor || "#2563eb";
|
const checkboxColor = component.checkbox_color || "#2563eb";
|
||||||
const checkboxBorderColor = component.checkboxBorderColor || "#6b7280";
|
const checkboxBorderColor = component.checkbox_border_color || "#6b7280";
|
||||||
const checkboxLabel = component.checkboxLabel || "";
|
const checkboxLabel = component.checkbox_label || "";
|
||||||
const checkboxLabelPosition = component.checkboxLabelPosition || "right";
|
const checkboxLabelPosition = component.checkbox_label_position || "right";
|
||||||
|
|
||||||
// 체크 상태 결정
|
// 체크 상태 결정
|
||||||
let isChecked = component.checkboxChecked === true;
|
let isChecked = component.checkbox_checked === true;
|
||||||
if (component.checkboxFieldName && queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
if (component.checkbox_field_name && queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
||||||
const val = queryResult.rows[0][component.checkboxFieldName];
|
const val = queryResult.rows[0][component.checkbox_field_name];
|
||||||
isChecked = val === true || val === "Y" || val === "1" || val === 1 || val === "true";
|
isChecked = val === true || val === "Y" || val === "1" || val === 1 || val === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,8 +862,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
// Table 컴포넌트
|
// Table 컴포넌트
|
||||||
else if (component.type === "table" && queryResult && queryResult.rows.length > 0) {
|
else if (component.type === "table" && queryResult && queryResult.rows.length > 0) {
|
||||||
const columns =
|
const columns =
|
||||||
component.tableColumns && component.tableColumns.length > 0
|
component.table_columns && component.table_columns.length > 0
|
||||||
? component.tableColumns
|
? component.table_columns
|
||||||
: queryResult.fields.map((field) => ({
|
: queryResult.fields.map((field) => ({
|
||||||
field,
|
field,
|
||||||
header: field,
|
header: field,
|
||||||
@@ -875,17 +875,17 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
.map(
|
.map(
|
||||||
(row) => `
|
(row) => `
|
||||||
<tr>
|
<tr>
|
||||||
${columns.map((col: { field: string; align?: string }) => `<td style="border: ${component.showBorder !== false ? "1px solid #d1d5db" : "none"}; padding: 6px 8px; text-align: ${col.align || "left"}; height: ${component.rowHeight || "auto"}px;">${String(row[col.field] ?? "")}</td>`).join("")}
|
${columns.map((col: { field: string; align?: string }) => `<td style="border: ${component.show_border !== false ? "1px solid #d1d5db" : "none"}; padding: 6px 8px; text-align: ${col.align || "left"}; height: ${component.row_height || "auto"}px;">${String(row[col.field] ?? "")}</td>`).join("")}
|
||||||
</tr>
|
</tr>
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
content = `
|
content = `
|
||||||
<table style="width: 100%; border-collapse: ${component.showBorder !== false ? "collapse" : "separate"}; font-size: 12px;">
|
<table style="width: 100%; border-collapse: ${component.show_border !== false ? "collapse" : "separate"}; font-size: 12px;">
|
||||||
<thead style="display: table-header-group; break-inside: avoid; break-after: avoid;">
|
<thead style="display: table-header-group; break-inside: avoid; break-after: avoid;">
|
||||||
<tr style="background-color: ${component.headerBackgroundColor || "#f3f4f6"}; color: ${component.headerTextColor || "#111827"};">
|
<tr style="background-color: ${component.header_background_color || "#f3f4f6"}; color: ${component.header_text_color || "#111827"};">
|
||||||
${columns.map((col: { header: string; align?: string; width?: number }) => `<th style="border: ${component.showBorder !== false ? "1px solid #d1d5db" : "none"}; padding: 6px 8px; text-align: ${col.align || "left"}; width: ${col.width ? `${col.width}px` : "auto"}; font-weight: 600;">${col.header}</th>`).join("")}
|
${columns.map((col: { header: string; align?: string; width?: number }) => `<th style="border: ${component.show_border !== false ? "1px solid #d1d5db" : "none"}; padding: 6px 8px; text-align: ${col.align || "left"}; width: ${col.width ? `${col.width}px` : "auto"}; font-weight: 600;">${col.header}</th>`).join("")}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -902,7 +902,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
const heightMm = component.height / MM_TO_PX;
|
const heightMm = component.height / MM_TO_PX;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div style="position: absolute; left: ${xMm}mm; top: ${yMm}mm; width: ${widthMm}mm; height: ${heightMm}mm; background-color: ${component.backgroundColor || "transparent"}; border: ${component.borderWidth ? `${component.borderWidth}px solid ${component.borderColor}` : "none"}; box-sizing: border-box; overflow: hidden;">
|
<div style="position: absolute; left: ${xMm}mm; top: ${yMm}mm; width: ${widthMm}mm; height: ${heightMm}mm; background-color: ${component.background_color || "transparent"}; border: ${component.border_width ? `${component.border_width}px solid ${component.border_color}` : "none"}; box-sizing: border-box; overflow: hidden;">
|
||||||
${content}
|
${content}
|
||||||
</div>`;
|
</div>`;
|
||||||
})
|
})
|
||||||
@@ -1064,9 +1064,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
const componentsWithBase64 = await Promise.all(
|
const componentsWithBase64 = await Promise.all(
|
||||||
(Array.isArray(page.components) ? page.components : []).map(async (component) => {
|
(Array.isArray(page.components) ? page.components : []).map(async (component) => {
|
||||||
// 이미지가 있는 컴포넌트는 Base64로 변환
|
// 이미지가 있는 컴포넌트는 Base64로 변환
|
||||||
if (component.imageUrl) {
|
if (component.image_url) {
|
||||||
try {
|
try {
|
||||||
const base64 = await imageUrlToBase64(component.imageUrl);
|
const base64 = await imageUrlToBase64(component.image_url);
|
||||||
return { ...component, imageBase64: base64 };
|
return { ...component, imageBase64: base64 };
|
||||||
} catch {
|
} catch {
|
||||||
return component;
|
return component;
|
||||||
@@ -1093,10 +1093,10 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
for (const page of layoutConfig.pages) {
|
for (const page of layoutConfig.pages) {
|
||||||
const pageComponents = Array.isArray(page.components) ? page.components : [];
|
const pageComponents = Array.isArray(page.components) ? page.components : [];
|
||||||
for (const component of pageComponents) {
|
for (const component of pageComponents) {
|
||||||
if (component.queryId) {
|
if (component.query_id) {
|
||||||
const result = getQueryResult(component.queryId);
|
const result = getQueryResult(component.query_id);
|
||||||
if (result) {
|
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) => {
|
{(Array.isArray(page.components) ? page.components : []).map((component) => {
|
||||||
const displayValue = getComponentValue(component);
|
const displayValue = getComponentValue(component);
|
||||||
const queryResult = component.queryId ? getQueryResult(component.queryId) : null;
|
const queryResult = component.query_id ? getQueryResult(component.query_id) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -1193,9 +1193,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
top: `${component.y}px`,
|
top: `${component.y}px`,
|
||||||
width: `${component.width}px`,
|
width: `${component.width}px`,
|
||||||
height: `${component.height}px`,
|
height: `${component.height}px`,
|
||||||
backgroundColor: component.backgroundColor,
|
backgroundColor: component.background_color,
|
||||||
border: component.borderWidth
|
border: component.border_width
|
||||||
? `${component.borderWidth}px solid ${component.borderColor}`
|
? `${component.border_width}px solid ${component.border_color}`
|
||||||
: "none",
|
: "none",
|
||||||
padding: "8px",
|
padding: "8px",
|
||||||
}}
|
}}
|
||||||
@@ -1203,10 +1203,10 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
{component.type === "text" && (
|
{component.type === "text" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontSize: `${component.fontSize}px`,
|
fontSize: `${component.font_size}px`,
|
||||||
color: component.fontColor,
|
color: component.font_color,
|
||||||
fontWeight: component.fontWeight,
|
fontWeight: component.font_weight,
|
||||||
textAlign: component.textAlign as "left" | "center" | "right",
|
textAlign: component.text_align as "left" | "center" | "right",
|
||||||
whiteSpace: "pre-wrap",
|
whiteSpace: "pre-wrap",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -1217,10 +1217,10 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
{component.type === "label" && (
|
{component.type === "label" && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontSize: `${component.fontSize}px`,
|
fontSize: `${component.font_size}px`,
|
||||||
color: component.fontColor,
|
color: component.font_color,
|
||||||
fontWeight: component.fontWeight,
|
fontWeight: component.font_weight,
|
||||||
textAlign: component.textAlign as "left" | "center" | "right",
|
textAlign: component.text_align as "left" | "center" | "right",
|
||||||
whiteSpace: "pre-wrap",
|
whiteSpace: "pre-wrap",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -1232,8 +1232,8 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
(() => {
|
(() => {
|
||||||
// tableColumns가 없으면 자동 생성
|
// tableColumns가 없으면 자동 생성
|
||||||
const columns =
|
const columns =
|
||||||
component.tableColumns && component.tableColumns.length > 0
|
component.table_columns && component.table_columns.length > 0
|
||||||
? component.tableColumns
|
? component.table_columns
|
||||||
: queryResult.fields.map((field) => ({
|
: queryResult.fields.map((field) => ({
|
||||||
field,
|
field,
|
||||||
header: field,
|
header: field,
|
||||||
@@ -1245,22 +1245,22 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
<table
|
<table
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
borderCollapse: component.showBorder !== false ? "collapse" : "separate",
|
borderCollapse: component.show_border !== false ? "collapse" : "separate",
|
||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<tr
|
<tr
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: component.headerBackgroundColor || "#f3f4f6",
|
backgroundColor: component.header_background_color || "#f3f4f6",
|
||||||
color: component.headerTextColor || "#111827",
|
color: component.header_text_color || "#111827",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{columns.map((col) => (
|
{columns.map((col) => (
|
||||||
<th
|
<th
|
||||||
key={col.field}
|
key={col.field}
|
||||||
style={{
|
style={{
|
||||||
border: component.showBorder !== false ? "1px solid #d1d5db" : "none",
|
border: component.show_border !== false ? "1px solid #d1d5db" : "none",
|
||||||
padding: "6px 8px",
|
padding: "6px 8px",
|
||||||
textAlign: col.align || "left",
|
textAlign: col.align || "left",
|
||||||
width: col.width ? `${col.width}px` : "auto",
|
width: col.width ? `${col.width}px` : "auto",
|
||||||
@@ -1279,10 +1279,10 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
<td
|
<td
|
||||||
key={col.field}
|
key={col.field}
|
||||||
style={{
|
style={{
|
||||||
border: component.showBorder !== false ? "1px solid #d1d5db" : "none",
|
border: component.show_border !== false ? "1px solid #d1d5db" : "none",
|
||||||
padding: "6px 8px",
|
padding: "6px 8px",
|
||||||
textAlign: col.align || "left",
|
textAlign: col.align || "left",
|
||||||
height: component.rowHeight ? `${component.rowHeight}px` : "auto",
|
height: component.row_height ? `${component.row_height}px` : "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{String(row[col.field] ?? "")}
|
{String(row[col.field] ?? "")}
|
||||||
@@ -1298,14 +1298,14 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
<div className="text-xs text-muted-foreground/70">쿼리를 실행해주세요</div>
|
<div className="text-xs text-muted-foreground/70">쿼리를 실행해주세요</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{component.type === "image" && component.imageUrl && (
|
{component.type === "image" && component.image_url && (
|
||||||
<img
|
<img
|
||||||
src={getFullImageUrl(component.imageUrl)}
|
src={getFullImageUrl(component.image_url)}
|
||||||
alt="이미지"
|
alt="이미지"
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: component.objectFit || "contain",
|
objectFit: component.object_fit || "contain",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -1314,34 +1314,34 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width:
|
width:
|
||||||
component.orientation === "horizontal" ? "100%" : `${component.lineWidth || 1}px`,
|
component.orientation === "horizontal" ? "100%" : `${component.line_width || 1}px`,
|
||||||
height: component.orientation === "vertical" ? "100%" : `${component.lineWidth || 1}px`,
|
height: component.orientation === "vertical" ? "100%" : `${component.line_width || 1}px`,
|
||||||
backgroundColor: component.lineColor || "#000000",
|
backgroundColor: component.line_color || "#000000",
|
||||||
...(component.lineStyle === "dashed" && {
|
...(component.line_style === "dashed" && {
|
||||||
backgroundImage: `repeating-linear-gradient(
|
backgroundImage: `repeating-linear-gradient(
|
||||||
${component.orientation === "horizontal" ? "90deg" : "0deg"},
|
${component.orientation === "horizontal" ? "90deg" : "0deg"},
|
||||||
${component.lineColor || "#000000"} 0px,
|
${component.line_color || "#000000"} 0px,
|
||||||
${component.lineColor || "#000000"} 10px,
|
${component.line_color || "#000000"} 10px,
|
||||||
transparent 10px,
|
transparent 10px,
|
||||||
transparent 20px
|
transparent 20px
|
||||||
)`,
|
)`,
|
||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
}),
|
}),
|
||||||
...(component.lineStyle === "dotted" && {
|
...(component.line_style === "dotted" && {
|
||||||
backgroundImage: `repeating-linear-gradient(
|
backgroundImage: `repeating-linear-gradient(
|
||||||
${component.orientation === "horizontal" ? "90deg" : "0deg"},
|
${component.orientation === "horizontal" ? "90deg" : "0deg"},
|
||||||
${component.lineColor || "#000000"} 0px,
|
${component.line_color || "#000000"} 0px,
|
||||||
${component.lineColor || "#000000"} 3px,
|
${component.line_color || "#000000"} 3px,
|
||||||
transparent 3px,
|
transparent 3px,
|
||||||
transparent 10px
|
transparent 10px
|
||||||
)`,
|
)`,
|
||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
}),
|
}),
|
||||||
...(component.lineStyle === "double" && {
|
...(component.line_style === "double" && {
|
||||||
boxShadow:
|
boxShadow:
|
||||||
component.orientation === "horizontal"
|
component.orientation === "horizontal"
|
||||||
? `0 ${(component.lineWidth || 1) * 2}px 0 0 ${component.lineColor || "#000000"}`
|
? `0 ${(component.line_width || 1) * 2}px 0 0 ${component.line_color || "#000000"}`
|
||||||
: `${(component.lineWidth || 1) * 2}px 0 0 0 ${component.lineColor || "#000000"}`,
|
: `${(component.line_width || 1) * 2}px 0 0 0 ${component.line_color || "#000000"}`,
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -1353,18 +1353,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "8px",
|
gap: "8px",
|
||||||
flexDirection:
|
flexDirection:
|
||||||
component.labelPosition === "top" || component.labelPosition === "bottom"
|
component.label_position === "top" || component.label_position === "bottom"
|
||||||
? "column"
|
? "column"
|
||||||
: "row",
|
: "row",
|
||||||
...(component.labelPosition === "right" || component.labelPosition === "bottom"
|
...(component.label_position === "right" || component.label_position === "bottom"
|
||||||
? {
|
? {
|
||||||
flexDirection:
|
flexDirection:
|
||||||
component.labelPosition === "right" ? "row-reverse" : "column-reverse",
|
component.label_position === "right" ? "row-reverse" : "column-reverse",
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{component.showLabel !== false && (
|
{component.show_label !== false && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -1373,23 +1373,23 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
minWidth:
|
minWidth:
|
||||||
component.labelPosition === "left" || component.labelPosition === "right"
|
component.label_position === "left" || component.label_position === "right"
|
||||||
? "40px"
|
? "40px"
|
||||||
: "auto",
|
: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{component.labelText || "서명:"}
|
{component.label_text || "서명:"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ flex: 1, position: "relative" }}>
|
<div style={{ flex: 1, position: "relative" }}>
|
||||||
{component.imageUrl && (
|
{component.image_url && (
|
||||||
<img
|
<img
|
||||||
src={getFullImageUrl(component.imageUrl)}
|
src={getFullImageUrl(component.image_url)}
|
||||||
alt="서명"
|
alt="서명"
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: component.objectFit || "contain",
|
objectFit: component.object_fit || "contain",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -1406,7 +1406,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
height: "100%",
|
height: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{component.personName && (
|
{component.person_name && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -1415,7 +1415,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{component.personName}
|
{component.person_name}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
@@ -1424,18 +1424,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{component.imageUrl && (
|
{component.image_url && (
|
||||||
<img
|
<img
|
||||||
src={getFullImageUrl(component.imageUrl)}
|
src={getFullImageUrl(component.image_url)}
|
||||||
alt="도장"
|
alt="도장"
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: component.objectFit || "contain",
|
objectFit: component.object_fit || "contain",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{component.showLabel !== false && (
|
{component.show_label !== false && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
@@ -1451,7 +1451,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
pointerEvents: "none",
|
pointerEvents: "none",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{component.labelText || "(인)"}
|
{component.label_text || "(인)"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -1459,7 +1459,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{component.type === "pageNumber" && (() => {
|
{component.type === "pageNumber" && (() => {
|
||||||
const format = component.pageNumberFormat || "number";
|
const format = component.page_number_format || "number";
|
||||||
const pageIndex = layoutConfig.pages
|
const pageIndex = layoutConfig.pages
|
||||||
.sort((a, b) => a.page_order - b.page_order)
|
.sort((a, b) => a.page_order - b.page_order)
|
||||||
.findIndex((p) => p.page_id === page.page_id);
|
.findIndex((p) => p.page_id === page.page_id);
|
||||||
@@ -1486,9 +1486,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
fontSize: `${component.fontSize}px`,
|
fontSize: `${component.font_size}px`,
|
||||||
color: component.fontColor,
|
color: component.font_color,
|
||||||
fontWeight: component.fontWeight,
|
fontWeight: component.font_weight,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{pageNumberText}
|
{pageNumberText}
|
||||||
@@ -1498,22 +1498,22 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
|
|
||||||
{/* Card 컴포넌트 */}
|
{/* Card 컴포넌트 */}
|
||||||
{component.type === "card" && (() => {
|
{component.type === "card" && (() => {
|
||||||
const cardTitle = component.cardTitle || "정보 카드";
|
const cardTitle = component.card_title || "정보 카드";
|
||||||
const cardItems = component.cardItems || [];
|
const cardItems = component.card_items || [];
|
||||||
const labelWidth = component.labelWidth || 80;
|
const labelWidth = component.label_width || 80;
|
||||||
const showCardTitle = component.showCardTitle !== false;
|
const showCardTitle = component.show_card_title !== false;
|
||||||
const titleFontSize = component.titleFontSize || 14;
|
const titleFontSize = component.title_font_size || 14;
|
||||||
const labelFontSize = component.labelFontSize || 13;
|
const labelFontSize = component.label_font_size || 13;
|
||||||
const valueFontSize = component.valueFontSize || 13;
|
const valueFontSize = component.value_font_size || 13;
|
||||||
const titleColor = component.titleColor || "#1e40af";
|
const titleColor = component.title_color || "#1e40af";
|
||||||
const labelColor = component.labelColor || "#374151";
|
const labelColor = component.label_color || "#374151";
|
||||||
const valueColor = component.valueColor || "#000000";
|
const valueColor = component.value_color || "#000000";
|
||||||
const borderColor = component.borderColor || "#e5e7eb";
|
const borderColor = component.border_color || "#e5e7eb";
|
||||||
|
|
||||||
// 쿼리 바인딩된 값 가져오기
|
// 쿼리 바인딩된 값 가져오기
|
||||||
const getCardValue = (item: { label: string; value: string; fieldName?: string }) => {
|
const getCardValue = (item: { label: string; value: string; fieldName?: string }) => {
|
||||||
if (item.fieldName && component.queryId) {
|
if (item.fieldName && component.query_id) {
|
||||||
const qResult = getQueryResult(component.queryId);
|
const qResult = getQueryResult(component.query_id);
|
||||||
if (qResult && qResult.rows && qResult.rows.length > 0) {
|
if (qResult && qResult.rows && qResult.rows.length > 0) {
|
||||||
const row = qResult.rows[0];
|
const row = qResult.rows[0];
|
||||||
return row[item.fieldName] !== undefined ? String(row[item.fieldName]) : item.value;
|
return row[item.fieldName] !== undefined ? String(row[item.fieldName]) : item.value;
|
||||||
@@ -1585,18 +1585,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
|
|
||||||
{/* 계산 컴포넌트 */}
|
{/* 계산 컴포넌트 */}
|
||||||
{component.type === "calculation" && (() => {
|
{component.type === "calculation" && (() => {
|
||||||
const calcItems = component.calcItems || [];
|
const calcItems = component.calc_items || [];
|
||||||
const resultLabel = component.resultLabel || "합계";
|
const resultLabel = component.result_label || "합계";
|
||||||
const calcLabelWidth = component.labelWidth || 120;
|
const calcLabelWidth = component.label_width || 120;
|
||||||
const calcLabelFontSize = component.labelFontSize || 13;
|
const calcLabelFontSize = component.label_font_size || 13;
|
||||||
const calcValueFontSize = component.valueFontSize || 13;
|
const calcValueFontSize = component.value_font_size || 13;
|
||||||
const calcResultFontSize = component.resultFontSize || 16;
|
const calcResultFontSize = component.result_font_size || 16;
|
||||||
const calcLabelColor = component.labelColor || "#374151";
|
const calcLabelColor = component.label_color || "#374151";
|
||||||
const calcValueColor = component.valueColor || "#000000";
|
const calcValueColor = component.value_color || "#000000";
|
||||||
const calcResultColor = component.resultColor || "#2563eb";
|
const calcResultColor = component.result_color || "#2563eb";
|
||||||
const numberFormat = component.numberFormat || "currency";
|
const numberFormat = component.number_format || "currency";
|
||||||
const currencySuffix = component.currencySuffix || "원";
|
const currencySuffix = component.currency_suffix || "원";
|
||||||
const borderColor = component.borderColor || "#374151";
|
const borderColor = component.border_color || "#374151";
|
||||||
|
|
||||||
// 숫자 포맷팅 함수
|
// 숫자 포맷팅 함수
|
||||||
const formatNumber = (num: number): string => {
|
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 => {
|
const getCalcItemValue = (item: { label: string; value: number | string; operator: string; fieldName?: string }): number => {
|
||||||
if (item.fieldName && component.queryId) {
|
if (item.fieldName && component.query_id) {
|
||||||
const qResult = getQueryResult(component.queryId);
|
const qResult = getQueryResult(component.query_id);
|
||||||
if (qResult && qResult.rows && qResult.rows.length > 0) {
|
if (qResult && qResult.rows && qResult.rows.length > 0) {
|
||||||
const row = qResult.rows[0];
|
const row = qResult.rows[0];
|
||||||
const val = row[item.fieldName];
|
const val = row[item.fieldName];
|
||||||
@@ -1715,18 +1715,18 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||||||
|
|
||||||
{/* 체크박스 컴포넌트 */}
|
{/* 체크박스 컴포넌트 */}
|
||||||
{component.type === "checkbox" && (() => {
|
{component.type === "checkbox" && (() => {
|
||||||
const checkboxSize = component.checkboxSize || 18;
|
const checkboxSize = component.checkbox_size || 18;
|
||||||
const checkboxColor = component.checkboxColor || "#2563eb";
|
const checkboxColor = component.checkbox_color || "#2563eb";
|
||||||
const checkboxBorderColor = component.checkboxBorderColor || "#6b7280";
|
const checkboxBorderColor = component.checkbox_border_color || "#6b7280";
|
||||||
const checkboxLabel = component.checkboxLabel || "";
|
const checkboxLabel = component.checkbox_label || "";
|
||||||
const checkboxLabelPosition = component.checkboxLabelPosition || "right";
|
const checkboxLabelPosition = component.checkbox_label_position || "right";
|
||||||
|
|
||||||
// 체크 상태 결정
|
// 체크 상태 결정
|
||||||
let isChecked = component.checkboxChecked === true;
|
let isChecked = component.checkbox_checked === true;
|
||||||
if (component.checkboxFieldName && component.queryId) {
|
if (component.checkbox_field_name && component.query_id) {
|
||||||
const qResult = getQueryResult(component.queryId);
|
const qResult = getQueryResult(component.query_id);
|
||||||
if (qResult && qResult.rows && qResult.rows.length > 0) {
|
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";
|
isChecked = val === true || val === "Y" || val === "1" || val === 1 || val === "true";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,28 +35,28 @@ export function ScreenSplitPanel({ screenId, config, initialFormData, groupedDat
|
|||||||
const leftEmbedding = config?.leftScreenId
|
const leftEmbedding = config?.leftScreenId
|
||||||
? {
|
? {
|
||||||
id: 1,
|
id: 1,
|
||||||
parentScreenId: screenId || 0,
|
parent_screen_id: screenId || 0,
|
||||||
childScreenId: config.leftScreenId,
|
child_screen_id: config.leftScreenId,
|
||||||
position: "left" as const,
|
position: "left" as const,
|
||||||
mode: "view" as const,
|
mode: "view" as const,
|
||||||
config: {},
|
config: {},
|
||||||
companyCode: "*",
|
company_code: "*",
|
||||||
createdAt: now,
|
created_at: now,
|
||||||
updatedAt: now,
|
updated_at: now,
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const rightEmbedding = config?.rightScreenId
|
const rightEmbedding = config?.rightScreenId
|
||||||
? {
|
? {
|
||||||
id: 2,
|
id: 2,
|
||||||
parentScreenId: screenId || 0,
|
parent_screen_id: screenId || 0,
|
||||||
childScreenId: config.rightScreenId,
|
child_screen_id: config.rightScreenId,
|
||||||
position: "right" as const,
|
position: "right" as const,
|
||||||
mode: "view" as const,
|
mode: "view" as const,
|
||||||
config: {},
|
config: {},
|
||||||
companyCode: "*",
|
company_code: "*",
|
||||||
createdAt: now,
|
created_at: now,
|
||||||
updatedAt: now,
|
updated_at: now,
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -94,12 +94,12 @@ export function ScreenSplitPanel({ screenId, config, initialFormData, groupedDat
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SplitPanelProvider
|
<SplitPanelProvider
|
||||||
splitPanelId={splitPanelId}
|
split_panel_id={splitPanelId}
|
||||||
leftScreenId={config?.leftScreenId || null}
|
left_screen_id={config?.leftScreenId || null}
|
||||||
rightScreenId={config?.rightScreenId || null}
|
right_screen_id={config?.rightScreenId || null}
|
||||||
parentDataMapping={config?.parentDataMapping || []}
|
parent_data_mapping={config?.parentDataMapping || []}
|
||||||
linkedFilters={config?.linkedFilters || []}
|
linked_filters={config?.linkedFilters || []}
|
||||||
disableAutoDataTransfer={config?.disableAutoDataTransfer ?? false}
|
disable_auto_data_transfer={config?.disableAutoDataTransfer ?? false}
|
||||||
>
|
>
|
||||||
<ResponsiveSplitPanel
|
<ResponsiveSplitPanel
|
||||||
left={
|
left={
|
||||||
|
|||||||
@@ -747,7 +747,7 @@ export default function CopyScreenModal({
|
|||||||
parent_group_id: parentGroupId,
|
parent_group_id: parentGroupId,
|
||||||
target_company_code: targetCompany,
|
target_company_code: targetCompany,
|
||||||
display_order: sourceGroupData.display_order, // 원본 정렬순서 유지
|
display_order: sourceGroupData.display_order, // 원본 정렬순서 유지
|
||||||
});
|
} as any);
|
||||||
|
|
||||||
if (!newGroupResponse.success || !newGroupResponse.data) {
|
if (!newGroupResponse.success || !newGroupResponse.data) {
|
||||||
throw new Error(newGroupResponse.error || `그룹 생성 실패: ${sourceGroupData.group_name}`);
|
throw new Error(newGroupResponse.error || `그룹 생성 실패: ${sourceGroupData.group_name}`);
|
||||||
@@ -790,15 +790,16 @@ export default function CopyScreenModal({
|
|||||||
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
|
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
|
||||||
|
|
||||||
for (const { screenData: screen, displayOrder, screenRole } of screensWithOrder) {
|
for (const { screenData: screen, displayOrder, screenRole } of screensWithOrder) {
|
||||||
|
if (!screen) continue;
|
||||||
try {
|
try {
|
||||||
// 미리 생성된 화면 코드 사용
|
// 미리 생성된 화면 코드 사용
|
||||||
const newScreenCode = screenCodes[codeIndex.current];
|
const newScreenCode = screenCodes[codeIndex.current];
|
||||||
codeIndex.current++;
|
codeIndex.current++;
|
||||||
|
|
||||||
// 진행률 업데이트
|
// 진행률 업데이트
|
||||||
setCopyProgress({
|
setCopyProgress({
|
||||||
current: stats.screens + 1,
|
current: stats.screens + 1,
|
||||||
total: totalScreenCount,
|
total: totalScreenCount,
|
||||||
message: `화면 복제 중: ${screen.screen_name}`
|
message: `화면 복제 중: ${screen.screen_name}`
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -926,7 +927,7 @@ export default function CopyScreenModal({
|
|||||||
parent_group_id: groupParentId,
|
parent_group_id: groupParentId,
|
||||||
target_company_code: finalCompanyCode,
|
target_company_code: finalCompanyCode,
|
||||||
display_order: groupDisplayOrder, // 사용자가 입력한 정렬 순서
|
display_order: groupDisplayOrder, // 사용자가 입력한 정렬 순서
|
||||||
});
|
} as any);
|
||||||
|
|
||||||
if (!newGroupResponse.success || !newGroupResponse.data) {
|
if (!newGroupResponse.success || !newGroupResponse.data) {
|
||||||
throw new Error(newGroupResponse.error || "그룹 생성 실패");
|
throw new Error(newGroupResponse.error || "그룹 생성 실패");
|
||||||
@@ -980,6 +981,7 @@ export default function CopyScreenModal({
|
|||||||
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
|
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
|
||||||
|
|
||||||
for (const { screenData: screen, displayOrder, screenRole } of screensWithOrder) {
|
for (const { screenData: screen, displayOrder, screenRole } of screensWithOrder) {
|
||||||
|
if (!screen) continue;
|
||||||
try {
|
try {
|
||||||
// 미리 생성된 화면 코드 사용
|
// 미리 생성된 화면 코드 사용
|
||||||
const newScreenCode = screenCodes[codeIndex.current];
|
const newScreenCode = screenCodes[codeIndex.current];
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ export function SortableColumnRow({
|
|||||||
<GripVertical className="h-3 w-3" />
|
<GripVertical className="h-3 w-3" />
|
||||||
</div>
|
</div>
|
||||||
{isEntityJoin ? (
|
{isEntityJoin ? (
|
||||||
<Link2 className="h-3 w-3 shrink-0 text-primary" title="Entity 조인 컬럼" />
|
<Link2 className="h-3 w-3 shrink-0 text-primary" />
|
||||||
) : (
|
) : (
|
||||||
<span className="text-muted-foreground w-5 shrink-0 text-center text-[10px] font-medium">#{index + 1}</span>
|
<span className="text-muted-foreground w-5 shrink-0 text-center text-[10px] font-medium">#{index + 1}</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -275,10 +275,10 @@ export function TableGroupedComponent({
|
|||||||
|
|
||||||
// TABLE_SELECTION_CHANGE 이벤트 발송 (선택 데이터 변경 시 다른 컴포넌트에 알림)
|
// TABLE_SELECTION_CHANGE 이벤트 발송 (선택 데이터 변경 시 다른 컴포넌트에 알림)
|
||||||
v2EventBus.emit(V2_EVENTS.TABLE_SELECTION_CHANGE, {
|
v2EventBus.emit(V2_EVENTS.TABLE_SELECTION_CHANGE, {
|
||||||
componentId: componentId || tableId,
|
|
||||||
tableName: config.selectedTable || "",
|
tableName: config.selectedTable || "",
|
||||||
selectedRows: selectedItems,
|
selectedRows: selectedItems,
|
||||||
selectedCount: selectedItems.length,
|
selectedRowIds: selectedItems.map((item: any) => item.id).filter(Boolean),
|
||||||
|
source: componentId || tableId,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[TableGroupedComponent] 선택 변경 이벤트 발송:", {
|
console.log("[TableGroupedComponent] 선택 변경 이벤트 발송:", {
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ import {
|
|||||||
} from "@/components/ui/command";
|
} from "@/components/ui/command";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { tableTypeApi } from "@/lib/api/screen";
|
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 {
|
import {
|
||||||
groupHeaderStyleOptions,
|
groupHeaderStyleOptions,
|
||||||
checkboxModeOptions,
|
checkboxModeOptions,
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||||||
ref={editInputRef as React.RefObject<HTMLSelectElement>}
|
ref={editInputRef as React.RefObject<HTMLSelectElement>}
|
||||||
value={editingValue ?? ""}
|
value={editingValue ?? ""}
|
||||||
onChange={(e) => onEditingValueChange?.(e.target.value)}
|
onChange={(e) => onEditingValueChange?.(e.target.value)}
|
||||||
onKeyDown={onEditKeyDown}
|
onKeyDown={onEditKeyDown as React.KeyboardEventHandler<HTMLSelectElement>}
|
||||||
onBlur={handleBlurSave}
|
onBlur={handleBlurSave}
|
||||||
className={cn(commonInputClass, "h-8")}
|
className={cn(commonInputClass, "h-8")}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
@@ -417,7 +417,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||||||
return (
|
return (
|
||||||
<InlineCellDatePicker
|
<InlineCellDatePicker
|
||||||
value={editingValue ?? ""}
|
value={editingValue ?? ""}
|
||||||
onChange={(v) => onEditingValueChange?.(v)}
|
onChange={(v: string) => onEditingValueChange?.(v)}
|
||||||
onSave={() => {
|
onSave={() => {
|
||||||
handleBlurSave();
|
handleBlurSave();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect, useMemo, useCallback, useRef } from "react";
|
import React, { useState, useEffect, useMemo, useCallback, useRef } from "react";
|
||||||
import { TableListConfig, ColumnConfig } from "./types";
|
import { TableListConfig, ColumnConfig } from "./types";
|
||||||
import { WebType } from "@/types/common";
|
|
||||||
import { tableTypeApi } from "@/lib/api/screen";
|
import { tableTypeApi } from "@/lib/api/screen";
|
||||||
import { entityJoinApi } from "@/lib/api/entityJoin";
|
import { entityJoinApi } from "@/lib/api/entityJoin";
|
||||||
import { codeCache } from "@/lib/caching/codeCache";
|
import { codeCache } from "@/lib/caching/codeCache";
|
||||||
@@ -60,7 +59,7 @@ const TableCellImage: React.FC<{ value: string }> = React.memo(({ value }) => {
|
|||||||
// 각 objid의 대표 여부를 확인
|
// 각 objid의 대표 여부를 확인
|
||||||
for (const objid of objids) {
|
for (const objid of objids) {
|
||||||
const info = await getFileInfoByObjid(objid);
|
const info = await getFileInfoByObjid(objid);
|
||||||
if (info.success && info.data?.isRepresentative) {
|
if (info.success && info.data?.is_representative) {
|
||||||
representativeId = objid;
|
representativeId = objid;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -173,9 +172,9 @@ const TableCellFile: React.FC<{ value: string }> = React.memo(({ value }) => {
|
|||||||
if (res.success && res.data) {
|
if (res.success && res.data) {
|
||||||
return {
|
return {
|
||||||
objid: oid,
|
objid: oid,
|
||||||
name: res.data.realFileName || "파일",
|
name: res.data.real_file_name || "파일",
|
||||||
ext: res.data.fileExt || "",
|
ext: res.data.file_ext || "",
|
||||||
size: res.data.fileSize || 0,
|
size: res.data.file_size || 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -544,6 +543,7 @@ export interface TableListComponentProps {
|
|||||||
parentTabsComponentId?: string; // 부모 탭 컴포넌트 ID
|
parentTabsComponentId?: string; // 부모 탭 컴포넌트 ID
|
||||||
// 🆕 프리뷰용 회사 코드 (DynamicComponentRenderer에서 전달, 최고 관리자만 오버라이드 가능)
|
// 🆕 프리뷰용 회사 코드 (DynamicComponentRenderer에서 전달, 최고 관리자만 오버라이드 가능)
|
||||||
companyCode?: string;
|
companyCode?: string;
|
||||||
|
renderer?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -715,14 +715,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
// columnVisibility 변경 시 컬럼 순서, 가시성, 너비 적용
|
// columnVisibility 변경 시 컬럼 순서, 가시성, 너비 적용
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (columnVisibility.length > 0) {
|
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);
|
setColumnOrder(newOrder);
|
||||||
|
|
||||||
// 너비 적용
|
// 너비 적용
|
||||||
const newWidths: Record<string, number> = {};
|
const newWidths: Record<string, number> = {};
|
||||||
columnVisibility.forEach((cv) => {
|
columnVisibility.forEach((cv) => {
|
||||||
if (cv.width) {
|
if (cv.width) {
|
||||||
newWidths[cv.columnName] = cv.width;
|
newWidths[cv.column_name] = cv.width;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (Object.keys(newWidths).length > 0) {
|
if (Object.keys(newWidths).length > 0) {
|
||||||
@@ -757,7 +757,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
// columnVisibility가 있으면 가시성 적용
|
// columnVisibility가 있으면 가시성 적용
|
||||||
if (columnVisibility.length > 0) {
|
if (columnVisibility.length > 0) {
|
||||||
cols = cols.filter((col) => {
|
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;
|
return visibilityConfig ? visibilityConfig.visible : true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1167,10 +1167,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
return (tableConfig.columns || [])
|
return (tableConfig.columns || [])
|
||||||
.filter((col) => col.additionalJoinInfo)
|
.filter((col) => col.additionalJoinInfo)
|
||||||
.map((col) => ({
|
.map((col) => ({
|
||||||
sourceTable: col.additionalJoinInfo!.sourceTable || tableConfig.selectedTable,
|
source_table: col.additionalJoinInfo!.sourceTable || tableConfig.selectedTable,
|
||||||
sourceColumn: col.additionalJoinInfo!.sourceColumn,
|
source_column: col.additionalJoinInfo!.sourceColumn,
|
||||||
joinAlias: col.additionalJoinInfo!.joinAlias,
|
join_alias: col.additionalJoinInfo!.joinAlias,
|
||||||
referenceTable: col.additionalJoinInfo!.referenceTable,
|
reference_table: col.additionalJoinInfo!.referenceTable,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1354,8 +1354,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
table_name: tableConfig.selectedTable,
|
table_name: tableConfig.selectedTable,
|
||||||
data_count: totalItems || data.length, // 초기 데이터 건수 포함
|
data_count: totalItems || data.length, // 초기 데이터 건수 포함
|
||||||
columns: columnsToRegister.map((col) => ({
|
columns: columnsToRegister.map((col) => ({
|
||||||
column_name: col.columnName || col.field,
|
column_name: col.columnName,
|
||||||
column_label: columnLabels[col.columnName] || col.displayName || col.label || col.columnName || col.field,
|
column_label: columnLabels[col.columnName] || col.displayName || col.columnName,
|
||||||
input_type: columnMeta[col.columnName]?.inputType || "text",
|
input_type: columnMeta[col.columnName]?.inputType || "text",
|
||||||
visible: col.visible !== false,
|
visible: col.visible !== false,
|
||||||
width: columnWidths[col.columnName] || col.width || 150,
|
width: columnWidths[col.columnName] || col.width || 150,
|
||||||
@@ -1373,7 +1373,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
setFrozenColumnCount(count);
|
setFrozenColumnCount(count);
|
||||||
const visibleCols = columnsToRegister
|
const visibleCols = columnsToRegister
|
||||||
.filter((col) => col.visible !== false)
|
.filter((col) => col.visible !== false)
|
||||||
.map((col) => col.columnName || col.field);
|
.map((col) => col.columnName);
|
||||||
setFrozenColumns(visibleCols.slice(0, count));
|
setFrozenColumns(visibleCols.slice(0, count));
|
||||||
},
|
},
|
||||||
// 탭 관련 정보 (탭 내부의 테이블인 경우)
|
// 탭 관련 정보 (탭 내부의 테이블인 경우)
|
||||||
@@ -1477,21 +1477,21 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
tableConfig.selectedTable,
|
tableConfig.selectedTable,
|
||||||
initialData,
|
initialData,
|
||||||
parsedOrder.filter((col) => col !== "__checkbox__"),
|
parsedOrder.filter((col) => col !== "__checkbox__"),
|
||||||
sortColumn,
|
sortColumn ?? null,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
{
|
{
|
||||||
filterConditions: Object.keys(searchValues).length > 0 ? searchValues : undefined,
|
filter_conditions: Object.keys(searchValues).length > 0 ? searchValues : undefined,
|
||||||
searchTerm: searchTerm || undefined,
|
search_term: searchTerm || undefined,
|
||||||
visibleColumns: cols.map((col) => col.columnName),
|
visible_columns: cols.map((col) => col.columnName),
|
||||||
columnLabels: labels,
|
column_labels: labels,
|
||||||
currentPage: currentPage,
|
current_page: currentPage,
|
||||||
pageSize: localPageSize,
|
page_size: localPageSize,
|
||||||
totalItems: totalItems,
|
total_items: totalItems,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectedRowsChange([], [], sortColumn, sortDirection, parsedOrder, initialData);
|
onSelectedRowsChange([], [], sortColumn || undefined, sortDirection, parsedOrder, initialData);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 컬럼 순서 파싱 실패:", error);
|
console.error("❌ 컬럼 순서 파싱 실패:", error);
|
||||||
@@ -1871,16 +1871,16 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
|
|
||||||
if (splitPanelContext) {
|
if (splitPanelContext) {
|
||||||
// 연결 필터 설정 여부 확인 (현재 테이블에 해당하는 필터가 있는지)
|
// 연결 필터 설정 여부 확인 (현재 테이블에 해당하는 필터가 있는지)
|
||||||
const linkedFiltersConfig = splitPanelContext.linkedFilters || [];
|
const linkedFiltersConfig = splitPanelContext.linked_filters || [];
|
||||||
hasLinkedFiltersConfigured = linkedFiltersConfig.some(
|
hasLinkedFiltersConfigured = linkedFiltersConfig.some(
|
||||||
(filter) =>
|
(filter: any) =>
|
||||||
filter.targetColumn?.startsWith(tableConfig.selectedTable + ".") ||
|
filter.targetColumn?.startsWith(tableConfig.selectedTable + ".") ||
|
||||||
filter.targetColumn === tableConfig.selectedTable,
|
filter.targetColumn === tableConfig.selectedTable,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 좌측 데이터 선택 여부 확인
|
// 좌측 데이터 선택 여부 확인
|
||||||
hasSelectedLeftData =
|
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();
|
const allLinkedFilters = splitPanelContext.getLinkedFilterValues();
|
||||||
|
|
||||||
@@ -2065,7 +2065,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
screenEntityConfigs: Object.keys(screenEntityConfigs).length > 0 ? screenEntityConfigs : undefined, // 🎯 화면별 엔티티 설정 전달
|
screenEntityConfigs: Object.keys(screenEntityConfigs).length > 0 ? screenEntityConfigs : undefined, // 🎯 화면별 엔티티 설정 전달
|
||||||
dataFilter: tableConfig.dataFilter, // 🆕 데이터 필터 전달
|
dataFilter: tableConfig.dataFilter, // 🆕 데이터 필터 전달
|
||||||
excludeFilter: excludeFilterParam, // 🆕 제외 필터 전달
|
excludeFilter: excludeFilterParam, // 🆕 제외 필터 전달
|
||||||
companyCodeOverride: companyCode, // 🆕 프리뷰용 회사 코드 오버라이드 (최고 관리자만)
|
company_code_override: companyCode, // 🆕 프리뷰용 회사 코드 오버라이드 (최고 관리자만)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 실제 데이터의 item_number만 추출하여 중복 확인
|
// 실제 데이터의 item_number만 추출하여 중복 확인
|
||||||
@@ -2096,16 +2096,16 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
tableConfig.selectedTable,
|
tableConfig.selectedTable,
|
||||||
response.data || [],
|
response.data || [],
|
||||||
cols.map((col) => col.columnName),
|
cols.map((col) => col.columnName),
|
||||||
sortBy,
|
sortBy ?? null,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
{
|
{
|
||||||
filterConditions: filters,
|
filter_conditions: filters,
|
||||||
searchTerm: search,
|
search_term: search,
|
||||||
visibleColumns: cols.map((col) => col.columnName),
|
visible_columns: cols.map((col) => col.columnName),
|
||||||
columnLabels: labels,
|
column_labels: labels,
|
||||||
currentPage: page,
|
current_page: page,
|
||||||
pageSize: pageSize,
|
page_size: pageSize,
|
||||||
totalItems: response.total || 0,
|
total_items: response.total || 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2131,7 +2131,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
// 🆕 우측 화면일 때만 selectedLeftData 변경에 반응 (좌측 테이블은 재조회 불필요)
|
// 🆕 우측 화면일 때만 selectedLeftData 변경에 반응 (좌측 테이블은 재조회 불필요)
|
||||||
splitPanelPosition,
|
splitPanelPosition,
|
||||||
currentSplitPosition,
|
currentSplitPosition,
|
||||||
splitPanelContext?.selectedLeftData,
|
splitPanelContext?.selected_left_data,
|
||||||
// 🆕 RelatedDataButtons 필터 추가
|
// 🆕 RelatedDataButtons 필터 추가
|
||||||
relatedButtonFilter,
|
relatedButtonFilter,
|
||||||
isRelatedButtonTarget,
|
isRelatedButtonTarget,
|
||||||
@@ -2383,8 +2383,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
||||||
const modalItems = selectedRowsData.map((row, idx) => ({
|
const modalItems = selectedRowsData.map((row, idx) => ({
|
||||||
id: getRowKey(row, idx),
|
id: getRowKey(row, idx),
|
||||||
originalData: row,
|
original_data: row,
|
||||||
additionalData: {},
|
additional_data: {},
|
||||||
}));
|
}));
|
||||||
useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems);
|
useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems);
|
||||||
});
|
});
|
||||||
@@ -2429,8 +2429,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||||||
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
||||||
const modalItems = filteredData.map((row, idx) => ({
|
const modalItems = filteredData.map((row, idx) => ({
|
||||||
id: getRowKey(row, idx),
|
id: getRowKey(row, idx),
|
||||||
originalData: row,
|
original_data: row,
|
||||||
additionalData: {},
|
additional_data: {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems);
|
useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems);
|
||||||
|
|||||||
@@ -469,13 +469,13 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||||||
|
|
||||||
// 🎯 joinTables에서 sourceColumn 찾기
|
// 🎯 joinTables에서 sourceColumn 찾기
|
||||||
const joinTableInfo = entityJoinColumns.joinTables?.find((jt: any) => jt.tableName === joinColumn.tableName);
|
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("🔍 조인 정보 추출:", {
|
console.log("🔍 조인 정보 추출:", {
|
||||||
tableName: joinColumn.tableName,
|
tableName: joinColumn.tableName,
|
||||||
foundJoinTable: !!joinTableInfo,
|
foundJoinTable: !!joinTableInfo,
|
||||||
sourceColumn,
|
sourceColumn,
|
||||||
joinConfig: joinTableInfo?.joinConfig,
|
joinConfig: (joinTableInfo as any)?.joinConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 조인 탭에서 추가하는 컬럼들은 일반 컬럼으로 처리 (isEntityJoin: false)
|
// 조인 탭에서 추가하는 컬럼들은 일반 컬럼으로 처리 (isEntityJoin: false)
|
||||||
@@ -1540,7 +1540,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<span className={cn("text-[10px] text-primary/80", !isAlreadyAdded && "ml-auto")}>
|
<span className={cn("text-[10px] text-primary/80", !isAlreadyAdded && "ml-auto")}>
|
||||||
{column.inputType || column.dataType}
|
{(column as any).inputType || column.dataType}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -489,7 +489,7 @@ export const ColumnsConfigPanel: React.FC<ColumnsConfigPanelProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<span className={cn("text-[10px] text-primary/80", !isAlreadyAdded && "ml-auto")}>
|
<span className={cn("text-[10px] text-primary/80", !isAlreadyAdded && "ml-auto")}>
|
||||||
{column.inputType || column.dataType}
|
{(column as any).inputType || column.dataType}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,18 +18,6 @@ ComponentRegistry.registerComponent({
|
|||||||
web_type: "custom" as any,
|
web_type: "custom" as any,
|
||||||
default_size: { width: 1920, height: 80 }, // 픽셀 단위: 전체 너비 × 80px 높이
|
default_size: { width: 1920, height: 80 }, // 픽셀 단위: 전체 너비 × 80px 높이
|
||||||
component: TableSearchWidget,
|
component: TableSearchWidget,
|
||||||
defaultProps: {
|
|
||||||
title: "테이블 검색",
|
|
||||||
style: {
|
|
||||||
width: "100%",
|
|
||||||
height: "80px",
|
|
||||||
padding: "0.75rem",
|
|
||||||
},
|
|
||||||
componentConfig: {
|
|
||||||
autoSelectFirstTable: true,
|
|
||||||
showTableSelector: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
renderer: TableSearchWidgetRenderer.render as any,
|
renderer: TableSearchWidgetRenderer.render as any,
|
||||||
config_panel: V2TableSearchWidgetConfigPanel,
|
config_panel: V2TableSearchWidgetConfigPanel,
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ComponentData } from "@/types/screen";
|
import { ComponentData } from "@/types/screen";
|
||||||
import { componentRegistry, ComponentRenderer } from "../DynamicComponentRenderer";
|
import { componentRegistry, ComponentRenderer } from "../../DynamicComponentRenderer";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
|
||||||
// 탭 컴포넌트 렌더러
|
// 탭 컴포넌트 렌더러
|
||||||
const TabsRenderer: ComponentRenderer = ({ component, children, ...props }) => {
|
const TabsRenderer: ComponentRenderer = ({ component, children, ...props }) => {
|
||||||
const config = component.componentConfig || {};
|
const config = component.component_config || {};
|
||||||
const {
|
const {
|
||||||
tabs = [
|
tabs = [
|
||||||
{ id: "tab1", label: "탭 1", content: "첫 번째 탭 내용" },
|
{ id: "tab1", label: "탭 1", content: "첫 번째 탭 내용" },
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ const TabsDesignEditor: React.FC<{
|
|||||||
return {
|
return {
|
||||||
...t,
|
...t,
|
||||||
components: (t.components || []).map((c) =>
|
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,
|
height: 600,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 에디터 모드에서의 렌더링 - 탭 선택 및 컴포넌트 드롭 지원
|
|
||||||
renderEditor: ({
|
|
||||||
component,
|
|
||||||
isSelected,
|
|
||||||
onClick,
|
|
||||||
onDragStart,
|
|
||||||
onDragEnd,
|
|
||||||
}: any) => {
|
|
||||||
const tabsConfig = (component as any).componentConfig || {};
|
|
||||||
const tabs: TabItem[] = tabsConfig.tabs || [];
|
|
||||||
|
|
||||||
// 에디터 모드에서 선택된 탭 상태 관리
|
|
||||||
const [activeTabId, setActiveTabId] = useState<string>(
|
|
||||||
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 (
|
|
||||||
<div
|
|
||||||
className="flex h-full w-full flex-col overflow-hidden rounded-lg border bg-background"
|
|
||||||
onClick={onClick}
|
|
||||||
onDragStart={onDragStart}
|
|
||||||
onDragEnd={onDragEnd}
|
|
||||||
>
|
|
||||||
{/* 탭 헤더 */}
|
|
||||||
<div className="flex items-center border-b bg-muted/50">
|
|
||||||
{tabs.length > 0 ? (
|
|
||||||
tabs.map((tab) => (
|
|
||||||
<div
|
|
||||||
key={tab.id}
|
|
||||||
className={getTabStyle(tab)}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setActiveTabId(tab.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{tab.label || "탭"}
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="px-4 py-2 text-sm text-muted-foreground">
|
|
||||||
탭이 없습니다
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 탭 컨텐츠 영역 - 드롭 영역 */}
|
|
||||||
<div
|
|
||||||
className="relative flex-1 overflow-hidden"
|
|
||||||
data-tabs-container="true"
|
|
||||||
data-component-id={component.id}
|
|
||||||
data-active-tab-id={activeTabId}
|
|
||||||
>
|
|
||||||
{activeTab ? (
|
|
||||||
<div className="absolute inset-0 overflow-auto p-2">
|
|
||||||
{activeTab.components && activeTab.components.length > 0 ? (
|
|
||||||
<div className="relative h-full w-full">
|
|
||||||
{activeTab.components.map((comp: TabInlineComponent) => (
|
|
||||||
<div
|
|
||||||
key={comp.id}
|
|
||||||
className="absolute rounded border border-dashed border-input bg-white/80 p-2 shadow-sm"
|
|
||||||
style={{
|
|
||||||
left: comp.position?.x || 0,
|
|
||||||
top: comp.position?.y || 0,
|
|
||||||
width: comp.size?.width || 200,
|
|
||||||
height: comp.size?.height || 100,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex h-full flex-col items-center justify-center">
|
|
||||||
<span className="text-xs font-medium text-muted-foreground">
|
|
||||||
{comp.label || comp.component_type}
|
|
||||||
</span>
|
|
||||||
<span className="text-[10px] text-muted-foreground/70">
|
|
||||||
{comp.component_type}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center rounded border-2 border-dashed border-input bg-muted/50">
|
|
||||||
<Plus className="mb-2 h-8 w-8 text-muted-foreground/70" />
|
|
||||||
<p className="text-sm font-medium text-muted-foreground">
|
|
||||||
컴포넌트를 드래그하여 추가
|
|
||||||
</p>
|
|
||||||
<p className="mt-1 text-xs text-muted-foreground/70">
|
|
||||||
좌측 패널에서 컴포넌트를 이 영역에 드롭하세요
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
설정 패널에서 탭을 추가하세요
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 선택 표시 */}
|
|
||||||
{isSelected && (
|
|
||||||
<div className="pointer-events-none absolute inset-0 rounded-lg ring-2 ring-primary ring-offset-2" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// 인터랙티브 모드에서의 렌더링
|
|
||||||
renderInteractive: ({ component }: any) => {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
// 설정 패널
|
// 설정 패널
|
||||||
config_panel: React.lazy(() =>
|
config_panel: React.lazy(() =>
|
||||||
import("@/components/screen/config-panels/TabsConfigPanel").then(
|
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,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export class CardLayoutRenderer extends AutoRegisteringLayoutRenderer {
|
|||||||
* 카드 컨테이너 스타일 계산
|
* 카드 컨테이너 스타일 계산
|
||||||
*/
|
*/
|
||||||
getCardContainerStyle(): React.CSSProperties {
|
getCardContainerStyle(): React.CSSProperties {
|
||||||
const cardConfig = this.props.layout.layout_config?.cardLayout || {
|
const cardConfig = (this.props.layout.layout_config as any)?.cardLayout || {
|
||||||
columns: 3,
|
columns: 3,
|
||||||
gap: 16,
|
gap: 16,
|
||||||
};
|
};
|
||||||
@@ -171,7 +171,7 @@ export class CardLayoutRenderer extends AutoRegisteringLayoutRenderer {
|
|||||||
* 그리드 위치 계산
|
* 그리드 위치 계산
|
||||||
*/
|
*/
|
||||||
getGridPosition(index: number): { row: number; column: number } {
|
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 {
|
return {
|
||||||
row: Math.floor(index / columns),
|
row: Math.floor(index / columns),
|
||||||
column: index % columns,
|
column: index % columns,
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ export const HeroSectionLayout: React.FC<HeroSectionLayoutProps> = ({
|
|||||||
onZoneClick,
|
onZoneClick,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
if (!layout.layoutConfig.heroSection) {
|
if (!(layout.layout_config as any)?.heroSection) {
|
||||||
return (
|
return (
|
||||||
<div className="error-layout flex items-center justify-center rounded border-2 border-destructive/30 bg-destructive/10 p-4">
|
<div className="error-layout flex items-center justify-center rounded border-2 border-destructive/30 bg-destructive/10 p-4">
|
||||||
<div className="text-center text-destructive">
|
<div className="text-center text-destructive">
|
||||||
<div className="font-medium">heroSection 설정이 없습니다.</div>
|
<div className="font-medium">heroSection 설정이 없습니다.</div>
|
||||||
<div className="mt-1 text-sm">layoutConfig.heroSection가 필요합니다.</div>
|
<div className="mt-1 text-sm">layout_config.heroSection가 필요합니다.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const heroSectionConfig = layout.layoutConfig.heroSection;
|
const heroSectionConfig = (layout.layout_config as any)?.heroSection;
|
||||||
const containerStyle = renderer.getLayoutContainerStyle();
|
const containerStyle = renderer.getLayoutContainerStyle();
|
||||||
|
|
||||||
// heroSection 컨테이너 스타일
|
// heroSection 컨테이너 스타일
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ async function initializeLegacyLayouts() {
|
|||||||
variant: "default",
|
variant: "default",
|
||||||
size: "md",
|
size: "md",
|
||||||
closable: false,
|
closable: false,
|
||||||
defaultTab: "tab1",
|
default_tab: "tab1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default_zones: [
|
default_zones: [
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ export const SplitLayout: React.FC<SplitLayoutProps> = ({
|
|||||||
onZoneClick,
|
onZoneClick,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
if (!layout.layoutConfig.split) {
|
if (!layout.layout_config?.split) {
|
||||||
return (
|
return (
|
||||||
<div className="error-layout flex items-center justify-center rounded border-2 border-destructive/30 bg-destructive/10 p-4">
|
<div className="error-layout flex items-center justify-center rounded border-2 border-destructive/30 bg-destructive/10 p-4">
|
||||||
<div className="text-center text-destructive">
|
<div className="text-center text-destructive">
|
||||||
<div className="font-medium">split 설정이 없습니다.</div>
|
<div className="font-medium">split 설정이 없습니다.</div>
|
||||||
<div className="mt-1 text-sm">layoutConfig.split가 필요합니다.</div>
|
<div className="mt-1 text-sm">layout_config.split가 필요합니다.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const splitConfig = layout.layoutConfig.split;
|
const splitConfig = layout.layout_config!.split!;
|
||||||
const containerStyle = renderer.getLayoutContainerStyle();
|
const containerStyle = renderer.getLayoutContainerStyle();
|
||||||
|
|
||||||
// split 컨테이너 스타일
|
// split 컨테이너 스타일
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ export function PopCardListComponent({
|
|||||||
const cartListMode = config!.cartListMode!;
|
const cartListMode = config!.cartListMode!;
|
||||||
|
|
||||||
// 원본 화면 미선택 시 데이터 조회하지 않음
|
// 원본 화면 미선택 시 데이터 조회하지 않음
|
||||||
if (!cartListMode.sourceScreenId) {
|
if (!cartListMode.source_screen_id) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setRows([]);
|
setRows([]);
|
||||||
return;
|
return;
|
||||||
@@ -525,7 +525,7 @@ export function PopCardListComponent({
|
|||||||
try {
|
try {
|
||||||
// 원본 화면 레이아웃에서 설정 전체 상속 (cardTemplate, inputField, packageConfig, cardSize 등)
|
// 원본 화면 레이아웃에서 설정 전체 상속 (cardTemplate, inputField, packageConfig, cardSize 등)
|
||||||
try {
|
try {
|
||||||
const layoutJson = await screenApi.getLayoutPop(cartListMode.sourceScreenId!);
|
const layoutJson = await screenApi.getLayoutPop(cartListMode.source_screen_id!);
|
||||||
const componentsMap = layoutJson?.components || {};
|
const componentsMap = layoutJson?.components || {};
|
||||||
const componentList = Object.values(componentsMap) as any[];
|
const componentList = Object.values(componentsMap) as any[];
|
||||||
const matched = cartListMode.sourceComponentId
|
const matched = cartListMode.sourceComponentId
|
||||||
@@ -541,8 +541,8 @@ export function PopCardListComponent({
|
|||||||
const cartFilters: Record<string, unknown> = {
|
const cartFilters: Record<string, unknown> = {
|
||||||
status: cartListMode.statusFilter || "in_cart",
|
status: cartListMode.statusFilter || "in_cart",
|
||||||
};
|
};
|
||||||
if (cartListMode.sourceScreenId) {
|
if (cartListMode.source_screen_id) {
|
||||||
cartFilters.screen_id = String(cartListMode.sourceScreenId);
|
cartFilters.screen_id = String(cartListMode.source_screen_id);
|
||||||
}
|
}
|
||||||
const result = await dataApi.getTableData("cart_items", {
|
const result = await dataApi.getTableData("cart_items", {
|
||||||
size: 500,
|
size: 500,
|
||||||
@@ -758,7 +758,7 @@ export function PopCardListComponent({
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className={`flex h-full w-full flex-col ${className || ""}`}
|
className={`flex h-full w-full flex-col ${className || ""}`}
|
||||||
>
|
>
|
||||||
{isCartListMode && !config?.cartListMode?.sourceScreenId ? (
|
{isCartListMode && !config?.cartListMode?.source_screen_id ? (
|
||||||
<div className="flex flex-1 items-center justify-center rounded-md border border-dashed bg-muted/30 p-4">
|
<div className="flex flex-1 items-center justify-center rounded-md border border-dashed bg-muted/30 p-4">
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
원본 화면을 선택해주세요.
|
원본 화면을 선택해주세요.
|
||||||
|
|||||||
@@ -163,10 +163,10 @@ export function ChartItemComponent({
|
|||||||
cy="50%"
|
cy="50%"
|
||||||
outerRadius={containerWidth > 400 ? "70%" : "80%"}
|
outerRadius={containerWidth > 400 ? "70%" : "80%"}
|
||||||
label={
|
label={
|
||||||
containerWidth > 250
|
(containerWidth > 250
|
||||||
? ({ name, value, percent }: { name: string; value: number; percent: number }) =>
|
? ({ name, value, percent }: { name: string; value: number; percent: number }) =>
|
||||||
`${name} ${abbreviateNumber(value)} (${(percent * 100).toFixed(0)}%)`
|
`${name} ${abbreviateNumber(value)} (${(percent * 100).toFixed(0)}%)`
|
||||||
: false
|
: undefined) as any
|
||||||
}
|
}
|
||||||
labelLine={containerWidth > 250}
|
labelLine={containerWidth > 250}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user