본문 바로가기

카테고리 없음

react 17, typescript, ag-grid 트리 목록

반응형
[
  {
    "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;
반응형