feat: V2 컴포넌트 설정 스키마 정비 및 화면 복제 기능 개선

- 레거시 컴포넌트를 제거하고, V2/V2 컴포넌트 전용 Zod 스키마와 기본값 레지스트리를 통합 관리합니다.
- V2 컴포넌트의 overrides 스키마를 정의하고, 관련된 설정 패널을 통합하였습니다.
- 화면 복제 기능을 개선하여 DB 구조 개편 후의 효율적인 화면 관리를 지원하며, 버튼의 `targetScreenId` 매핑 버그를 수정하였습니다.
- 프리뷰 모드에서 URL 파라미터의 company_code를 우선 사용하도록 변경하였습니다.
- UnifiedRepeater 및 UnifiedSelect 컴포넌트를 추가하여 다양한 데이터 관리 기능을 지원합니다.
This commit is contained in:
kjs
2026-01-29 14:45:04 +09:00
parent 3ab8c9b5a0
commit 924c95ab89
15 changed files with 3179 additions and 1023 deletions

View File

@@ -1062,7 +1062,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 부모 컴포넌트에 초기 컬럼 순서 전달
if (onSelectedRowsChange && parsedOrder.length > 0) {
// 초기 데이터도 함께 전달 (컬럼 순서대로 재정렬)
const initialData = data.map((row: any) => {
const reordered: any = {};
@@ -2630,21 +2629,26 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 카테고리 매핑된 값 처리
if (value !== null && value !== undefined) {
const valueStr = String(value);
// 디버그 로그 (카테고리 값인 경우만)
if (valueStr.startsWith("CATEGORY_")) {
console.log("🔍 [엑셀다운로드] 카테고리 변환 시도:", {
columnName: col.columnName,
value: valueStr,
hasMappings: !!categoryMappings[col.columnName],
mappingsKeys: categoryMappings[col.columnName] ? Object.keys(categoryMappings[col.columnName]).slice(0, 5) : [],
mappingsKeys: categoryMappings[col.columnName]
? Object.keys(categoryMappings[col.columnName]).slice(0, 5)
: [],
});
}
if (categoryMappings[col.columnName]) {
// 쉼표로 구분된 중복 값 처리
if (valueStr.includes(",")) {
const values = valueStr.split(",").map((v) => v.trim()).filter((v) => v);
const values = valueStr
.split(",")
.map((v) => v.trim())
.filter((v) => v);
const labels = values.map((v) => {
const mapping = categoryMappings[col.columnName][v];
return mapping ? mapping.label : v;
@@ -2657,7 +2661,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
return mapping.label;
}
}
return value;
}
@@ -5778,7 +5782,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
}}
className={cn(
"hover:bg-primary/20 ml-1 rounded p-0.5 transition-colors",
(headerFilters[column.columnName]?.size > 0 || headerLikeFilters[column.columnName]) && "text-primary bg-primary/10",
(headerFilters[column.columnName]?.size > 0 ||
headerLikeFilters[column.columnName]) &&
"text-primary bg-primary/10",
)}
title="필터"
>
@@ -5795,7 +5801,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
<span className="text-xs font-medium">
: {columnLabels[column.columnName] || column.displayName}
</span>
{(headerFilters[column.columnName]?.size > 0 || headerLikeFilters[column.columnName]) && (
{(headerFilters[column.columnName]?.size > 0 ||
headerLikeFilters[column.columnName]) && (
<button
onClick={() => {
clearHeaderFilter(column.columnName);
@@ -5813,7 +5820,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
</div>
{/* LIKE 검색 입력 필드 */}
<div className="relative">
<Search className="text-muted-foreground absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2" />
<Search className="text-muted-foreground absolute top-1/2 left-2 h-3 w-3 -translate-y-1/2" />
<input
type="text"
placeholder="검색어 입력 (포함)"
@@ -5824,12 +5831,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
[column.columnName]: e.target.value,
}));
}}
className="border-input bg-background placeholder:text-muted-foreground h-7 w-full rounded-md border pl-7 pr-2 text-xs focus:outline-none focus:ring-1 focus:ring-primary"
className="border-input bg-background placeholder:text-muted-foreground focus:ring-primary h-7 w-full rounded-md border pr-2 pl-7 text-xs focus:ring-1 focus:outline-none"
onClick={(e) => e.stopPropagation()}
/>
</div>
{/* 구분선 */}
<div className="text-muted-foreground border-t pt-2 text-[10px]"> :</div>
<div className="text-muted-foreground border-t pt-2 text-[10px]">
:
</div>
<div className="max-h-40 space-y-1 overflow-y-auto">
{columnUniqueValues[column.columnName]?.slice(0, 50).map((val) => {
const isSelected = headerFilters[column.columnName]?.has(val);