From dde6fdda9daaa5a4c2c699441495fbbab94d6378 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Sai Potteri Date: Sat, 23 May 2026 11:13:25 +0530 Subject: [PATCH] Render tiles/charts inside RecordViewTable (rv-screen layout, not parent screen) --- src/components/RecordViewTable.tsx | 64 ++++++++++++++++++++++++++---- src/components/ScreenRenderer.tsx | 30 +------------- 2 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/components/RecordViewTable.tsx b/src/components/RecordViewTable.tsx index 3569c89..ba047e2 100644 --- a/src/components/RecordViewTable.tsx +++ b/src/components/RecordViewTable.tsx @@ -6,6 +6,8 @@ import { } from "lucide-react"; import { getRVScreen, getRecordView, type RecordViewField, type SearchQuery, type TileValue, type ChartDataResponse } from "../api/viewService"; import FormModal from "./FormModal"; +import Tile from "./Tile"; +import BarChart from "./BarChart"; // --------------------------------------------------------------------------- // Action column (config-driven from rv-screen layout) @@ -141,7 +143,26 @@ interface Props { viewId: number | string; onRowClick?: (instanceId: string) => void; toolbarAction?: React.ReactNode; - onAnalyticsLoaded?: (tiles: TileValue[], charts: ChartDataResponse[]) => void; +} + +type AnalyticsSpec = + | { kind: "tile"; uid: string; title?: string } + | { kind: "chart"; uid: string; title?: string }; + +// Walk rv-screen layout to find rv_tile / rv_chart elements (the platform +// places them as sibling blocks above the rv_table). +function extractAnalyticsElements(layout: any): AnalyticsSpec[] { + const out: AnalyticsSpec[] = []; + const walk = (nodes: any[]): void => { + for (const n of nodes ?? []) { + const cfg = n?.config; + if (cfg?.type === "rv_tile" && cfg.tile_uid) out.push({ kind: "tile", uid: cfg.tile_uid, title: cfg.title }); + if (cfg?.type === "rv_chart" && cfg.chart_uid) out.push({ kind: "chart", uid: cfg.chart_uid, title: cfg.title }); + if (Array.isArray(n?.children)) walk(n.children); + } + }; + walk(Array.isArray(layout) ? layout : []); + return out; } // Walks the rv-screen layout to find the rv_table element and returns a @@ -164,11 +185,14 @@ function extractColumnLabels(layout: any): Record { return out; } -export default function RecordViewTable({ viewId, onRowClick, toolbarAction, onAnalyticsLoaded }: Props) { +export default function RecordViewTable({ viewId, onRowClick, toolbarAction }: Props) { const navigate = useNavigate(); const [rvTemplateUid, setRvTemplateUid] = useState(null); const [columnLabels, setColumnLabels] = useState>({}); const [actionColumns, setActionColumns] = useState([]); + const [analyticsSpecs, setAnalyticsSpecs] = useState([]); + const [tileValues, setTileValues] = useState([]); + const [chartData, setChartData] = useState([]); const [fields, setFields] = useState([]); const [rows, setRows] = useState[]>([]); const [pagination, setPagination] = useState({ page: 1, limit: 10, total_count: 0, total_pages: 0 }); @@ -185,10 +209,14 @@ export default function RecordViewTable({ viewId, onRowClick, toolbarAction, onA setRvTemplateUid(null); setColumnLabels({}); setActionColumns([]); + setAnalyticsSpecs([]); + setTileValues([]); + setChartData([]); getRVScreen(viewId) .then((rv) => { setColumnLabels(extractColumnLabels(rv.layout)); setActionColumns(extractActionColumns(rv.layout)); + setAnalyticsSpecs(extractAnalyticsElements(rv.layout)); setRvTemplateUid(rv.rv_template_uid); }) .catch((err) => { setError(err.message); setLoading(false); }); @@ -200,7 +228,8 @@ export default function RecordViewTable({ viewId, onRowClick, toolbarAction, onA setError(null); try { const res = await getRecordView(rvTemplateUid, viewId, query); - onAnalyticsLoaded?.(res.tile_values ?? [], res.chart_data ?? []); + setTileValues(res.tile_values ?? []); + setChartData(res.chart_data ?? []); setFields(res.config.fields.filter((f) => f.field_key !== "")); const dataRows = Array.isArray(res.data) ? res.data : []; setRows(dataRows); @@ -238,14 +267,30 @@ export default function RecordViewTable({ viewId, onRowClick, toolbarAction, onA onRowClick ? onRowClick(id) : navigate(`/detail/${id}`); }; - if (loading) return ; + const analyticsRow = analyticsSpecs.length > 0 ? ( +
+ {analyticsSpecs.map((a, i) => { + if (a.kind === "tile") { + const tv = tileValues.find(t => t.tile_uid === a.uid); + return ; + } + const cd = chartData.find(c => c.chart_uid === a.uid); + return ; + })} +
+ ) : null; + + if (loading) return <>{analyticsRow}; if (error) { return ( -
-

{error}

- -
+ <> + {analyticsRow} +
+

{error}

+ +
+ ); } @@ -259,6 +304,8 @@ export default function RecordViewTable({ viewId, onRowClick, toolbarAction, onA const pages = buildPageRange(page, total_pages); return ( + <> + {analyticsRow}
{/* ── Toolbar ───────────────────────────────────────────────────────── */} @@ -539,6 +586,7 @@ export default function RecordViewTable({ viewId, onRowClick, toolbarAction, onA /> )}
+ ); } diff --git a/src/components/ScreenRenderer.tsx b/src/components/ScreenRenderer.tsx index 84d9d26..e5cd534 100644 --- a/src/components/ScreenRenderer.tsx +++ b/src/components/ScreenRenderer.tsx @@ -1,11 +1,9 @@ import { useState, ReactNode } from "react"; import { Plus } from "lucide-react"; -import type { LayoutElement, TileValue, ChartDataResponse } from "../api/viewService"; +import type { LayoutElement } from "../api/viewService"; import RecordViewTable from "./RecordViewTable"; import DetailViewPanel from "./DetailViewPanel"; import FormModal from "./FormModal"; -import Tile from "./Tile"; -import BarChart from "./BarChart"; interface Props { layout: LayoutElement[]; @@ -21,9 +19,6 @@ interface AnyNode { children?: AnyNode[]; } -interface TileSpec { kind: "tile"; uid: string; title?: string; style?: Record } -interface ChartSpec { kind: "chart"; uid: string; title?: string; style?: Record } - function flatten(nodes: AnyNode[] | undefined): AnyNode[] { if (!nodes) return []; const out: AnyNode[] = []; @@ -36,12 +31,9 @@ function flatten(nodes: AnyNode[] | undefined): AnyNode[] { export default function ScreenRenderer({ layout, instanceId, onRefresh, onRowClick }: Props) { const [formModal, setFormModal] = useState<{ activityId: string; instanceId?: string; title?: string } | null>(null); - const [tileValues, setTileValues] = useState([]); - const [chartData, setChartData] = useState([]); const buttons: ReactNode[] = []; const elements: { type: "rv" | "dv"; viewId: number | string; style?: Record }[] = []; - const analytics: (TileSpec | ChartSpec)[] = []; for (const node of flatten(layout as AnyNode[])) { const cfg = node.config!; @@ -69,31 +61,12 @@ export default function ScreenRenderer({ layout, instanceId, onRefresh, onRowCli } else if (cfg.type === "detailsview") { const id = cfg.view_uuid || cfg.view_id; elements.push({ type: "dv", viewId: id, style: node.style }); - } else if (cfg.type === "rv_tile" && cfg.tile_uid) { - analytics.push({ kind: "tile", uid: cfg.tile_uid, title: cfg.title, style: node.style }); - } else if (cfg.type === "rv_chart" && cfg.chart_uid) { - analytics.push({ kind: "chart", uid: cfg.chart_uid, title: cfg.title, style: node.style }); } } - const tilesLoading = analytics.length > 0 && tileValues.length === 0 && chartData.length === 0; - return ( <>
- {analytics.length > 0 && ( -
- {analytics.map((a, i) => { - if (a.kind === "tile") { - const tv = tileValues.find(t => t.tile_uid === a.uid); - return ; - } - const cd = chartData.find(c => c.chart_uid === a.uid); - return ; - })} -
- )} - {elements.map((el, i) => { if (el.type === "rv") { return ( @@ -102,7 +75,6 @@ export default function ScreenRenderer({ layout, instanceId, onRefresh, onRowCli viewId={el.viewId} onRowClick={onRowClick} toolbarAction={buttons.length > 0 ?
{buttons}
: undefined} - onAnalyticsLoaded={(tiles, charts) => { setTileValues(tiles); setChartData(charts); }} />
);