diff --git a/frontend/components/v2/config-panels/V2AggregationWidgetConfigPanel.tsx b/frontend/components/v2/config-panels/V2AggregationWidgetConfigPanel.tsx index ace13f60..3385572d 100644 --- a/frontend/components/v2/config-panels/V2AggregationWidgetConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2AggregationWidgetConfigPanel.tsx @@ -16,6 +16,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/component import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Separator } from "@/components/ui/separator"; +import { Badge } from "@/components/ui/badge"; import { Database, Link2, @@ -30,6 +31,7 @@ import { LayoutGrid, Paintbrush, ChevronDown, + ChevronRight, } from "lucide-react"; import { cn } from "@/lib/utils"; import { tableManagementApi } from "@/lib/api/tableManagement"; @@ -238,6 +240,8 @@ export const V2AggregationWidgetConfigPanel: React.FC(null); const dataSourceType = config.dataSourceType || "table"; @@ -650,144 +654,156 @@ export const V2AggregationWidgetConfigPanel: React.FC -
- - -
- - - {(config.items || []).length === 0 ? ( -
- -

아직 집계 항목이 없어요

-

위의 추가 버튼으로 항목을 만들어보세요

-
- ) : ( -
- {(config.items || []).map((item, index) => ( -
-
- 항목 {index + 1} - -
- -
- {/* 컬럼 */} -
- 컬럼 - -
- - {/* 집계 타입 */} -
- 집계 타입 - -
- - {/* 표시 라벨 */} -
- 표시 라벨 - updateItem(item.id, { columnLabel: e.target.value })} - placeholder="표시될 라벨" - className="h-7 text-xs" - /> -
- - {/* 표시 형식 */} -
- 표시 형식 - -
- - {/* 접두사 */} -
- 접두사 - updateItem(item.id, { prefix: e.target.value })} - placeholder="예: ₩" - className="h-7 text-xs" - /> -
- - {/* 접미사 */} -
- 접미사 - updateItem(item.id, { suffix: e.target.value })} - placeholder="예: 원, 개" - className="h-7 text-xs" - /> -
-
+ + + + +
+ + + +
+ {(config.items || []).length === 0 ? ( +
+ +

아직 집계 항목이 없어요

+

위의 추가 버튼으로 항목을 만들어보세요

- ))} + ) : ( +
+ {(config.items || []).map((item, index) => ( +
+ + + {expandedItemId === item.id && ( +
+ {/* 컬럼 */} +
+ 컬럼 + +
+ + {/* 집계 타입 */} +
+ 집계 타입 + +
+ + {/* 표시 라벨 */} +
+ 표시 라벨 + updateItem(item.id, { columnLabel: e.target.value })} + placeholder="표시될 라벨" + className="h-7 text-xs" + /> +
+ + {/* 표시 형식 */} +
+ 표시 형식 + +
+ + {/* 접두사 */} +
+ 접두사 + updateItem(item.id, { prefix: e.target.value })} + placeholder="예: ₩" + className="h-7 text-xs" + /> +
+ + {/* 접미사 */} +
+ 접미사 + updateItem(item.id, { suffix: e.target.value })} + placeholder="예: 원, 개" + className="h-7 text-xs" + /> +
+
+ )} +
+ ))} +
+ )}
- )} -
+ + {/* ═══════════════════════════════════════ */} {/* 3단계: 필터 조건 (접힘) */} @@ -803,9 +819,9 @@ export const V2AggregationWidgetConfigPanel: React.FC 필터 조건 {(config.filters || []).length > 0 && ( - + {(config.filters || []).length} - + )} @@ -845,7 +861,7 @@ export const V2AggregationWidgetConfigPanel: React.FC필터 없음 - 전체 데이터를 집계합니다

) : ( -
+
{(config.filters || []).map((filter, index) => (
레이아웃 + {config.layout === "vertical" ? "세로" : "가로"}
diff --git a/frontend/components/v2/config-panels/V2BomItemEditorConfigPanel.tsx b/frontend/components/v2/config-panels/V2BomItemEditorConfigPanel.tsx index ccc37664..04865427 100644 --- a/frontend/components/v2/config-panels/V2BomItemEditorConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2BomItemEditorConfigPanel.tsx @@ -34,6 +34,7 @@ import { Check, ChevronsUpDown, GitBranch, + Settings, } from "lucide-react"; import { Command, @@ -48,6 +49,12 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; +import { Badge } from "@/components/ui/badge"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; import { tableTypeApi } from "@/lib/api/screen"; import { tableManagementApi } from "@/lib/api/tableManagement"; import { cn } from "@/lib/utils"; @@ -171,6 +178,9 @@ export function V2BomItemEditorConfigPanel({ const [loadingRelations, setLoadingRelations] = useState(false); const [tableComboboxOpen, setTableComboboxOpen] = useState(false); const [expandedColumn, setExpandedColumn] = useState(null); + const [featureOptionsOpen, setFeatureOptionsOpen] = useState(false); + const [columnSelectOpen, setColumnSelectOpen] = useState(false); + const [selectedColumnsOpen, setSelectedColumnsOpen] = useState(false); // ─── 업데이트 헬퍼 (리피터 패턴) ─── const updateConfig = useCallback( @@ -803,145 +813,215 @@ export function V2BomItemEditorConfigPanel({ )}
- {/* 기능 옵션 - Switch + 설명 텍스트 */} -
- 기능 옵션 -
-
-
-

추가 버튼

-

하위 품목을 추가할 수 있어요

+ {/* 기능 옵션 - Collapsible + Badge */} + + +
-
-
-

삭제 버튼

-

선택한 품목을 삭제할 수 있어요

+ + + +
+
+
+

추가 버튼

+

하위 품목을 추가할 수 있어요

+
+ updateFeatures("showAddButton", checked)} + />
- updateFeatures("showDeleteButton", checked)} - /> -
-
-
-

인라인 편집

-

셀을 클릭하면 바로 수정할 수 있어요

+
+
+

삭제 버튼

+

선택한 품목을 삭제할 수 있어요

+
+ updateFeatures("showDeleteButton", checked)} + />
- updateFeatures("inlineEdit", checked)} - /> -
-
-
-

행 번호

-

각 행에 순번을 표시해요

+
+
+

인라인 편집

+

셀을 클릭하면 바로 수정할 수 있어요

+
+ updateFeatures("inlineEdit", checked)} + /> +
+
+
+

행 번호

+

각 행에 순번을 표시해요

+
+ updateFeatures("showRowNumber", checked)} + />
- updateFeatures("showRowNumber", checked)} - />
-
-
+
+ {/* ─── 컬럼 설정 탭 ─── */} - {/* 통합 컬럼 선택 */} -
-
- - 어떤 컬럼을 표시하고 입력받을까요? -
-

