[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 <charles@twenty.com>
This commit is contained in:
+4
-35
@@ -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 <GraphWidgetAggregateChartRenderer />;
|
||||
|
||||
case WidgetConfigurationType.GAUGE_CHART: {
|
||||
const gaugeData = {
|
||||
value: 0.7,
|
||||
min: 0,
|
||||
max: 1,
|
||||
label: 'Progress',
|
||||
};
|
||||
|
||||
return (
|
||||
<Suspense fallback={<WidgetSkeletonLoader />}>
|
||||
<GraphWidgetGaugeChart
|
||||
data={{
|
||||
value: gaugeData.value,
|
||||
min: gaugeData.min,
|
||||
max: gaugeData.max,
|
||||
label: gaugeData.label,
|
||||
}}
|
||||
displayType="percentage"
|
||||
showValue
|
||||
id={`gauge-chart-${widget.id}`}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
case WidgetConfigurationType.PIE_CHART:
|
||||
return <GraphWidgetPieChartRenderer />;
|
||||
|
||||
@@ -58,6 +24,9 @@ export const GraphWidget = () => {
|
||||
case WidgetConfigurationType.LINE_CHART:
|
||||
return <GraphWidgetLineChartRenderer />;
|
||||
|
||||
case WidgetConfigurationType.GAUGE_CHART:
|
||||
return <PageLayoutWidgetInvalidConfigDisplay />;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
-1
@@ -12,7 +12,6 @@ const graphTypeOptions = [
|
||||
GraphType.LINE,
|
||||
GraphType.PIE,
|
||||
GraphType.AGGREGATE,
|
||||
GraphType.GAUGE,
|
||||
];
|
||||
|
||||
const StyledChartTypeSelectionContainer = styled.div`
|
||||
|
||||
+2
@@ -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 {}
|
||||
|
||||
+98
@@ -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<void> {
|
||||
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}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
-1
@@ -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',
|
||||
|
||||
|
||||
-34
@@ -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)
|
||||
? ({
|
||||
|
||||
Reference in New Issue
Block a user