Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9882c6b
done
ehconitin Oct 15, 2025
bff3f99
range should be only for numeric values, like omit null values
ehconitin Oct 16, 2025
808832c
improve input
ehconitin Oct 16, 2025
4a5db13
validate range max min and make groupby resolver service changes more…
ehconitin Oct 16, 2025
ddb770b
grep
ehconitin Oct 16, 2025
a9c02d6
update snapshot
ehconitin Oct 16, 2025
5031066
fix
ehconitin Oct 16, 2025
27f2475
fix
ehconitin Oct 16, 2025
871e2ea
improve
ehconitin Oct 16, 2025
549b184
nit
ehconitin Oct 16, 2025
58ab7f0
macros adjust
ehconitin Oct 16, 2025
a62ce9b
figma
ehconitin Oct 16, 2025
3f46f4a
Merge remote-tracking branch 'upstream/main' into min-max-range-clean
ehconitin Oct 16, 2025
138d3e7
improve
ehconitin Oct 16, 2025
f94c950
new plan
ehconitin Oct 16, 2025
eca043b
continuew
ehconitin Oct 16, 2025
0983d71
Merge remote-tracking branch 'upstream/main' into min-max-range-clean
ehconitin Oct 16, 2025
dbe0da7
fix
ehconitin Oct 16, 2025
9512d6d
improve
ehconitin Oct 17, 2025
7d9df2c
match figma
ehconitin Oct 17, 2025
adf8d1c
Merge remote-tracking branch 'upstream/main' into min-max-range-clean
ehconitin Oct 17, 2025
e36a07f
Merge remote-tracking branch 'upstream/main' into min-max-range-clean
ehconitin Oct 17, 2025
5d8295c
refact
ehconitin Oct 17, 2025
eaf6f06
Merge remote-tracking branch 'upstream/main' into min-max-range-clean
ehconitin Oct 19, 2025
40fe7fc
Merge branch 'main' into min-max-range
charlesBochet Oct 21, 2025
828d689
Merge branch 'main' into min-max-range
charlesBochet Oct 22, 2025
9bc5100
Fixes
charlesBochet Oct 22, 2025
d707db7
Merge branch 'main' into min-max-range
charlesBochet Oct 22, 2025
bffe841
Fixes
charlesBochet Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { TextInput } from '@/ui/input/components/TextInput';
import styled from '@emotion/styled';
import { useState } from 'react';
import { Key } from 'ts-key-enum';
import { isDefined } from 'twenty-shared/utils';
import {
canBeCastAsNumberOrNull,
castAsNumberOrNull,
} from '~/utils/cast-as-number-or-null';

type CommandMenuItemNumberInputProps = {
value: string;
onChange: (value: number | null) => void;
onValidate?: (value: number | null) => boolean;
placeholder?: string;
};
const StyledRightAlignedTextInput = styled(TextInput)`
input {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be reviewed with design to make the TextInput component evolve rather than overriding here

:focus {
color: ${({ theme }) => theme.font.color.primary};
}
color: ${({ theme }) => theme.font.color.tertiary};
text-align: right;
}
`;
export const CommandMenuItemNumberInput = ({
value,
onChange,
onValidate,
placeholder,
}: CommandMenuItemNumberInputProps) => {
const [draftValue, setDraftValue] = useState(value);
const [hasError, setHasError] = useState(false);

const handleChange = (text: string) => {
setDraftValue(text);
if (hasError) {
setHasError(false);
}
};

const handleCommit = () => {
if (!canBeCastAsNumberOrNull(draftValue)) {
setHasError(true);
setDraftValue(value);
return;
}

const numericValue = castAsNumberOrNull(draftValue);

if (isDefined(onValidate)) {
const isInvalid = onValidate(numericValue);
if (isInvalid) {
setHasError(true);
return;
}
}

onChange(numericValue);
setHasError(false);
};

const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
event.target.select();
};

const handleBlur = () => {
handleCommit();
};

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === Key.Enter || event.key === Key.Escape) {
event.stopPropagation();
handleCommit();
} else {
event.stopPropagation();
}
};

