반응형
[
{
"scenarioId": "SC001",
"name": "Sales",
"parentId": null,
"scenarioType": "Group",
"url": ""
},
{
"scenarioId": "SC001001",
"name": "EDI Order Management",
"parentId": "SC001",
"scenarioType": "Screen",
"url": "/edi/EDIOrderManagement"
},
{
"scenarioId": "SC002",
"name": "Service",
"parentId": null,
"scenarioType": "Group",
"url": ""
},
{
"scenarioId": "SC002001",
"name": "PD EDI Order Monitoring",
"parentId": "SC002",
"scenarioType": "Screen",
"url": "/edi/PD_EDI_OrderMonitoring"
}
]
import React, { useEffect, useState, useMemo } from "react";
import { AgGridReact } from "ag-grid-react";
import { ColDef } from "ag-grid-community";
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
type Scenario = {
scenarioId: string;
name: string;
parentId: string | null;
scenarioType: string;
url: string;
path?: string[]; // 트리 경로
};
const ScenarioManagement: React.FC = () => {
const [allScenarios, setAllScenarios] = useState<Scenario[]>([]);
const [treeData, setTreeData] = useState<Scenario[]>([]);
const [rowData, setRowData] = useState<Scenario[]>([]);
// 서버에서 데이터 조회
useEffect(() => {
fetch("/api/scenarios") // 실제 API 엔드포인트로 교체
.then((res) => res.json())
.then((data: Scenario[]) => {
// path 생성 (트리 표현용)
const withPath = buildPaths(data);
setAllScenarios(withPath);
setTreeData(withPath);
setRowData(withPath); // 초기에는 전체 표시
});
}, []);
// parentId 기반으로 path 생성
const buildPaths = (data: Scenario[]): Scenario[] => {
const map = new Map<string, Scenario>();
data.forEach((item) => map.set(item.scenarioId, { ...item }));
const buildPath = (item: Scenario): string[] => {
const parent = item.parentId ? map.get(item.parentId) : null;
if (parent) {
return [...buildPath(parent), item.name];
}
return [item.name];
};
return data.map((item) => ({
...item,
path: buildPath(item),
}));
};
// 좌측 트리 컬럼 정의
const treeColDefs = useMemo<ColDef[]>(
() => [
{ field: "name", headerName: "Scenario", cellRenderer: "agGroupCellRenderer", flex: 1 },
],
[]
);
// 우측 목록 컬럼 정의
const listColDefs = useMemo<ColDef[]>(
() => [
{ field: "scenarioId", headerName: "Scenario ID", flex: 1 },
{ field: "name", headerName: "Name", flex: 1 },
{ field: "scenarioType", headerName: "Type", flex: 1 },
{ field: "url", headerName: "URL", flex: 2 },
],
[]
);
// 좌측 트리에서 노드 선택 → 우측 목록 필터링
const onTreeSelectionChanged = (event: any) => {
const selected: Scenario = event.api.getSelectedNodes()[0]?.data;
if (!selected) {
setRowData(allScenarios);
return;
}
// 선택 시나리오 및 하위 시나리오 찾기
const selectedPath = selected.path?.join(">");
const filtered = allScenarios.filter((item) =>
item.path?.join(">").startsWith(selectedPath!)
);
setRowData(filtered);
};
return (
<div style={{ padding: "10px" }}>
{/* 상단 조회 조건 */}
<div style={{ marginBottom: "10px" }}>
<label>System: </label>
<select>
<option>N-EDI Portal System(QA)</option>
</select>
<label style={{ marginLeft: "20px" }}>Language: </label>
<select>
<option>English Only</option>
</select>
<label style={{ marginLeft: "20px" }}>Enable: </label>
<select>
<option>All</option>
<option>Enabled</option>
<option>Disabled</option>
</select>
<button style={{ marginLeft: "20px" }}>Search</button>
</div>
{/* 좌/우 레이아웃 */}
<div style={{ display: "flex", height: "500px" }}>
{/* 좌측 트리 ag-Grid */}
<div className="ag-theme-alpine" style={{ width: "300px" }}>
<AgGridReact
rowData={treeData}
columnDefs={treeColDefs}
treeData={true}
animateRows={true}
groupDefaultExpanded={-1} // 전체 펼침
getDataPath={(data) => data.path!}
rowSelection="single"
onSelectionChanged={onTreeSelectionChanged}
/>
</div>
{/* 우측 목록 ag-Grid */}
<div className="ag-theme-alpine" style={{ flex: 1, marginLeft: "10px" }}>
<AgGridReact rowData={rowData} columnDefs={listColDefs} domLayout="autoHeight" />
</div>
</div>
</div>
);
};
export default ScenarioManagement;반응형