From f7917084bca830f8c7d41a9fcdb2ddd350cfdaff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 03:05:26 +0000 Subject: [PATCH 1/6] Initial plan From c4f7fe574f3dcb6565bcf8617f6d6eacc49dfd7c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 03:10:40 +0000 Subject: [PATCH 2/6] feat: add ensureWidgetIds, default titles, and dynamic object/field dropdowns for dashboard widget config panel - Add ensureWidgetIds() to auto-fill missing widget IDs when entering edit mode - Set default widget title (e.g. "New Bar Chart") when creating widgets - WidgetConfigPanel now accepts availableObjects/availableFields props - Data source, category field, and value field use select dropdowns when metadata is available - Field selectors disabled when no object is selected (disabledWhen) - DashboardView passes metadata-derived objects/fields to WidgetConfigPanel Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/console/src/components/DashboardView.tsx | 54 +++- .../src/WidgetConfigPanel.tsx | 274 +++++++++++------- 2 files changed, 215 insertions(+), 113 deletions(-) diff --git a/apps/console/src/components/DashboardView.tsx b/apps/console/src/components/DashboardView.tsx index 92e72506a3..6c01e41e7b 100644 --- a/apps/console/src/components/DashboardView.tsx +++ b/apps/console/src/components/DashboardView.tsx @@ -51,6 +51,27 @@ function createWidgetId(): string { return `widget_${Date.now()}_${widgetCounter}`; } +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +/** Ensure every widget in the schema has a unique id. */ +function ensureWidgetIds(schema: DashboardSchema): DashboardSchema { + if (!schema.widgets?.length) return schema; + const needsFix = schema.widgets.some((w) => !w.id); + if (!needsFix) return schema; + return { + ...schema, + widgets: schema.widgets.map((w) => (w.id ? w : { ...w, id: createWidgetId() })), + }; +} + +/** Resolve a human-friendly default title for a new widget type. */ +function defaultWidgetTitle(type: string): string { + const entry = WIDGET_TYPES.find((t) => t.type === type); + return entry ? `New ${entry.label}` : 'New Widget'; +} + // --------------------------------------------------------------------------- // Helpers: flatten / unflatten widget config for WidgetConfigPanel // --------------------------------------------------------------------------- @@ -120,7 +141,7 @@ export function DashboardView({ dataSource }: { dataSource?: any }) { queueMicrotask(() => setIsLoading(false)); }, [dashboardName]); - const { dashboards } = useMetadata(); + const { dashboards, objects: metadataObjects } = useMetadata(); const dashboard = dashboards?.find((d: any) => d.name === dashboardName); // Local schema state for live preview — initialized from metadata @@ -142,7 +163,7 @@ export function DashboardView({ dataSource }: { dataSource?: any }) { // ---- Open / close config panel ------------------------------------------ const handleOpenConfigPanel = useCallback(() => { - setEditSchema(dashboard as DashboardSchema); + setEditSchema(ensureWidgetIds(dashboard as DashboardSchema)); setConfigPanelOpen(true); setConfigVersion((v) => v + 1); }, [dashboard]); @@ -159,7 +180,7 @@ export function DashboardView({ dataSource }: { dataSource?: any }) { const id = createWidgetId(); const newWidget: DashboardWidgetSchema = { id, - title: '', + title: defaultWidgetTitle(type), type, layout: { x: 0, @@ -290,6 +311,31 @@ export function DashboardView({ dataSource }: { dataSource?: any }) { [selectedWidgetId], ); + // ---- Metadata-driven dropdown options ----------------------------------- + const availableObjects = useMemo(() => { + if (!metadataObjects?.length) return undefined; + return metadataObjects.map((obj: any) => ({ + value: obj.name, + label: obj.label || obj.name, + })); + }, [metadataObjects]); + + const availableFields = useMemo(() => { + const objectName = selectedWidget?.object; + if (!objectName || !metadataObjects?.length) return undefined; + const obj = metadataObjects.find((o: any) => o.name === objectName); + if (!obj?.fields) return undefined; + const fields = obj.fields; + if (Array.isArray(fields)) { + return fields.map((f: any) => ({ value: f.name, label: f.label || f.name })); + } + // fields can be Record + return Object.entries(fields).map(([key, f]: [string, any]) => ({ + value: key, + label: f.label || key, + })); + }, [selectedWidget?.object, metadataObjects]); + // ---- Loading / not-found guards ----------------------------------------- if (isLoading) { return ; @@ -377,6 +423,8 @@ export function DashboardView({ dataSource }: { dataSource?: any }) { config={widgetConfig} onSave={handleWidgetConfigSave} onFieldChange={handleWidgetFieldChange} + availableObjects={availableObjects} + availableFields={availableFields} headerExtra={