codeant-ai-for-open-source[bot] commented on code in PR #40912:
URL: https://github.com/apache/superset/pull/40912#discussion_r3488891488


##########
superset-frontend/src/features/lineage/LineageView.tsx:
##########
@@ -0,0 +1,731 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { FC, useMemo, useState, useCallback } from 'react';
+import { t } from '@apache-superset/core/translation';
+import { styled, useTheme } from '@apache-superset/core/theme';
+import { Empty, Loading } from '@superset-ui/core/components';
+import { Button } from '@superset-ui/core/components';
+import { ResourceStatus } from 'src/hooks/apiResources/apiResources';
+import type { Resource } from 'src/hooks/apiResources/apiResources';
+import type {
+  DatasetLineage,
+  ChartLineage,
+  DashboardLineage,
+  ChartEntity,
+  DashboardEntity,
+  DatasetEntity,
+  DatabaseEntity,
+} from 'src/hooks/apiResources/lineage';
+import Echart from 
'../../../plugins/plugin-chart-echarts/src/components/Echart';
+import type { EChartsCoreOption } from 'echarts/core';
+
+const LineageContainer = styled.div`
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: 100%;
+`;
+
+const Legend = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    gap: ${theme.sizeUnit * 4}px;
+    padding: ${theme.sizeUnit * 3}px;
+    background-color: ${theme.colorBgLayout};
+    border-bottom: 1px solid ${theme.colorBorder};
+  `}
+`;
+
+const LegendItem = styled.div<{ color: string }>`
+  ${({ theme, color }) => `
+    display: flex;
+    align-items: center;
+    gap: ${theme.sizeUnit * 2}px;
+    font-size: ${theme.fontSizeSM}px;
+    color: ${theme.colorText};
+
+    &::before {
+      content: '';
+      width: 12px;
+      height: 12px;
+      border-radius: 2px;
+      background-color: ${color};
+    }
+  `}
+`;
+
+const DetailsPanel = styled.div`
+  ${({ theme }) => `
+    padding: ${theme.sizeUnit * 4}px;
+    background-color: ${theme.colorBgLayout};
+    border-top: 1px solid ${theme.colorBorder};
+    min-height: 120px;
+  `}
+`;
+
+const DetailsPanelHeader = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: ${theme.sizeUnit * 3}px;
+  `}
+`;
+
+const DetailsPanelActions = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    gap: ${theme.sizeUnit * 2}px;
+  `}
+`;
+
+const DetailsPanelTitle = styled.h4`
+  ${({ theme }) => `
+    margin: 0;
+    font-size: ${theme.fontSizeLG}px;
+    font-weight: ${theme.fontWeightStrong};
+    color: ${theme.colorText};
+  `}
+`;
+
+const DetailsPanelContent = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    flex-direction: column;
+    gap: ${theme.sizeUnit * 2}px;
+  `}
+`;
+
+const DetailRow = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    gap: ${theme.sizeUnit * 2}px;
+    font-size: ${theme.fontSizeSM}px;
+    color: ${theme.colorText};
+  `}
+`;
+
+const DetailLabel = styled.span`
+  ${({ theme }) => `
+    font-weight: ${theme.fontWeightStrong};
+    min-width: 100px;
+  `}
+`;
+
+const DetailValue = styled.span`
+  ${({ theme }) => `
+    color: ${theme.colorTextSecondary};
+  `}
+`;
+
+type NodeType = 'database' | 'dataset' | 'chart' | 'dashboard';
+
+type NodeDetails = {
+  name: string;
+  type: NodeType;
+  id?: number;
+  additionalInfo?: Record<string, string | number | null | undefined>;
+};
+
+// Build a stable, unique graph identity for a node so that entities sharing 
the
+// same display name (e.g. two charts with identical titles) never collapse 
into
+// a single Sankey node. The human-readable name is kept separately as the 
label.
+const nodeKey = (type: NodeType, id?: number, name?: string): string =>
+  id != null ? `${type}:${id}` : `${type}:${name ?? ''}`;
+
+type LineageViewProps = {
+  lineageResource:
+    | Resource<DatasetLineage>
+    | Resource<ChartLineage>
+    | Resource<DashboardLineage>;
+  entityType: 'dataset' | 'chart' | 'dashboard';
+};
+
+const LineageView: FC<LineageViewProps> = ({ lineageResource, entityType }) => 
{
+  const theme = useTheme();
+  const [selectedNode, setSelectedNode] = useState<NodeDetails | null>(null);
+
+  // Create a mapping of node names to their details
+  const nodeDetailsMap = useMemo(() => {
+    if (
+      lineageResource.status !== ResourceStatus.Complete ||
+      !lineageResource.result
+    ) {
+      return new Map<string, NodeDetails>();
+    }
+
+    const data = lineageResource.result;
+    const map = new Map<string, NodeDetails>();
+
+    if (entityType === 'dataset' && 'dataset' in data) {
+      const { dataset, upstream, downstream } = data as DatasetLineage;
+
+      // Add current dataset
+      map.set(nodeKey('dataset', dataset.id, dataset.name), {
+        name: dataset.name,
+        type: 'dataset',
+        id: dataset.id,
+        additionalInfo: {
+          schema: dataset.schema,
+          table_name: dataset.table_name,
+          database_name: dataset.database_name,
+        },
+      });
+
+      // Add upstream database
+      if (upstream?.database) {
+        map.set(
+          nodeKey(
+            'database',
+            upstream.database.id,
+            upstream.database.database_name,
+          ),
+          {
+            name: upstream.database.database_name,
+            type: 'database',
+            id: upstream.database.id,
+          },
+        );
+      }
+
+      // Add downstream charts
+      if (downstream?.charts?.result) {
+        downstream.charts.result.forEach((chart: ChartEntity) => {
+          map.set(nodeKey('chart', chart.id, chart.slice_name), {
+            name: chart.slice_name,
+            type: 'chart',
+            id: chart.id,
+            additionalInfo: {
+              viz_type: chart.viz_type,
+            },
+          });
+        });
+      }
+
+      // Add downstream dashboards
+      if (downstream?.dashboards?.result) {
+        downstream.dashboards.result.forEach((dashboard: DashboardEntity) => {
+          map.set(nodeKey('dashboard', dashboard.id, dashboard.title), {
+            name: dashboard.title,
+            type: 'dashboard',
+            id: dashboard.id,
+            additionalInfo: {
+              slug: dashboard.slug,
+            },
+          });
+        });
+      }
+    } else if (entityType === 'chart' && 'chart' in data) {
+      const { chart, upstream, downstream } = data as ChartLineage;
+
+      // Add current chart
+      map.set(nodeKey('chart', chart.id, chart.slice_name), {
+        name: chart.slice_name,
+        type: 'chart',
+        id: chart.id,
+        additionalInfo: {
+          viz_type: chart.viz_type,
+        },
+      });
+
+      // Add upstream dataset
+      if (upstream?.dataset) {
+        map.set(
+          nodeKey('dataset', upstream.dataset.id, upstream.dataset.name),
+          {
+            name: upstream.dataset.name,
+            type: 'dataset',
+            id: upstream.dataset.id,
+            additionalInfo: {
+              schema: upstream.dataset.schema,
+              table_name: upstream.dataset.table_name,
+            },
+          },
+        );
+      }
+
+      // Add upstream database
+      if (upstream?.database) {
+        map.set(
+          nodeKey(
+            'database',
+            upstream.database.id,
+            upstream.database.database_name,
+          ),
+          {
+            name: upstream.database.database_name,
+            type: 'database',
+            id: upstream.database.id,
+          },
+        );
+      }
+
+      // Add downstream dashboards
+      if (downstream?.dashboards?.result) {
+        downstream.dashboards.result.forEach((dashboard: DashboardEntity) => {
+          map.set(nodeKey('dashboard', dashboard.id, dashboard.title), {
+            name: dashboard.title,
+            type: 'dashboard',
+            id: dashboard.id,
+            additionalInfo: {
+              slug: dashboard.slug,
+            },
+          });
+        });
+      }
+    } else if (entityType === 'dashboard' && 'dashboard' in data) {
+      const { dashboard, upstream } = data as DashboardLineage;
+
+      // Add current dashboard
+      map.set(nodeKey('dashboard', dashboard.id, dashboard.title), {
+        name: dashboard.title,
+        type: 'dashboard',
+        id: dashboard.id,
+        additionalInfo: {
+          slug: dashboard.slug,
+        },
+      });
+
+      // Add upstream charts
+      if (upstream?.charts?.result) {
+        upstream.charts.result.forEach((chart: ChartEntity) => {
+          map.set(nodeKey('chart', chart.id, chart.slice_name), {
+            name: chart.slice_name,
+            type: 'chart',
+            id: chart.id,
+            additionalInfo: {
+              viz_type: chart.viz_type,
+            },
+          });
+        });
+      }
+
+      // Add upstream datasets
+      if (upstream?.datasets?.result) {
+        upstream.datasets.result.forEach((dataset: DatasetEntity) => {
+          map.set(nodeKey('dataset', dataset.id, dataset.name), {
+            name: dataset.name,
+            type: 'dataset',
+            id: dataset.id,
+            additionalInfo: {
+              schema: dataset.schema,
+              table_name: dataset.table_name,
+            },
+          });
+        });
+      }
+
+      // Add upstream databases
+      if (upstream?.databases?.result) {
+        upstream.databases.result.forEach((database: DatabaseEntity) => {
+          map.set(nodeKey('database', database.id, database.database_name), {
+            name: database.database_name,
+            type: 'database',
+            id: database.id,
+          });
+        });
+      }
+    }
+
+    return map;
+  }, [lineageResource, entityType]);
+
+  // Handle node click
+  const handleNodeClick = useCallback(
+    (params: {
+      dataType?: string;
+      name?: string;
+      event?: { stop: () => void };
+    }) => {
+      if (params.dataType === 'node' && params.name) {
+        const nodeDetails = nodeDetailsMap.get(params.name);
+        if (nodeDetails) {
+          setSelectedNode(nodeDetails);
+        }
+      }
+      // Always stop event propagation to prevent tooltip issues
+      if (params.event) {
+        params.event.stop();
+      }
+    },
+    [nodeDetailsMap],
+  );
+
+  const echartOptions: EChartsCoreOption | null = useMemo(() => {
+    if (
+      lineageResource.status !== ResourceStatus.Complete ||
+      !lineageResource.result
+    ) {
+      return null;
+    }
+
+    const data = lineageResource.result;
+    const nodes: {
+      name: string;
+      label?: { position?: string; formatter?: string };
+      itemStyle?: { color: string };
+    }[] = [];
+    const links: { source: string; target: string; value: number }[] = [];
+    const nodeSet = new Set<string>();
+
+    // Helper to add a node. `key` is the stable unique identity used for graph
+    // links and detail lookups; `label` is the human-readable text shown.
+    const addNode = (
+      key: string,
+      label: string,
+      color: string,
+      labelPosition: 'left' | 'right' | 'inside',
+    ) => {
+      if (!nodeSet.has(key)) {
+        nodeSet.add(key);
+        nodes.push({
+          name: key,
+          itemStyle: { color },
+          label: {
+            position: labelPosition,
+            formatter: label,
+          },
+        });
+      }
+    };
+
+    // Helper to add a link between two node keys
+    const addLink = (source: string, target: string) => {
+      links.push({ source, target, value: 1 });
+    };
+
+    // Build nodes and links based on entity type
+    if (entityType === 'dataset' && 'dataset' in data) {
+      const { dataset, upstream, downstream } = data as DatasetLineage;
+
+      const datasetKey = nodeKey('dataset', dataset.id, dataset.name);
+      // Add current dataset node (center) - label inside
+      addNode(datasetKey, dataset.name, theme.colorPrimary, 'inside');
+
+      // Add upstream database - label on left
+      if (upstream?.database) {
+        const dbKey = nodeKey(
+          'database',
+          upstream.database.id,
+          upstream.database.database_name,
+        );
+        addNode(
+          dbKey,
+          upstream.database.database_name,
+          theme.colorInfo,
+          'left',
+        );
+        addLink(dbKey, datasetKey);
+      }
+
+      // Add downstream charts - label on right
+      const chartKeys = new Map<number, string>();
+      if (downstream?.charts?.result) {
+        downstream.charts.result.forEach((chart: ChartEntity) => {
+          const chartKey = nodeKey('chart', chart.id, chart.slice_name);
+          chartKeys.set(chart.id, chartKey);
+          addNode(chartKey, chart.slice_name, theme.colorSuccess, 'right');
+          addLink(datasetKey, chartKey);
+        });
+      }
+
+      // Add downstream dashboards - label on right
+      if (downstream?.dashboards?.result) {
+        downstream.dashboards.result.forEach((dashboard: DashboardEntity) => {
+          const dashKey = nodeKey('dashboard', dashboard.id, dashboard.title);
+          addNode(dashKey, dashboard.title, theme.colorWarning, 'right');
+
+          // Link from charts to dashboards using chart_ids
+          if (dashboard.chart_ids && dashboard.chart_ids.length > 0) {
+            dashboard.chart_ids.forEach(chartId => {
+              const chartKey = chartKeys.get(chartId);
+              if (chartKey) {
+                addLink(chartKey, dashKey);
+              }
+            });
+          }
+        });
+      }
+    } else if (entityType === 'chart' && 'chart' in data) {
+      const { chart, upstream, downstream } = data as ChartLineage;
+
+      const chartKey = nodeKey('chart', chart.id, chart.slice_name);
+      // Add current chart node (center) - label inside
+      addNode(chartKey, chart.slice_name, theme.colorPrimary, 'inside');
+
+      // Add upstream dataset - label on left
+      if (upstream?.dataset) {
+        const datasetKey = nodeKey(
+          'dataset',
+          upstream.dataset.id,
+          upstream.dataset.name,
+        );
+        addNode(datasetKey, upstream.dataset.name, theme.colorInfo, 'left');
+        addLink(datasetKey, chartKey);
+
+        // Add upstream database - label on left
+        if (upstream.database) {
+          const dbKey = nodeKey(
+            'database',
+            upstream.database.id,
+            upstream.database.database_name,
+          );
+          addNode(
+            dbKey,
+            upstream.database.database_name,
+            theme.colorWarning,
+            'left',
+          );
+          addLink(dbKey, datasetKey);
+        }
+      }
+
+      // Add downstream dashboards - label on right
+      if (downstream?.dashboards?.result) {
+        downstream.dashboards.result.forEach((dashboard: DashboardEntity) => {
+          const dashKey = nodeKey('dashboard', dashboard.id, dashboard.title);
+          addNode(dashKey, dashboard.title, theme.colorSuccess, 'right');
+          addLink(chartKey, dashKey);
+        });
+      }
+    } else if (entityType === 'dashboard' && 'dashboard' in data) {
+      const { dashboard, upstream } = data as DashboardLineage;
+
+      const dashKey = nodeKey('dashboard', dashboard.id, dashboard.title);
+      // Add current dashboard node (right) - label inside
+      addNode(dashKey, dashboard.title, theme.colorPrimary, 'inside');
+
+      // Add upstream charts - label on left
+      const chartKeys = new Map<number, string>();
+      if (upstream?.charts?.result) {
+        upstream.charts.result.forEach((chart: ChartEntity) => {
+          const chartKey = nodeKey('chart', chart.id, chart.slice_name);
+          chartKeys.set(chart.id, chartKey);
+          addNode(chartKey, chart.slice_name, theme.colorInfo, 'left');
+          addLink(chartKey, dashKey);
+        });
+      }
+
+      // Add upstream datasets - label on left
+      const datasetKeys = new Map<number, string>();
+      if (upstream?.datasets?.result) {
+        upstream.datasets.result.forEach(dataset => {
+          const datasetKey = nodeKey('dataset', dataset.id, dataset.name);
+          datasetKeys.set(dataset.id, datasetKey);
+          addNode(datasetKey, dataset.name, theme.colorSuccess, 'left');
+        });
+      }
+
+      // Link charts to their specific datasets using dataset_id from each 
chart
+      if (upstream?.charts?.result) {
+        upstream.charts.result.forEach((chart: ChartEntity) => {
+          if (chart.dataset_id) {
+            const datasetKey = datasetKeys.get(chart.dataset_id);
+            const chartKey = chartKeys.get(chart.id);
+            if (datasetKey && chartKey) {
+              addLink(datasetKey, chartKey);
+            }
+          }
+        });
+      }
+
+      // Add upstream databases and link to their specific datasets
+      if (upstream?.databases?.result) {
+        upstream.databases.result.forEach(database => {
+          const dbKey = nodeKey(
+            'database',
+            database.id,
+            database.database_name,
+          );
+          addNode(dbKey, database.database_name, theme.colorWarning, 'left');
+
+          // Link databases to datasets that belong to them using database_id
+          if (upstream.datasets?.result) {
+            upstream.datasets.result.forEach(dataset => {
+              if (dataset.database_id === database.id) {
+                const datasetKey = datasetKeys.get(dataset.id);
+                if (datasetKey) {
+                  addLink(dbKey, datasetKey);
+                }
+              }
+            });
+          }
+        });
+      }
+    }
+
+    return {
+      series: {
+        animation: false,
+        data: nodes,
+        lineStyle: {
+          color: 'source',
+        },
+        links,
+        type: 'sankey',
+      },
+      tooltip: {
+        show: false,
+      },
+    };
+  }, [lineageResource, entityType, theme]);
+
+  // Build legend data based on entity type
+  const legendItems: { label: string; color: string }[] = useMemo(() => {
+    if (entityType === 'dataset') {
+      return [
+        { label: 'Database (Upstream)', color: theme.colorInfo },
+        { label: 'Dataset (Current)', color: theme.colorPrimary },
+        { label: 'Chart (Downstream)', color: theme.colorSuccess },
+        { label: 'Dashboard (Downstream)', color: theme.colorWarning },
+      ];
+    } else if (entityType === 'chart') {
+      return [
+        { label: 'Database (Upstream)', color: theme.colorWarning },
+        { label: 'Dataset (Upstream)', color: theme.colorInfo },
+        { label: 'Chart (Current)', color: theme.colorPrimary },
+        { label: 'Dashboard (Downstream)', color: theme.colorSuccess },
+      ];
+    } else if (entityType === 'dashboard') {
+      return [
+        { label: 'Database (Upstream)', color: theme.colorWarning },
+        { label: 'Dataset (Upstream)', color: theme.colorSuccess },
+        { label: 'Chart (Upstream)', color: theme.colorInfo },
+        { label: 'Dashboard (Current)', color: theme.colorPrimary },
+      ];
+    }
+    return [];
+  }, [entityType, theme]);
+
+  if (lineageResource.status === ResourceStatus.Loading) {
+    return <Loading />;
+  }
+
+  if (
+    lineageResource.status === ResourceStatus.Error ||
+    !lineageResource.result
+  ) {
+    return <Empty description={t('Failed to load lineage data')} />;
+  }
+
+  if (!echartOptions) {
+    return <Empty description={t('No lineage data available')} />;
+  }
+
+  // Helper function to get the URL for an entity
+  const getEntityUrl = (nodeDetails: NodeDetails): string => {
+    switch (nodeDetails.type) {
+      case 'dashboard':
+        return `/superset/dashboard/${nodeDetails.id}/`;
+      case 'chart':
+        return `/explore/?slice_id=${nodeDetails.id}`;
+      case 'dataset':
+        return `/dataset/${nodeDetails.id}`;
+      default:
+        return '#';
+    }
+  };
+
+  return (
+    <LineageContainer>
+      <Legend>
+        {legendItems.map(item => (
+          <LegendItem key={item.label} color={item.color}>
+            {item.label}
+          </LegendItem>
+        ))}
+      </Legend>
+      <Echart
+        refs={{}}
+        height={selectedNode ? 450 : 600}
+        width={800}
+        echartOptions={echartOptions}

Review Comment:
   **Suggestion:** The chart width is hardcoded to 800px, which breaks 
responsive layout in narrower containers (notably embedded/tabbed contexts) by 
causing overflow and clipped content. Use container-driven sizing so the Sankey 
adapts to available width instead of forcing a fixed pixel width. [css layout 
issue]
   
   <details>
   <summary><b>Severity Level:</b> Minor 🧹</summary>
   
   ```mdx
   - ⚠️ Lineage chart overflows in narrow dataset tabs.
   - ⚠️ Lineage modal layout can clip chart content.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. In `superset-frontend/src/features/lineage/LineageView.tsx`, inspect the 
JSX return
   near the bottom of the component (lines 129-147): the `LineageContainer` 
styled div sets
   `display: flex` and `width: 100%`, meaning the lineage view is intended to 
stretch to its
   parent container size.
   
   2. Just below the legend, the component renders an `Echart` (lines 138-147) 
with props
   `height={selectedNode ? 450 : 600}` and a hardcoded `width={800}`, so the 
chart canvas
   always reserves 800px width regardless of the available horizontal space in 
its container.
   
   3. `LineageView` is used in multiple contexts: the dataset edit page
   (`superset-frontend/src/features/datasets/AddDataset/EditDataset/index.tsx`, 
lines
   101-105) embeds it as a tab content within Ant Design `Tabs`, and 
`LineageModal`
   (`superset-frontend/src/features/lineage/LineageModal.tsx`, lines 62-73) 
embeds it inside
   a `ModalTrigger` with `width="850px"` and `responsive` set, which will often 
be narrower
   on small screens or when sidebars are present.
   
   4. When the parent container is narrower than 800px (for example, a narrower 
browser
   window, a dataset edit tab with side panels, or an embedded dashboard using 
the lineage
   modal), the fixed 800px width causes the Sankey chart to overflow its 
container, leading
   to horizontal scrollbars or clipped content instead of resizing to fit; 
allowing the
   `Echart` width to be driven by the container (e.g., width 100% or leaving 
width undefined)
   would avoid this layout issue.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=8f16405301374be3a0dac429b27333fb&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=8f16405301374be3a0dac429b27333fb&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/features/lineage/LineageView.tsx
   **Line:** 660:660
   **Comment:**
        *Css Layout Issue: The chart width is hardcoded to 800px, which breaks 
responsive layout in narrower containers (notably embedded/tabbed contexts) by 
causing overflow and clipped content. Use container-driven sizing so the Sankey 
adapts to available width instead of forcing a fixed pixel width.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=d3d4245657812148456d97a233a1b55a388d7d83df1386529057fcc86c985e47&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=d3d4245657812148456d97a233a1b55a388d7d83df1386529057fcc86c985e47&reaction=dislike'>👎</a>



##########
superset-frontend/src/features/lineage/LineageView.tsx:
##########
@@ -0,0 +1,731 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { FC, useMemo, useState, useCallback } from 'react';
+import { t } from '@apache-superset/core/translation';
+import { styled, useTheme } from '@apache-superset/core/theme';
+import { Empty, Loading } from '@superset-ui/core/components';
+import { Button } from '@superset-ui/core/components';
+import { ResourceStatus } from 'src/hooks/apiResources/apiResources';
+import type { Resource } from 'src/hooks/apiResources/apiResources';
+import type {
+  DatasetLineage,
+  ChartLineage,
+  DashboardLineage,
+  ChartEntity,
+  DashboardEntity,
+  DatasetEntity,
+  DatabaseEntity,
+} from 'src/hooks/apiResources/lineage';
+import Echart from 
'../../../plugins/plugin-chart-echarts/src/components/Echart';
+import type { EChartsCoreOption } from 'echarts/core';
+
+const LineageContainer = styled.div`
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: 100%;
+`;
+
+const Legend = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    gap: ${theme.sizeUnit * 4}px;
+    padding: ${theme.sizeUnit * 3}px;
+    background-color: ${theme.colorBgLayout};
+    border-bottom: 1px solid ${theme.colorBorder};
+  `}
+`;
+
+const LegendItem = styled.div<{ color: string }>`
+  ${({ theme, color }) => `
+    display: flex;
+    align-items: center;
+    gap: ${theme.sizeUnit * 2}px;
+    font-size: ${theme.fontSizeSM}px;
+    color: ${theme.colorText};
+
+    &::before {
+      content: '';
+      width: 12px;
+      height: 12px;
+      border-radius: 2px;
+      background-color: ${color};
+    }
+  `}
+`;
+
+const DetailsPanel = styled.div`
+  ${({ theme }) => `
+    padding: ${theme.sizeUnit * 4}px;
+    background-color: ${theme.colorBgLayout};
+    border-top: 1px solid ${theme.colorBorder};
+    min-height: 120px;
+  `}
+`;
+
+const DetailsPanelHeader = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: ${theme.sizeUnit * 3}px;
+  `}
+`;
+
+const DetailsPanelActions = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    gap: ${theme.sizeUnit * 2}px;
+  `}
+`;
+
+const DetailsPanelTitle = styled.h4`
+  ${({ theme }) => `
+    margin: 0;
+    font-size: ${theme.fontSizeLG}px;
+    font-weight: ${theme.fontWeightStrong};
+    color: ${theme.colorText};
+  `}
+`;
+
+const DetailsPanelContent = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    flex-direction: column;
+    gap: ${theme.sizeUnit * 2}px;
+  `}
+`;
+
+const DetailRow = styled.div`
+  ${({ theme }) => `
+    display: flex;
+    gap: ${theme.sizeUnit * 2}px;
+    font-size: ${theme.fontSizeSM}px;
+    color: ${theme.colorText};
+  `}
+`;
+
+const DetailLabel = styled.span`
+  ${({ theme }) => `
+    font-weight: ${theme.fontWeightStrong};
+    min-width: 100px;
+  `}
+`;
+
+const DetailValue = styled.span`
+  ${({ theme }) => `
+    color: ${theme.colorTextSecondary};
+  `}
+`;
+
+type NodeType = 'database' | 'dataset' | 'chart' | 'dashboard';
+
+type NodeDetails = {
+  name: string;
+  type: NodeType;
+  id?: number;
+  additionalInfo?: Record<string, string | number | null | undefined>;
+};
+
+// Build a stable, unique graph identity for a node so that entities sharing 
the
+// same display name (e.g. two charts with identical titles) never collapse 
into
+// a single Sankey node. The human-readable name is kept separately as the 
label.
+const nodeKey = (type: NodeType, id?: number, name?: string): string =>
+  id != null ? `${type}:${id}` : `${type}:${name ?? ''}`;
+
+type LineageViewProps = {
+  lineageResource:
+    | Resource<DatasetLineage>
+    | Resource<ChartLineage>
+    | Resource<DashboardLineage>;
+  entityType: 'dataset' | 'chart' | 'dashboard';
+};
+
+const LineageView: FC<LineageViewProps> = ({ lineageResource, entityType }) => 
{
+  const theme = useTheme();
+  const [selectedNode, setSelectedNode] = useState<NodeDetails | null>(null);
+
+  // Create a mapping of node names to their details
+  const nodeDetailsMap = useMemo(() => {
+    if (
+      lineageResource.status !== ResourceStatus.Complete ||
+      !lineageResource.result
+    ) {
+      return new Map<string, NodeDetails>();
+    }
+
+    const data = lineageResource.result;
+    const map = new Map<string, NodeDetails>();
+
+    if (entityType === 'dataset' && 'dataset' in data) {
+      const { dataset, upstream, downstream } = data as DatasetLineage;
+
+      // Add current dataset
+      map.set(nodeKey('dataset', dataset.id, dataset.name), {
+        name: dataset.name,
+        type: 'dataset',
+        id: dataset.id,
+        additionalInfo: {
+          schema: dataset.schema,
+          table_name: dataset.table_name,
+          database_name: dataset.database_name,
+        },
+      });
+
+      // Add upstream database
+      if (upstream?.database) {
+        map.set(
+          nodeKey(
+            'database',
+            upstream.database.id,
+            upstream.database.database_name,
+          ),
+          {
+            name: upstream.database.database_name,
+            type: 'database',
+            id: upstream.database.id,
+          },
+        );
+      }
+
+      // Add downstream charts
+      if (downstream?.charts?.result) {
+        downstream.charts.result.forEach((chart: ChartEntity) => {
+          map.set(nodeKey('chart', chart.id, chart.slice_name), {
+            name: chart.slice_name,
+            type: 'chart',
+            id: chart.id,
+            additionalInfo: {
+              viz_type: chart.viz_type,
+            },
+          });
+        });
+      }
+
+      // Add downstream dashboards
+      if (downstream?.dashboards?.result) {
+        downstream.dashboards.result.forEach((dashboard: DashboardEntity) => {
+          map.set(nodeKey('dashboard', dashboard.id, dashboard.title), {
+            name: dashboard.title,
+            type: 'dashboard',
+            id: dashboard.id,
+            additionalInfo: {
+              slug: dashboard.slug,
+            },
+          });
+        });
+      }
+    } else if (entityType === 'chart' && 'chart' in data) {
+      const { chart, upstream, downstream } = data as ChartLineage;
+
+      // Add current chart
+      map.set(nodeKey('chart', chart.id, chart.slice_name), {
+        name: chart.slice_name,
+        type: 'chart',
+        id: chart.id,
+        additionalInfo: {
+          viz_type: chart.viz_type,
+        },
+      });
+
+      // Add upstream dataset
+      if (upstream?.dataset) {
+        map.set(
+          nodeKey('dataset', upstream.dataset.id, upstream.dataset.name),
+          {
+            name: upstream.dataset.name,
+            type: 'dataset',
+            id: upstream.dataset.id,
+            additionalInfo: {
+              schema: upstream.dataset.schema,
+              table_name: upstream.dataset.table_name,
+            },
+          },
+        );
+      }
+
+      // Add upstream database
+      if (upstream?.database) {
+        map.set(
+          nodeKey(
+            'database',
+            upstream.database.id,
+            upstream.database.database_name,
+          ),
+          {
+            name: upstream.database.database_name,
+            type: 'database',
+            id: upstream.database.id,
+          },
+        );
+      }
+
+      // Add downstream dashboards
+      if (downstream?.dashboards?.result) {
+        downstream.dashboards.result.forEach((dashboard: DashboardEntity) => {
+          map.set(nodeKey('dashboard', dashboard.id, dashboard.title), {
+            name: dashboard.title,
+            type: 'dashboard',
+            id: dashboard.id,
+            additionalInfo: {
+              slug: dashboard.slug,
+            },
+          });
+        });
+      }
+    } else if (entityType === 'dashboard' && 'dashboard' in data) {
+      const { dashboard, upstream } = data as DashboardLineage;
+
+      // Add current dashboard
+      map.set(nodeKey('dashboard', dashboard.id, dashboard.title), {
+        name: dashboard.title,
+        type: 'dashboard',
+        id: dashboard.id,
+        additionalInfo: {
+          slug: dashboard.slug,
+        },
+      });
+
+      // Add upstream charts
+      if (upstream?.charts?.result) {
+        upstream.charts.result.forEach((chart: ChartEntity) => {
+          map.set(nodeKey('chart', chart.id, chart.slice_name), {
+            name: chart.slice_name,
+            type: 'chart',
+            id: chart.id,
+            additionalInfo: {
+              viz_type: chart.viz_type,
+            },
+          });
+        });
+      }
+
+      // Add upstream datasets
+      if (upstream?.datasets?.result) {
+        upstream.datasets.result.forEach((dataset: DatasetEntity) => {
+          map.set(nodeKey('dataset', dataset.id, dataset.name), {
+            name: dataset.name,
+            type: 'dataset',
+            id: dataset.id,
+            additionalInfo: {
+              schema: dataset.schema,
+              table_name: dataset.table_name,
+            },
+          });
+        });
+      }
+
+      // Add upstream databases
+      if (upstream?.databases?.result) {
+        upstream.databases.result.forEach((database: DatabaseEntity) => {
+          map.set(nodeKey('database', database.id, database.database_name), {
+            name: database.database_name,
+            type: 'database',
+            id: database.id,
+          });
+        });
+      }
+    }
+
+    return map;
+  }, [lineageResource, entityType]);
+
+  // Handle node click
+  const handleNodeClick = useCallback(
+    (params: {
+      dataType?: string;
+      name?: string;
+      event?: { stop: () => void };
+    }) => {
+      if (params.dataType === 'node' && params.name) {
+        const nodeDetails = nodeDetailsMap.get(params.name);
+        if (nodeDetails) {
+          setSelectedNode(nodeDetails);
+        }
+      }
+      // Always stop event propagation to prevent tooltip issues
+      if (params.event) {
+        params.event.stop();
+      }
+    },
+    [nodeDetailsMap],
+  );
+
+  const echartOptions: EChartsCoreOption | null = useMemo(() => {
+    if (
+      lineageResource.status !== ResourceStatus.Complete ||
+      !lineageResource.result
+    ) {
+      return null;
+    }
+
+    const data = lineageResource.result;
+    const nodes: {
+      name: string;
+      label?: { position?: string; formatter?: string };
+      itemStyle?: { color: string };
+    }[] = [];
+    const links: { source: string; target: string; value: number }[] = [];
+    const nodeSet = new Set<string>();
+
+    // Helper to add a node. `key` is the stable unique identity used for graph
+    // links and detail lookups; `label` is the human-readable text shown.
+    const addNode = (
+      key: string,
+      label: string,
+      color: string,
+      labelPosition: 'left' | 'right' | 'inside',
+    ) => {
+      if (!nodeSet.has(key)) {
+        nodeSet.add(key);
+        nodes.push({
+          name: key,
+          itemStyle: { color },
+          label: {
+            position: labelPosition,
+            formatter: label,
+          },
+        });
+      }
+    };
+
+    // Helper to add a link between two node keys
+    const addLink = (source: string, target: string) => {
+      links.push({ source, target, value: 1 });
+    };
+
+    // Build nodes and links based on entity type
+    if (entityType === 'dataset' && 'dataset' in data) {
+      const { dataset, upstream, downstream } = data as DatasetLineage;
+
+      const datasetKey = nodeKey('dataset', dataset.id, dataset.name);
+      // Add current dataset node (center) - label inside
+      addNode(datasetKey, dataset.name, theme.colorPrimary, 'inside');
+
+      // Add upstream database - label on left
+      if (upstream?.database) {
+        const dbKey = nodeKey(
+          'database',
+          upstream.database.id,
+          upstream.database.database_name,
+        );
+        addNode(
+          dbKey,
+          upstream.database.database_name,
+          theme.colorInfo,
+          'left',
+        );
+        addLink(dbKey, datasetKey);
+      }
+
+      // Add downstream charts - label on right
+      const chartKeys = new Map<number, string>();
+      if (downstream?.charts?.result) {
+        downstream.charts.result.forEach((chart: ChartEntity) => {
+          const chartKey = nodeKey('chart', chart.id, chart.slice_name);
+          chartKeys.set(chart.id, chartKey);
+          addNode(chartKey, chart.slice_name, theme.colorSuccess, 'right');
+          addLink(datasetKey, chartKey);
+        });
+      }
+
+      // Add downstream dashboards - label on right
+      if (downstream?.dashboards?.result) {
+        downstream.dashboards.result.forEach((dashboard: DashboardEntity) => {
+          const dashKey = nodeKey('dashboard', dashboard.id, dashboard.title);
+          addNode(dashKey, dashboard.title, theme.colorWarning, 'right');
+
+          // Link from charts to dashboards using chart_ids
+          if (dashboard.chart_ids && dashboard.chart_ids.length > 0) {
+            dashboard.chart_ids.forEach(chartId => {
+              const chartKey = chartKeys.get(chartId);
+              if (chartKey) {
+                addLink(chartKey, dashKey);
+              }
+            });

Review Comment:
   **Suggestion:** This only creates dashboard edges via `chart_ids` 
intersections with visible chart nodes, so dashboards become orphaned whenever 
none of their `chart_ids` resolve to rendered chart nodes (for example after 
permission filtering). In Sankey, orphaned nodes may not appear, so downstream 
dashboards can disappear even though they are present in the API response. Add 
a fallback edge (for example dataset-to-dashboard in dataset lineage) when no 
chart-based edge is created. [incomplete implementation]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ Dataset lineage view may omit downstream dashboards.
   - ⚠️ Lineage visualization can misrepresent dashboard dependencies.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Examine the dataset lineage graph construction in
   `superset-frontend/src/features/lineage/LineageView.tsx`: in the 
`echartOptions` `useMemo`
   block, when `entityType === 'dataset'` (lines 157-207 of the file), the code 
builds
   `chartKeys` from `downstream.charts.result` (lines 181-187) and then 
iterates over
   `downstream.dashboards.result` to add dashboard nodes (lines 191-195).
   
   2. In that dashboards loop (lines 191-205), the snippet at lines 197-205 
(corresponding to
   the provided existing_code) only adds a `links` entry when 
`dashboard.chart_ids` contains
   ids that are found in `chartKeys`: it checks `if (dashboard.chart_ids &&
   dashboard.chart_ids.length > 0)` and then, for each `chartId`, looks up
   `chartKeys.get(chartId)` and calls `addLink(chartKey, dashKey)` only when a 
matching chart
   node exists.
   
   3. The lineage API types in 
`superset-frontend/src/hooks/apiResources/lineage.ts` (lines
   57-73) show that `DatasetLineage.downstream.dashboards` is returned 
independently of
   `downstream.charts`, and each `DashboardEntity` has an optional `chart_ids?: 