- 소스 테이블 컬럼은 표시용, 저장 테이블 컬럼은 입력용이에요 -

- - {/* 소스 테이블 컬럼 (표시용) */} - {config.dataSource?.sourceTable && ( - <> -
- - 소스 테이블 ({config.dataSource.sourceTable}) - 표시용 + {/* 컬럼 선택 - Collapsible + Badge */} + + + + + +
+

+ 소스 테이블 컬럼은 표시용, 저장 테이블 컬럼은 입력용이에요 +

+ + {/* 소스 테이블 컬럼 (표시용) */} + {config.dataSource?.sourceTable && ( + <> +
+ + 소스 테이블 ({config.dataSource.sourceTable}) - 표시용 +
+ {loadingSourceColumns ? ( +

로딩 중...

+ ) : sourceTableColumns.length === 0 ? ( +

컬럼 정보가 없어요

+ ) : ( +
+ {sourceTableColumns.map((column) => ( +
toggleSourceDisplayColumn(column)} + > + toggleSourceDisplayColumn(column)} + className="pointer-events-none h-3.5 w-3.5" + /> + + {column.displayName} + 표시 +
+ ))} +
+ )} + + )} + + {/* 저장 테이블 컬럼 (입력용) */} +
+ + 저장 테이블 ({targetTableForColumns || "미선택"}) - 입력용 +
+ {loadingColumns ? (

로딩 중...

- ) : sourceTableColumns.length === 0 ? ( + ) : inputableColumns.length === 0 ? (

컬럼 정보가 없어요

) : ( -
- {sourceTableColumns.map((column) => ( +
+ {inputableColumns.map((column) => (
toggleSourceDisplayColumn(column)} + onClick={() => toggleInputColumn(column)} > toggleSourceDisplayColumn(column)} + checked={isColumnAdded(column.columnName)} + onCheckedChange={() => toggleInputColumn(column)} className="pointer-events-none h-3.5 w-3.5" /> - + {column.displayName} - 표시 + {column.inputType}
))}
)} - - )} - - {/* 저장 테이블 컬럼 (입력용) */} -
- - 저장 테이블 ({targetTableForColumns || "미선택"}) - 입력용 -
- {loadingColumns ? ( -

로딩 중...

- ) : inputableColumns.length === 0 ? ( -

컬럼 정보가 없어요

- ) : ( -
- {inputableColumns.map((column) => ( -
toggleInputColumn(column)} - > - toggleInputColumn(column)} - className="pointer-events-none h-3.5 w-3.5" - /> - - {column.displayName} - {column.inputType} -
- ))}
- )} -
+ + - {/* 선택된 컬럼 상세 */} + {/* 선택된 컬럼 상세 - Collapsible + Badge */} {config.columns.length > 0 && ( -
-
- 선택된 컬럼 ({config.columns.length}) - 드래그로 순서 변경 -
-
+ + + + + +
+
+ 드래그로 순서 변경 +
+
{config.columns.map((col, index) => (
))} -
-
+
+
+
+
)} diff --git a/frontend/components/v2/config-panels/V2BomTreeConfigPanel.tsx b/frontend/components/v2/config-panels/V2BomTreeConfigPanel.tsx index 17f298d4..a5ef20c5 100644 --- a/frontend/components/v2/config-panels/V2BomTreeConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2BomTreeConfigPanel.tsx @@ -22,6 +22,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; +import { Badge } from "@/components/ui/badge"; import { Database, Link2, @@ -163,6 +164,10 @@ export function V2BomTreeConfigPanel({ const [loadingRelations, setLoadingRelations] = useState(false); const [tableComboboxOpen, setTableComboboxOpen] = useState(false); const [expandedColumn, setExpandedColumn] = useState(null); + const [displayOptionsOpen, setDisplayOptionsOpen] = useState(false); + const [columnSelectOpen, setColumnSelectOpen] = useState(false); + const [selectedColumnsOpen, setSelectedColumnsOpen] = useState(false); + const [advancedOpen, setAdvancedOpen] = useState(false); const updateConfig = useCallback( (updates: Partial) => { @@ -680,62 +685,103 @@ export function V2BomTreeConfigPanel({ )}
- {/* 표시 옵션 - Switch + 설명 텍스트 */} -
- 표시 옵션 -
-
-
-

전체 펼치기/접기

-

트리를 한번에 펼치거나 접을 수 있어요

-
- updateFeatures("showExpandAll", checked)} - /> -
-
-
-

헤더 정보

-

트리 상단에 요약 정보를 표시해요

-
- updateFeatures("showHeader", checked)} - /> -
-
-
-

수량 표시

-

각 노드에 수량 정보를 보여줘요

-
- updateFeatures("showQuantity", checked)} - /> -
-
-
-

로스율 표시

-

생산 손실율을 함께 보여줘요

-
- updateFeatures("showLossRate", checked)} - /> -
-
-
- - {/* 고급 설정 (이력/버전 관리) - Collapsible */} - + {/* 표시 옵션 - Collapsible + Badge */} + - + + +
+
+
+

전체 펼치기/접기

+

트리를 한번에 펼치거나 접을 수 있어요

+
+ updateFeatures("showExpandAll", checked)} + /> +
+
+
+

