github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/ui/dashboard/src/hooks/usePanel.tsx (about)

     1  import usePanelControls from "./usePanelControls";
     2  import { BaseChartProps } from "../components/dashboards/charts/types";
     3  import { CardProps } from "../components/dashboards/Card";
     4  import {
     5    createContext,
     6    ReactNode,
     7    useContext,
     8    useEffect,
     9    useMemo,
    10    useState,
    11  } from "react";
    12  import {
    13    DashboardInputs,
    14    DashboardPanelType,
    15    DashboardRunState,
    16    PanelDefinition,
    17    PanelDependenciesByStatus,
    18    PanelsMap,
    19  } from "../types";
    20  import { FlowProps } from "../components/dashboards/flows/types";
    21  import { getNodeAndEdgeDataFormat } from "../components/dashboards/common/useNodeAndEdgeData";
    22  import { GraphProps } from "../components/dashboards/graphs/types";
    23  import { HierarchyProps } from "../components/dashboards/hierarchies/types";
    24  import { ImageProps } from "../components/dashboards/Image";
    25  import {
    26    InputProperties,
    27    InputProps,
    28  } from "../components/dashboards/inputs/types";
    29  import { IPanelControl } from "../components/dashboards/layout/Panel/PanelControls";
    30  import { NodeAndEdgeProperties } from "../components/dashboards/common/types";
    31  import { TableProps } from "../components/dashboards/Table";
    32  import { TextProps } from "../components/dashboards/Text";
    33  import { useDashboard } from "./useDashboard";
    34  import { useContainer } from "./useContainer";
    35  
    36  type IPanelContext = {
    37    definition:
    38      | BaseChartProps
    39      | CardProps
    40      | FlowProps
    41      | GraphProps
    42      | HierarchyProps
    43      | ImageProps
    44      | InputProps
    45      | PanelDefinition
    46      | TableProps
    47      | TextProps;
    48    dependencies: PanelDefinition[];
    49    dependenciesByStatus: PanelDependenciesByStatus;
    50    inputPanelsAwaitingValue: PanelDefinition[];
    51    panelControls: IPanelControl[];
    52    panelInformation: ReactNode | null;
    53    showPanelControls: boolean;
    54    showPanelInformation: boolean;
    55    setPanelInformation: (information: ReactNode) => void;
    56    setShowPanelControls: (show: boolean) => void;
    57    setShowPanelInformation: (show: boolean) => void;
    58  };
    59  
    60  type PanelProviderProps = {
    61    children: ReactNode;
    62    definition:
    63      | BaseChartProps
    64      | CardProps
    65      | FlowProps
    66      | GraphProps
    67      | HierarchyProps
    68      | ImageProps
    69      | InputProps
    70      | PanelDefinition
    71      | TableProps
    72      | TextProps;
    73    parentType: DashboardPanelType;
    74    showControls?: boolean;
    75  };
    76  
    77  const PanelContext = createContext<IPanelContext | null>(null);
    78  
    79  const recordDependency = (
    80    definition: PanelDefinition,
    81    panelsMap: PanelsMap,
    82    selectedDashboardInputs: DashboardInputs,
    83    dependencies: PanelDefinition[],
    84    dependenciesByStatus: PanelDependenciesByStatus,
    85    inputPanelsAwaitingValue: PanelDefinition[],
    86    recordedInputPanels: {}
    87  ) => {
    88    // Record this panel as a dependency
    89    dependencies.push(definition);
    90  
    91    // Keep track of this panel by its status
    92    const statuses =
    93      dependenciesByStatus[definition.status as DashboardRunState] || [];
    94    statuses.push(definition);
    95    dependenciesByStatus[definition.status as DashboardRunState] = statuses;
    96  
    97    // Is this panel an input? If so, does it have a value?
    98    const isInput = definition.panel_type === "input";
    99    const inputProperties = isInput
   100      ? (definition.properties as InputProperties)
   101      : null;
   102    const hasInputValue =
   103      isInput &&
   104      inputProperties?.unqualified_name &&
   105      !!selectedDashboardInputs[inputProperties?.unqualified_name];
   106    if (isInput && !hasInputValue && !recordedInputPanels[definition.name]) {
   107      inputPanelsAwaitingValue.push(definition);
   108      recordedInputPanels[definition.name] = definition;
   109    }
   110  
   111    for (const dependency of definition?.dependencies || []) {
   112      const dependencyPanel = panelsMap[dependency];
   113      if (!dependencyPanel || !dependencyPanel.status) {
   114        continue;
   115      }
   116      recordDependency(
   117        dependencyPanel,
   118        panelsMap,
   119        selectedDashboardInputs,
   120        dependencies,
   121        dependenciesByStatus,
   122        inputPanelsAwaitingValue,
   123        recordedInputPanels
   124      );
   125    }
   126  };
   127  
   128  const PanelProvider = ({
   129    children,
   130    definition,
   131    parentType,
   132    showControls,
   133  }: PanelProviderProps) => {
   134    const { updateChildStatus } = useContainer();
   135    const { selectedDashboardInputs, panelsMap } = useDashboard();
   136    const [showPanelControls, setShowPanelControls] = useState(false);
   137    const [showPanelInformation, setShowPanelInformation] = useState(false);
   138    const [panelInformation, setPanelInformation] = useState<ReactNode | null>(
   139      null
   140    );
   141    const { panelControls } = usePanelControls(definition, showControls);
   142    const { dependencies, dependenciesByStatus, inputPanelsAwaitingValue } =
   143      useMemo(() => {
   144        if (!definition) {
   145          return {
   146            dependencies: [],
   147            dependenciesByStatus: {},
   148            inputPanelsAwaitingValue: [],
   149          };
   150        }
   151        const dataFormat = getNodeAndEdgeDataFormat(
   152          definition.properties as NodeAndEdgeProperties
   153        );
   154        if (
   155          dataFormat === "LEGACY" &&
   156          (!definition.dependencies || definition.dependencies.length === 0)
   157        ) {
   158          return {
   159            dependencies: [],
   160            dependenciesByStatus: {},
   161            inputPanelsAwaitingValue: [],
   162          };
   163        }
   164        const dependencies: PanelDefinition[] = [];
   165        const dependenciesByStatus: PanelDependenciesByStatus = {};
   166        const inputPanelsAwaitingValue: PanelDefinition[] = [];
   167        const recordedInputPanels = {};
   168  
   169        if (dataFormat === "NODE_AND_EDGE") {
   170          const nodeAndEdgeProperties =
   171            definition.properties as NodeAndEdgeProperties;
   172          for (const node of nodeAndEdgeProperties.nodes || []) {
   173            const nodePanel = panelsMap[node];
   174            if (!nodePanel || !nodePanel.status) {
   175              continue;
   176            }
   177            recordDependency(
   178              nodePanel,
   179              panelsMap,
   180              selectedDashboardInputs,
   181              dependencies,
   182              dependenciesByStatus,
   183              inputPanelsAwaitingValue,
   184              recordedInputPanels
   185            );
   186          }
   187          for (const edge of nodeAndEdgeProperties.edges || []) {
   188            const edgePanel = panelsMap[edge];
   189            if (!edgePanel || !edgePanel.status) {
   190              continue;
   191            }
   192            recordDependency(
   193              edgePanel,
   194              panelsMap,
   195              selectedDashboardInputs,
   196              dependencies,
   197              dependenciesByStatus,
   198              inputPanelsAwaitingValue,
   199              recordedInputPanels
   200            );
   201          }
   202        }
   203  
   204        for (const dependency of definition.dependencies || []) {
   205          const dependencyPanel = panelsMap[dependency];
   206          if (!dependencyPanel || !dependencyPanel.status) {
   207            continue;
   208          }
   209          recordDependency(
   210            dependencyPanel,
   211            panelsMap,
   212            selectedDashboardInputs,
   213            dependencies,
   214            dependenciesByStatus,
   215            inputPanelsAwaitingValue,
   216            recordedInputPanels
   217          );
   218        }
   219  
   220        return { dependencies, dependenciesByStatus, inputPanelsAwaitingValue };
   221      }, [definition, panelsMap, selectedDashboardInputs]);
   222  
   223    useEffect(() => {
   224      if (parentType !== "container") {
   225        return;
   226      }
   227      updateChildStatus(
   228        definition as PanelDefinition,
   229        inputPanelsAwaitingValue.length === 0 ? "visible" : "hidden"
   230      );
   231    }, [definition, inputPanelsAwaitingValue, parentType, updateChildStatus]);
   232  
   233    return (
   234      <PanelContext.Provider
   235        value={{
   236          definition,
   237          dependencies,
   238          dependenciesByStatus,
   239          inputPanelsAwaitingValue,
   240          panelControls,
   241          panelInformation,
   242          showPanelControls,
   243          showPanelInformation,
   244          setPanelInformation,
   245          setShowPanelControls,
   246          setShowPanelInformation,
   247        }}
   248      >
   249        {children}
   250      </PanelContext.Provider>
   251    );
   252  };
   253  
   254  const usePanel = () => {
   255    const context = useContext(PanelContext);
   256    if (context === undefined) {
   257      throw new Error("usePanel must be used within a PanelContext");
   258    }
   259    return context as IPanelContext;
   260  };
   261  
   262  export { PanelContext, PanelProvider, usePanel };