number[];`
   field; there is no guarantee that every `chart_id` present on a dashboard 
will also appear
   in `downstream.charts.result` (for example, the backend may exclude certain 
charts from
   `downstream.charts` due to permissions or filters while still listing 
dashboards).
   
   4. Because ECharts Sankey (used via the `Echart` component configured at 
lines 57-70 in
   the same file) only renders nodes that participate in at least one link, 
dashboards whose
   `chart_ids` do not resolve to any chart in `chartKeys` end up with no 
incoming or outgoing
   edges and therefore are omitted from the visual graph, even though their 
nodes are added;
   this manifests in dataset lineage consumers such as the dataset edit page
   (`superset-frontend/src/features/datasets/AddDataset/EditDataset/index.tsx`, 
lines
   101-105) where certain downstream dashboards present in the API response 
simply do not
   appear in the Sankey if none of their charts produced visible chart nodes.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=f57a26e7e3684ce78ad79d06344b2a50&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=f57a26e7e3684ce78ad79d06344b2a50&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/features/lineage/LineageView.tsx
   **Line:** 457:463
   **Comment:**
        *Incomplete Implementation: This only creates dashboard edges via 
`chart_ids` intersections with visible chart nodes, so dashboards become 
orphaned whenever none of their `chart_ids` resolve to rendered chart nodes 
(for example after permission filtering). In Sankey, orphaned nodes may not 
appear, so downstream dashboards can disappear even though they are present in 
the API response. Add a fallback edge (for example dataset-to-dashboard in 
dataset lineage) when no chart-based edge is created.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=c91f2e28e11c00668c9346c9bea15f9f0d4b56938dd5f1d7876520d519c7f284&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=c91f2e28e11c00668c9346c9bea15f9f0d4b56938dd5f1d7876520d519c7f284&reaction=dislike'>👎</a>



##########
superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx:
##########
@@ -413,6 +415,16 @@ const StyledTableTabWrapper = styled.div`
   }
 `;
 
+// Functional wrapper for the lineage tab, since hooks can't be used directly 
in
+// the DatasourceEditor class component.
+function DatasetLineageTab({ datasourceId }: { datasourceId?: number }) {
+  const lineageResource = useDatasetLineage(datasourceId ?? '');
+  if (!datasourceId) {
+    return <Loading />;

Review Comment:
   **Suggestion:** When the dataset has no persisted ID yet, this branch 
returns a perpetual loading spinner instead of a terminal empty/disabled state. 
Because the hook skips fetching for empty IDs, the UI never transitions and 
users see an infinite loading state on new/unsaved datasets. Return an explicit 
“no lineage available until saved” empty state (or hide the tab) when the ID is 
missing. [logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ Dataset editor lineage tab shows indefinite loading spinner.
   - ⚠️ Unsaved datasets cannot see clear “no lineage” state.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. In `DatasetLineageTab` in
   
`superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx`
   (around lines 420-426), note it calls `const lineageResource =
   useDatasetLineage(datasourceId ?? '')` and then immediately returns a 
`Loading` spinner
   when `!datasourceId`, without ever rendering `LineageView` for this case.
   
   2. Inspect the lineage hook implementation in
   `superset-frontend/src/hooks/apiResources/lineage.ts`: `useDatasetLineage` 
(lines 125-129)
   calls `useApiV1Resource('/api/v1/dataset/${idOrUuid}/lineage', skip ||
   isEmptyId(idOrUuid))`, and `isEmptyId` (lines 115-118) returns true for an 
empty string,
   which prevents any request when the id passed is `''`.
   
   3. Observe that `DatasourceEditor` wires this tab by passing `datasource.id` 
as
   `datasourceId` (lines 88-93 in the same DatasourceEditor file), so for any
   `DatasourceEditor` caller that provides a `datasource` object without a 
persisted `id`
   (for example, a newly created dataset that has not yet been saved and 
therefore has
   `datasource.id` undefined), `DatasetLineageTab` will evaluate `datasourceId` 
as falsy and
   return `<Loading />`.
   
   4. Because the loading branch is keyed solely on the presence of 
`datasourceId` and the
   lineage hook skips fetching when the id is empty, there is no code path that 
transitions
   this tab from the loading spinner to a resolved “no lineage until saved” or 
empty state
   while the dataset remains unsaved; the UI will show an indefinite spinner 
for any unsaved
   dataset that exposes this Lineage tab.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=8d4531b47340418fa66dd32950495694&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=8d4531b47340418fa66dd32950495694&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx
   **Line:** 421:423
   **Comment:**
        *Logic Error: When the dataset has no persisted ID yet, this branch 
returns a perpetual loading spinner instead of a terminal empty/disabled state. 
Because the hook skips fetching for empty IDs, the UI never transitions and 
users see an infinite loading state on new/unsaved datasets. Return an explicit 
“no lineage available until saved” empty state (or hide the tab) when the ID is 
missing.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=3d7bd7906238880d00206dd89a807b47b33477ab08cf4ea932019b9044b72105&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F40912&comment_hash=3d7bd7906238880d00206dd89a807b47b33477ab08cf4ea932019b9044b72105&reaction=dislike'>👎</a>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to