From bbd9720ab3143c61cec6d0c64c1e7e109dd2a886 Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Wed, 6 May 2026 03:06:45 +0530 Subject: [PATCH] [Dashboards] [Warning] Remove gauge chart support and delete existing widgets (#20172) ## Summary Removes gauge chart from the chart-type picker and deletes existing gauge widgets via a workspace migration. The gauge was rendering a hardcoded `0.7 / "Progress"` stub regardless of configuration -- never wired to real data. The contract stays in place. We keep `WidgetConfigurationType.GAUGE_CHART`, the DTO, the GraphQL union member, and the gauge folder -- so stored gauge JSON still resolves through the schema. The render path falls through to `default: return null`, so any un-migrated gauge widget renders as an empty cell, not a crash. This PR just removes existing gauge widgets if there are any (via `upgrade:2-3:delete-gauge-widgets`). The deliberate cleanup -- deleting the type definitions, the gauge folder, the DTO -- comes in a follow-up PR after the migration has run. --------- Co-authored-by: Charles Bochet --- .../widgets/graph/components/GraphWidget.tsx | 39 +------- .../components/ChartTypeSelectionSection.tsx | 1 - .../2-3/2-3-upgrade-version-command.module.ts | 2 + ...8000000000-delete-gauge-widgets.command.ts | 98 +++++++++++++++++++ .../page-layout-widget-seeds.constant.ts | 1 - ...t-page-layout-widget-data-seeds-v2.util.ts | 34 ------- 6 files changed, 104 insertions(+), 71 deletions(-) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx index aa801e675d..50e8137265 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx @@ -1,20 +1,11 @@ -import { WidgetSkeletonLoader } from '@/page-layout/widgets/components/WidgetSkeletonLoader'; +import { PageLayoutWidgetInvalidConfigDisplay } from '@/page-layout/widgets/components/PageLayoutWidgetInvalidConfigDisplay'; import { GraphWidgetAggregateChartRenderer } from '@/page-layout/widgets/graph/graph-widget-aggregate-chart/components/GraphWidgetAggregateChartRenderer'; import { GraphWidgetBarChartRenderer } from '@/page-layout/widgets/graph/graph-widget-bar-chart/components/GraphWidgetBarChartRenderer'; import { GraphWidgetLineChartRenderer } from '@/page-layout/widgets/graph/graph-widget-line-chart/components/GraphWidgetLineChartRenderer'; import { GraphWidgetPieChartRenderer } from '@/page-layout/widgets/graph/graph-widget-pie-chart/components/GraphWidgetPieChartRenderer'; import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget'; -import { lazy, Suspense } from 'react'; import { WidgetConfigurationType } from '~/generated-metadata/graphql'; -const GraphWidgetGaugeChart = lazy(() => - import( - '@/page-layout/widgets/graph/graph-widget-gauge-chart/components/GraphWidgetGaugeChart' - ).then((module) => ({ - default: module.GraphWidgetGaugeChart, - })), -); - export const GraphWidget = () => { const widget = useCurrentWidget(); @@ -24,31 +15,6 @@ export const GraphWidget = () => { case WidgetConfigurationType.AGGREGATE_CHART: return ; - case WidgetConfigurationType.GAUGE_CHART: { - const gaugeData = { - value: 0.7, - min: 0, - max: 1, - label: 'Progress', - }; - - return ( - }> - - - ); - } - case WidgetConfigurationType.PIE_CHART: return ; @@ -58,6 +24,9 @@ export const GraphWidget = () => { case WidgetConfigurationType.LINE_CHART: return ; + case WidgetConfigurationType.GAUGE_CHART: + return ; + default: return null; } diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartTypeSelectionSection.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartTypeSelectionSection.tsx index 49cb5235c5..6812a8fb98 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartTypeSelectionSection.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/ChartTypeSelectionSection.tsx @@ -12,7 +12,6 @@ const graphTypeOptions = [ GraphType.LINE, GraphType.PIE, GraphType.AGGREGATE, - GraphType.GAUGE, ]; const StyledChartTypeSelectionContainer = styled.div` diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-upgrade-version-command.module.ts index d31a0b8e16..970e79870c 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-upgrade-version-command.module.ts @@ -3,6 +3,7 @@ import { Module } from '@nestjs/common'; import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module'; import { DropMessageDirectionFieldCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1777400000000-drop-message-direction-field.command'; import { BackfillImageIdentifierFieldMetadataIdCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1777920000000-backfill-image-identifier-field-metadata-id.command'; +import { DeleteGaugeWidgetsCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command'; import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module'; @@ -17,6 +18,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace providers: [ DropMessageDirectionFieldCommand, BackfillImageIdentifierFieldMetadataIdCommand, + DeleteGaugeWidgetsCommand, ], }) export class V2_3_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts new file mode 100644 index 0000000000..56b4aebce2 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-3/2-3-workspace-command-1798000000000-delete-gauge-widgets.command.ts @@ -0,0 +1,98 @@ +import { Command } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; + +import { ActiveOrSuspendedWorkspaceCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspace.command-runner'; +import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; + +@RegisteredWorkspaceCommand('2.3.0', 1798000000000) +@Command({ + name: 'upgrade:2-3:delete-gauge-widgets', + description: + 'Delete all GAUGE_CHART page layout widgets — gauge support has been removed', +}) +export class DeleteGaugeWidgetsCommand extends ActiveOrSuspendedWorkspaceCommandRunner { + constructor( + protected readonly workspaceIteratorService: WorkspaceIteratorService, + private readonly applicationService: ApplicationService, + private readonly workspaceCacheService: WorkspaceCacheService, + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + ) { + super(workspaceIteratorService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const isDryRun = options.dryRun ?? false; + + const { flatPageLayoutWidgetMaps } = + await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatPageLayoutWidgetMaps', + ]); + + const gaugeWidgets = Object.values( + flatPageLayoutWidgetMaps.byUniversalIdentifier, + ) + .filter(isDefined) + .filter( + (widget) => + widget.universalConfiguration.configurationType === + WidgetConfigurationType.GAUGE_CHART, + ); + + if (gaugeWidgets.length === 0) { + this.logger.log(`No gauge widgets in workspace ${workspaceId}`); + + return; + } + + if (isDryRun) { + this.logger.log( + `[DRY RUN] Would delete ${gaugeWidgets.length} gauge widget(s) in workspace ${workspaceId}`, + ); + + return; + } + + const { twentyStandardFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const result = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + pageLayoutWidget: { + flatEntityToCreate: [], + flatEntityToDelete: gaugeWidgets, + flatEntityToUpdate: [], + }, + }, + workspaceId, + applicationUniversalIdentifier: + twentyStandardFlatApplication.universalIdentifier, + }, + ); + + if (result.status === 'fail') { + this.logger.error( + `Failed to delete gauge widgets in workspace ${workspaceId}:\n${JSON.stringify(result, null, 2)}`, + ); + throw new Error( + `Failed to delete gauge widgets for workspace ${workspaceId}`, + ); + } + + this.logger.log( + `Deleted ${gaugeWidgets.length} gauge widget(s) for workspace ${workspaceId}`, + ); + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/page-layout-widget-seeds.constant.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/page-layout-widget-seeds.constant.ts index b493f8f50e..51e2370b39 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/page-layout-widget-seeds.constant.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/constants/page-layout-widget-seeds.constant.ts @@ -11,7 +11,6 @@ export const PAGE_LAYOUT_WIDGET_SEEDS = { CUSTOMER_COMPANIES_BY_SIZE: 'CUSTOMER_COMPANIES_BY_SIZE_WIDGET', CUSTOMER_ANNUAL_RECURRING_REVENUE: 'CUSTOMER_ANNUAL_RECURRING_REVENUE_WIDGET', CUSTOMER_REVENUE_DISTRIBUTION: 'CUSTOMER_REVENUE_DISTRIBUTION_WIDGET', - CUSTOMER_AVERAGE_ARR: 'CUSTOMER_AVERAGE_ARR_WIDGET', CUSTOMER_LINKEDIN_COUNT: 'CUSTOMER_LINKEDIN_COUNT_WIDGET', CUSTOMER_LINKEDIN_DISTRIBUTION: 'CUSTOMER_LINKEDIN_DISTRIBUTION_WIDGET', diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts index a6da5c3d9c..79153954d2 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts @@ -168,40 +168,6 @@ export const getPageLayoutWidgetDataSeedsV2 = ( } satisfies SeederFlatPageLayoutWidget) : null, - // GAUGE chart: Average ARR (Customer Analytics) - isDefined(companyArrFieldId) - ? ({ - id: generateSeedId( - workspaceId, - PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_AVERAGE_ARR, - ), - pageLayoutTabId: generateSeedId( - workspaceId, - PAGE_LAYOUT_TAB_SEEDS.CUSTOMER_ANALYTICS, - ), - title: 'Average ARR', - type: WidgetType.GRAPH, - gridPosition: { row: 0, column: 7, rowSpan: 6, columnSpan: 5 }, - position: { - layoutMode: PageLayoutTabLayoutMode.GRID, - row: 0, - column: 7, - rowSpan: 6, - columnSpan: 5, - }, - configuration: { - configurationType: WidgetConfigurationType.GAUGE_CHART, - aggregateFieldMetadataId: companyArrFieldId, - aggregateOperation: AggregateOperations.AVG, - displayDataLabel: true, - timezone: 'UTC', - firstDayOfTheWeek: CalendarStartDay.MONDAY, - }, - objectMetadataId: companyObject?.id ?? null, - overrides: null, - } satisfies SeederFlatPageLayoutWidget) - : null, - // PIE chart: Companies by LinkedIn (Customer Overview) isDefined(companyIdFieldId) && isDefined(companyLinkedinLinkFieldId) ? ({