헤더 정보

+

트리 상단에 요약 정보를 표시해요

+
+ updateFeatures("showHeader", checked)} + /> +
+
+
+

수량 표시

+

각 노드에 수량 정보를 보여줘요

+
+ updateFeatures("showQuantity", checked)} + /> +
+
+
+

로스율 표시

+

생산 손실율을 함께 보여줘요

+
+ updateFeatures("showLossRate", checked)} + /> +
+
+
+
+ + {/* 고급 설정 (이력/버전 관리) - Collapsible + Badge */} + + + @@ -880,95 +926,134 @@ export function V2BomTreeConfigPanel({ {/* ─── 컬럼 설정 탭 ─── */} - {/* 통합 컬럼 선택 */} -
-
- - 트리에 어떤 컬럼을 표시할까요? -
-

- 소스 테이블 컬럼은 표시용, 디테일 테이블 컬럼은 직접 컬럼이에요 -

- - {/* 소스 테이블 컬럼 (표시용) */} - {config.dataSource?.sourceTable && ( - <> -
- - 소스 테이블 ({config.dataSource.sourceTable}) - 표시용 + {/* 컬럼 선택 - Collapsible + Badge */} + + + + + +
+

+ 소스 테이블 컬럼은 표시용, 디테일 테이블 컬럼은 직접 컬럼이에요 +

+ + {/* 소스 테이블 컬럼 (표시용) */} + {config.dataSource?.sourceTable && ( + <> +
+ + 소스 테이블 ({config.dataSource.sourceTable}) - 표시용 +
+ {loadingSourceColumns ? ( +

로딩 중...

+ ) : sourceTableColumns.length === 0 ? ( +

컬럼 정보가 없어요

+ ) : ( +
+ {sourceTableColumns.map((column) => ( +
toggleSourceDisplayColumn(column)} + > + toggleSourceDisplayColumn(column)} + className="pointer-events-none h-3.5 w-3.5" + /> + + {column.displayName} + 표시 +
+ ))} +
+ )} + + )} + + {/* 디테일 테이블 컬럼 */} +
+ + 디테일 테이블 ({config.detailTable || "미선택"}) - 직접 컬럼 +
+ {loadingColumns ? (

로딩 중...

- ) : sourceTableColumns.length === 0 ? ( + ) : displayableColumns.length === 0 ? (

컬럼 정보가 없어요

) : ( -
- {sourceTableColumns.map((column) => ( +
+ {displayableColumns.map((column) => (
toggleSourceDisplayColumn(column)} + onClick={() => toggleDetailColumn(column)} > toggleSourceDisplayColumn(column)} + checked={isColumnAdded(column.columnName)} + onCheckedChange={() => toggleDetailColumn(column)} className="pointer-events-none h-3.5 w-3.5" /> - + {column.displayName} - 표시 + {column.inputType}
))}
)} - - )} - - {/* 디테일 테이블 컬럼 */} -
- - 디테일 테이블 ({config.detailTable || "미선택"}) - 직접 컬럼 -
- {loadingColumns ? ( -

로딩 중...

- ) : displayableColumns.length === 0 ? ( -

컬럼 정보가 없어요

- ) : ( -
- {displayableColumns.map((column) => ( -
toggleDetailColumn(column)} - > - toggleDetailColumn(column)} - className="pointer-events-none h-3.5 w-3.5" - /> - - {column.displayName} - {column.inputType} -
- ))}
- )} -
+ + - {/* 선택된 컬럼 상세 */} + {/* 선택된 컬럼 상세 - Collapsible + Badge */} {config.columns.length > 0 && ( -
-
- 선택된 컬럼 ({config.columns.length}) - 드래그로 순서 변경 -
-
+ + + + + +
+ 드래그로 순서 변경 +
{config.columns.map((col, index) => (
))} -
-
+
+
+
+
)}