diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx
index 4e35aeb51b..930649b427 100644
--- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx
+++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx
@@ -16,11 +16,20 @@ import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/
import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds';
import { isChartSettingDisabled } from '@/command-menu/pages/page-layout/utils/isChartSettingDisabled';
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
+import { GRAPH_MAXIMUM_NUMBER_OF_GROUPS } from '@/page-layout/widgets/graph/constants/GraphMaximumNumberOfGroups.constant';
+import { hasWidgetTooManyGroupsComponentState } from '@/page-layout/widgets/graph/states/hasWidgetTooManyGroupsComponentState';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
+import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
+import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
+import { SidePanelInformationBanner } from 'twenty-ui/display';
-import { type GraphType, type PageLayoutWidget } from '~/generated/graphql';
+import { GraphType, type PageLayoutWidget } from '~/generated/graphql';
+
+const StyledSidePanelInformationBanner = styled(SidePanelInformationBanner)`
+ margin-top: ${({ theme }) => theme.spacing(2)};
+`;
export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
const { updateCommandMenuPageInfo } = useUpdateCommandMenuPageInfo();
@@ -56,6 +65,8 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
const isGroupByEnabled = getChartSettingsValues(
CHART_CONFIGURATION_SETTING_IDS.GROUP_BY,
);
+ const [hasWidgetTooManyGroups, setHasWidgetTooManyGroups] =
+ useRecoilComponentState(hasWidgetTooManyGroupsComponentState);
const handleGraphTypeChange = (graphType: GraphType) => {
updateCurrentWidgetConfig({
@@ -68,6 +79,13 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
updateCommandMenuPageInfo({
pageIcon: GRAPH_TYPE_INFORMATION[graphType].icon,
});
+
+ if (
+ graphType !== GraphType.VERTICAL_BAR &&
+ graphType !== GraphType.HORIZONTAL_BAR
+ ) {
+ setHasWidgetTooManyGroups(false);
+ }
};
const chartSettings = GRAPH_TYPE_INFORMATION[currentGraphType].settings;
@@ -83,6 +101,11 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
currentGraphType={currentGraphType}
setCurrentGraphType={handleGraphTypeChange}
/>
+ {hasWidgetTooManyGroups && (
+
+ )}
{chartSettings.map((group) => (
{group.items.map((item) => {
diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutGraphTypeSelect.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutGraphTypeSelect.tsx
index 173feb628d..bb23a32319 100644
--- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutGraphTypeSelect.tsx
+++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutGraphTypeSelect.tsx
@@ -5,6 +5,7 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pa
import { useUpdatePageLayoutWidget } from '@/page-layout/hooks/useUpdatePageLayoutWidget';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
+import { GraphWidgetComponentInstanceContext } from '@/page-layout/widgets/graph/states/contexts/GraphWidgetComponentInstanceContext';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
@@ -53,7 +54,9 @@ export const CommandMenuPageLayoutGraphTypeSelect = () => {
const graphTypeLabel = t(GRAPH_TYPE_INFORMATION[currentGraphType].label);
return (
- <>
+
{
}}
/>
- >
+
);
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx
index 76816771cb..e9ddb1ba8b 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx
@@ -1,5 +1,6 @@
import { PageLayoutWidgetNoDataDisplay } from '@/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay';
import { GraphWidget } from '@/page-layout/widgets/graph/components/GraphWidget';
+import { GraphWidgetComponentInstanceContext } from '@/page-layout/widgets/graph/states/contexts/GraphWidgetComponentInstanceContext';
import { isDefined } from 'twenty-shared/utils';
import { GraphType, type PageLayoutWidget } from '~/generated/graphql';
@@ -22,10 +23,14 @@ export const GraphWidgetRenderer = ({ widget }: GraphWidgetRendererProps) => {
}
return (
-
+
+
+
);
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartHasTooManyGroupsEffect.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartHasTooManyGroupsEffect.tsx
new file mode 100644
index 0000000000..915a10f42e
--- /dev/null
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartHasTooManyGroupsEffect.tsx
@@ -0,0 +1,21 @@
+import { hasWidgetTooManyGroupsComponentState } from '@/page-layout/widgets/graph/states/hasWidgetTooManyGroupsComponentState';
+import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
+import { useEffect } from 'react';
+
+type GraphWidgetBarChartHasTooManyGroupsEffectProps = {
+ hasTooManyGroups: boolean;
+};
+
+export const GraphWidgetBarChartHasTooManyGroupsEffect = ({
+ hasTooManyGroups,
+}: GraphWidgetBarChartHasTooManyGroupsEffectProps) => {
+ const setHasWidgetTooManyGroups = useSetRecoilComponentState(
+ hasWidgetTooManyGroupsComponentState,
+ );
+
+ useEffect(() => {
+ setHasWidgetTooManyGroups(hasTooManyGroups);
+ }, [hasTooManyGroups, setHasWidgetTooManyGroups]);
+
+ return null;
+};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx
index c3eda43c1e..95ec99c058 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx
@@ -1,4 +1,5 @@
import { ChartSkeletonLoader } from '@/page-layout/widgets/graph/components/ChartSkeletonLoader';
+import { GraphWidgetBarChartHasTooManyGroupsEffect } from '@/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartHasTooManyGroupsEffect';
import { useGraphBarChartWidgetData } from '@/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData';
import { lazy, Suspense, useMemo } from 'react';
import {
@@ -29,6 +30,7 @@ export const GraphWidgetBarChartRenderer = ({
showDataLabels,
layout,
loading,
+ hasTooManyGroups,
} = useGraphBarChartWidgetData({
objectMetadataItemId: widget.objectMetadataId,
configuration: widget.configuration as BarChartConfiguration,
@@ -53,23 +55,28 @@ export const GraphWidgetBarChartRenderer = ({
}
return (
- }>
-
+
-
+ }>
+
+
+ >
);
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts
index d777a5829d..50c299f269 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts
@@ -22,6 +22,7 @@ type UseGraphBarChartWidgetDataResult = {
layout?: 'vertical' | 'horizontal';
loading: boolean;
error?: Error;
+ hasTooManyGroups: boolean;
};
export const useGraphBarChartWidgetData = ({
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/states/contexts/GraphWidgetComponentInstanceContext.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/states/contexts/GraphWidgetComponentInstanceContext.ts
new file mode 100644
index 0000000000..8069d16a58
--- /dev/null
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/states/contexts/GraphWidgetComponentInstanceContext.ts
@@ -0,0 +1,4 @@
+import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
+
+export const GraphWidgetComponentInstanceContext =
+ createComponentInstanceContext();
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/states/hasWidgetTooManyGroupsComponentState.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/states/hasWidgetTooManyGroupsComponentState.ts
new file mode 100644
index 0000000000..4822d9528b
--- /dev/null
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/states/hasWidgetTooManyGroupsComponentState.ts
@@ -0,0 +1,8 @@
+import { GraphWidgetComponentInstanceContext } from '@/page-layout/widgets/graph/states/contexts/GraphWidgetComponentInstanceContext';
+import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
+
+export const hasWidgetTooManyGroupsComponentState = createComponentState({
+ key: 'hasWidgetTooManyGroupsComponentState',
+ defaultValue: false,
+ componentInstanceContext: GraphWidgetComponentInstanceContext,
+});
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformGroupByDataToBarChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformGroupByDataToBarChartData.ts
index 6dc061125e..f125e6c9c8 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformGroupByDataToBarChartData.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformGroupByDataToBarChartData.ts
@@ -33,6 +33,7 @@ type TransformGroupByDataToBarChartDataResult = {
yAxisLabel?: string;
showDataLabels: boolean;
layout?: 'vertical' | 'horizontal';
+ hasTooManyGroups: boolean;
};
const EMPTY_BAR_CHART_RESULT: TransformGroupByDataToBarChartDataResult = {
@@ -44,6 +45,7 @@ const EMPTY_BAR_CHART_RESULT: TransformGroupByDataToBarChartDataResult = {
yAxisLabel: undefined,
showDataLabels: false,
layout: 'vertical',
+ hasTooManyGroups: false,
};
export const transformGroupByDataToBarChartData = ({
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformOneDimensionalGroupByToBarChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformOneDimensionalGroupByToBarChartData.ts
index 1f76bd8d29..59148ffe0f 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformOneDimensionalGroupByToBarChartData.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformOneDimensionalGroupByToBarChartData.ts
@@ -28,6 +28,7 @@ type TransformOneDimensionalGroupByToBarChartDataResult = {
indexBy: string;
keys: string[];
series: BarChartSeries[];
+ hasTooManyGroups: boolean;
};
export const transformOneDimensionalGroupByToBarChartData = ({
@@ -87,5 +88,6 @@ export const transformOneDimensionalGroupByToBarChartData = ({
indexBy: indexByKey,
keys: [aggregateField.name],
series,
+ hasTooManyGroups: rawResults.length > GRAPH_MAXIMUM_NUMBER_OF_GROUPS,
};
};
diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformTwoDimensionalGroupByToBarChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformTwoDimensionalGroupByToBarChartData.ts
index 9ebca27ddf..1bb0913f87 100644
--- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformTwoDimensionalGroupByToBarChartData.ts
+++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/transformTwoDimensionalGroupByToBarChartData.ts
@@ -28,6 +28,7 @@ type TransformTwoDimensionalGroupByToBarChartDataResult = {
indexBy: string;
keys: string[];
series: BarChartSeries[];
+ hasTooManyGroups: boolean;
};
export const transformTwoDimensionalGroupByToBarChartData = ({
@@ -49,6 +50,8 @@ export const transformTwoDimensionalGroupByToBarChartData = ({
const xValues = new Set();
const yValues = new Set();
+ let hasTooManyGroups = false;
+
rawResults.forEach((result) => {
const dimensionValues = result.groupByDimensionValues;
if (!isDefined(dimensionValues) || dimensionValues.length < 2) return;
@@ -75,6 +78,7 @@ export const transformTwoDimensionalGroupByToBarChartData = ({
totalUniqueDimensions + additionalDimensions >
GRAPH_MAXIMUM_NUMBER_OF_GROUPS
) {
+ hasTooManyGroups = true;
return;
}
@@ -118,5 +122,6 @@ export const transformTwoDimensionalGroupByToBarChartData = ({
indexBy: indexByKey,
keys,
series,
+ hasTooManyGroups,
};
};
diff --git a/packages/twenty-ui/src/display/banner/components/SidePanelInformationBanner.tsx b/packages/twenty-ui/src/display/banner/components/SidePanelInformationBanner.tsx
new file mode 100644
index 0000000000..60e617a948
--- /dev/null
+++ b/packages/twenty-ui/src/display/banner/components/SidePanelInformationBanner.tsx
@@ -0,0 +1,54 @@
+import styled from '@emotion/styled';
+import { IconInfoCircle } from '../../icon/components/TablerIcons';
+
+const StyledBanner = styled.div`
+ align-items: center;
+ background-color: ${({ theme }) => theme.accent.secondary};
+ border-radius: ${({ theme }) => theme.border.radius.md};
+ box-sizing: border-box;
+ display: flex;
+ gap: ${({ theme }) => theme.spacing(2)};
+ padding: ${({ theme }) => theme.spacing(2)};
+ width: 100%;
+`;
+
+const StyledIconContainer = styled.div`
+ align-items: center;
+ color: ${({ theme }) => theme.color.blue};
+ display: flex;
+ flex-shrink: 0;
+ height: 16px;
+ justify-content: center;
+ width: 16px;
+`;
+
+const StyledMessage = styled.p`
+ color: ${({ theme }) => theme.color.blue};
+ flex-grow: 1;
+ font-family: ${({ theme }) => theme.font.family};
+ font-size: ${({ theme }) => theme.font.size.sm};
+ font-style: normal;
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+ line-height: 1.4;
+ margin: 0;
+ min-width: 0;
+`;
+
+export type SidePanelInformationBannerProps = {
+ message: string;
+ className?: string;
+};
+
+export const SidePanelInformationBanner = ({
+ message,
+ className,
+}: SidePanelInformationBannerProps) => {
+ return (
+
+
+
+
+ {message}
+
+ );
+};
diff --git a/packages/twenty-ui/src/display/banner/components/__stories__/SidePanelInformationBanner.stories.tsx b/packages/twenty-ui/src/display/banner/components/__stories__/SidePanelInformationBanner.stories.tsx
new file mode 100644
index 0000000000..d36fa0559d
--- /dev/null
+++ b/packages/twenty-ui/src/display/banner/components/__stories__/SidePanelInformationBanner.stories.tsx
@@ -0,0 +1,31 @@
+import { type Meta, type StoryObj } from '@storybook/react';
+import { ComponentDecorator } from '@ui/testing';
+import { SidePanelInformationBanner } from '../SidePanelInformationBanner';
+
+const meta: Meta = {
+ title: 'UI/Display/Banner/SidePanelInformationBanner',
+ component: SidePanelInformationBanner,
+ decorators: [ComponentDecorator],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ message: 'Max 200 bars per chart. Consider adding a filter',
+ },
+};
+
+export const ShortMessage: Story = {
+ args: {
+ message: 'Too many groups',
+ },
+};
+
+export const LongMessage: Story = {
+ args: {
+ message:
+ 'The chart has exceeded the maximum number of 200 bars. Please consider adding filters to reduce the number of data points displayed.',
+ },
+};
diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts
index e2a7294c5f..1d2339a8a5 100644
--- a/packages/twenty-ui/src/display/index.ts
+++ b/packages/twenty-ui/src/display/index.ts
@@ -17,6 +17,8 @@ export type { AvatarSize } from './avatar/types/AvatarSize';
export type { AvatarType } from './avatar/types/AvatarType';
export type { BannerVariant } from './banner/components/Banner';
export { Banner } from './banner/components/Banner';
+export type { SidePanelInformationBannerProps } from './banner/components/SidePanelInformationBanner';
+export { SidePanelInformationBanner } from './banner/components/SidePanelInformationBanner';
export type { AnimatedCheckmarkProps } from './checkmark/components/AnimatedCheckmark';
export { AnimatedCheckmark } from './checkmark/components/AnimatedCheckmark';
export type { CheckmarkProps } from './checkmark/components/Checkmark';