return (
<StyledRightAlignedTextInput
value={draftValue}
sizeVariant="sm"
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
placeholder={placeholder}
noErrorHelper
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,60 +1,61 @@
import { CommandGroup } from '@/command-menu/components/CommandGroup';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown';
import { CommandMenuItemToggle } from '@/command-menu/components/CommandMenuItemToggle';
import { CommandMenuList } from '@/command-menu/components/CommandMenuList';
import { COMMAND_MENU_LIST_SELECTABLE_LIST_ID } from '@/command-menu/constants/CommandMenuListSelectableListId';
import { useUpdateCommandMenuPageInfo } from '@/command-menu/hooks/useUpdateCommandMenuPageInfo';
import { ChartSettingItem } from '@/command-menu/pages/page-layout/components/chart-settings/ChartSettingItem';
import { ChartTypeSelectionSection } from '@/command-menu/pages/page-layout/components/ChartTypeSelectionSection';
import { GRAPH_TYPE_INFORMATION } from '@/command-menu/pages/page-layout/constants/GraphTypeInformation';
import { GRAPH_TYPE_TO_CONFIG_TYPENAME } from '@/command-menu/pages/page-layout/constants/GraphTypeToConfigTypename';
import { useChartSettingsValues } from '@/command-menu/pages/page-layout/hooks/useChartSettingsValues';
import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { useUpdateChartSettingInput } from '@/command-menu/pages/page-layout/hooks/useUpdateChartSettingInput';
import { useUpdateChartSettingToggle } from '@/command-menu/pages/page-layout/hooks/useUpdateChartSettingToggle';
import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration';
import {
CHART_CONFIGURATION_SETTING_IDS,
CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP,
} from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds';
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 { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { t } from '@lingui/core/macro';
import { isNonEmptyString } from '@sniptt/guards';

import {
BarChartGroupMode,
type GraphType,
type PageLayoutWidget,
} from '~/generated/graphql';
import { type GraphType, type PageLayoutWidget } from '~/generated/graphql';

export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
const { updateCommandMenuPageInfo } = useUpdateCommandMenuPageInfo();

const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu();

const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();

const { updateCurrentWidgetConfig } =
useUpdateCurrentWidgetConfig(pageLayoutId);

const { openDropdown } = useOpenDropdown();
const { setSelectedItemId } = useSelectableList(
COMMAND_MENU_LIST_SELECTABLE_LIST_ID,
);

if (widget.configuration?.__typename === 'IframeConfiguration') {
throw new Error(t`IframeConfiguration is not supported`);
}

const configuration = widget.configuration as ChartConfiguration;
const currentGraphType = configuration?.graphType;

const { getChartSettingsValues } = useChartSettingsValues({
objectMetadataId: widget.objectMetadataId,
configuration,
});

const currentGraphType = configuration?.graphType;
const { updateChartSettingToggle } = useUpdateChartSettingToggle({
pageLayoutId,
objectMetadataId: widget.objectMetadataId,
configuration,
});

const { updateChartSettingInput } = useUpdateChartSettingInput(pageLayoutId);

const isGroupByEnabled = getChartSettingsValues(
CHART_CONFIGURATION_SETTING_IDS.GROUP_BY,
);

const handleGraphTypeChange = (graphType: GraphType) => {
updateCurrentWidgetConfig({
Expand All @@ -71,14 +72,6 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {

const chartSettings = GRAPH_TYPE_INFORMATION[currentGraphType].settings;

const { setSelectedItemId } = useSelectableList(
COMMAND_MENU_LIST_SELECTABLE_LIST_ID,
);

const isGroupByEnabled = getChartSettingsValues(
CHART_CONFIGURATION_SETTING_IDS.GROUP_BY,
);

return (
<CommandMenuList
commandGroups={[]}
Expand All @@ -93,116 +86,45 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
{chartSettings.map((group) => (
<CommandGroup key={group.heading} heading={group.heading}>
{group.items.map((item) => {
const isDisabled =
(!isNonEmptyString(widget.objectMetadataId) &&
(item?.dependsOn?.includes(
CHART_CONFIGURATION_SETTING_IDS.SOURCE,
) ??
false)) ||
(!isGroupByEnabled &&
item?.dependsOn?.includes(
CHART_CONFIGURATION_SETTING_IDS.GROUP_BY,
));

const handleToggleChange = () => {
const configKey =
item.id in CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP
? CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP[
item.id as keyof typeof CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP
]
: item.id;
const isDisabled = isChartSettingDisabled(
item,
widget.objectMetadataId,
isGroupByEnabled as boolean,
);

const handleItemToggleChange = () => {
setSelectedItemId(item.id);
updateChartSettingToggle(item.id);
};

if (item.id === CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS) {
const isCurrentlyStacked = getChartSettingsValues(item.id);
const newGroupMode = isCurrentlyStacked
? BarChartGroupMode.GROUPED
: BarChartGroupMode.STACKED;

updateCurrentWidgetConfig({
configToUpdate: {
groupMode: newGroupMode,
},
});
} else {
const newValue = !getChartSettingsValues(item.id);

updateCurrentWidgetConfig({
configToUpdate: {
[configKey]: newValue,
},
});
}
const handleItemInputChange = (value: number | null) => {
updateChartSettingInput(item.id, value);
};

const handleDropdownOpen = () => {
const handleItemDropdownOpen = () => {
openDropdown({
dropdownComponentInstanceIdFromProps: item.id,
});
};

const handleFilterSettingsClick = () => {
const handleFilterClick = () => {
navigatePageLayoutCommandMenu({
commandMenuPage: CommandMenuPages.PageLayoutGraphFilter,
});
};

if (item.id === CHART_CONFIGURATION_SETTING_IDS.FILTER) {
return (
<SelectableListItem
key={item.id}
itemId={item.id}
onEnter={handleFilterSettingsClick}
>
<CommandMenuItem
id={item.id}
label="Filter"
Icon={item.Icon}
hasSubMenu
onClick={handleFilterSettingsClick}
/>
</SelectableListItem>
);
}

return item.isBoolean ? (
<SelectableListItem
key={item.id}
itemId={item.id}
onEnter={isDisabled ? undefined : handleToggleChange}
>
<CommandMenuItemToggle
LeftIcon={item.Icon}
text={t(item.label)}
id={item.id}
toggled={getChartSettingsValues(item.id) as boolean}
onToggleChange={handleToggleChange}
/>
</SelectableListItem>
) : (
<SelectableListItem
return (
<ChartSettingItem
key={item.id}
itemId={item.id}
onEnter={isDisabled ? undefined : handleDropdownOpen}
>
<CommandMenuItemDropdown
Icon={item.Icon}
label={t(item.label)}
id={item.id}
dropdownId={item.id}
dropdownComponents={
<DropdownContent>
{item.DropdownContent && <item.DropdownContent />}
</DropdownContent>
}
dropdownPlacement="bottom-end"
description={getChartSettingsValues(item.id) as string}
contextualTextPosition={'right'}
hasSubMenu
disabled={isDisabled}
/>
</SelectableListItem>
item={item}
isDisabled={isDisabled}
configuration={configuration}
getChartSettingsValues={getChartSettingsValues}
onToggleChange={handleItemToggleChange}
onInputChange={handleItemInputChange}
onDropdownOpen={handleItemDropdownOpen}
onFilterClick={handleFilterClick}
/>
);
})}
</CommandGroup>
Expand Down
Loading
Loading