[Dashboards] - Min Max range on secondary axis bar charts (#15118)
video QA https://github.com/user-attachments/assets/70c37188-2398-43de-bbf6-5882bb79940a --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+45
-123
@@ -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({
|
||||
@@ -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={[]}
|
||||
@@ -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);
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
updateChartSettingToggle(item.id);
|
||||
};
|
||||
|
||||
const handleDropdownOpen = () => {
|
||||
const handleItemInputChange = (value: number | null) => {
|
||||
updateChartSettingInput(item.id, value);
|
||||
};
|
||||
|
||||
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
|
||||
return (
|
||||
<ChartSettingItem
|
||||
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
|
||||
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>
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown';
|
||||
import { CommandMenuItemNumberInput } from '@/command-menu/components/CommandMenuItemNumberInput';
|
||||
import { CommandMenuItemToggle } from '@/command-menu/components/CommandMenuItemToggle';
|
||||
import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration';
|
||||
import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds';
|
||||
import { type ChartSettingsItem } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup';
|
||||
import { isMinMaxRangeValid } from '@/command-menu/pages/page-layout/utils/isMinMaxRangeValid';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isString } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type ChartSettingItemProps = {
|
||||
item: ChartSettingsItem;
|
||||
isDisabled: boolean;
|
||||
configuration: ChartConfiguration;
|
||||
getChartSettingsValues: (
|
||||
itemId: CHART_CONFIGURATION_SETTING_IDS,
|
||||
) => boolean | string | undefined;
|
||||
onToggleChange: () => void;
|
||||
onInputChange: (value: number | null) => void;
|
||||
onDropdownOpen: () => void;
|
||||
onFilterClick: () => void;
|
||||
};
|
||||
|
||||
export const ChartSettingItem = ({
|
||||
item,
|
||||
isDisabled,
|
||||
configuration,
|
||||
getChartSettingsValues,
|
||||
onToggleChange,
|
||||
onInputChange,
|
||||
onDropdownOpen,
|
||||
onFilterClick,
|
||||
}: ChartSettingItemProps) => {
|
||||
if (item.id === CHART_CONFIGURATION_SETTING_IDS.FILTER) {
|
||||
return (
|
||||
<SelectableListItem
|
||||
key={item.id}
|
||||
itemId={item.id}
|
||||
onEnter={onFilterClick}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={item.id}
|
||||
label={t(item.label)}
|
||||
Icon={item.Icon}
|
||||
hasSubMenu
|
||||
onClick={onFilterClick}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
}
|
||||
|
||||
if (isDefined(item.isInput)) {
|
||||
const settingValue = getChartSettingsValues(item.id);
|
||||
const stringValue = isString(settingValue) ? settingValue : '';
|
||||
|
||||
return (
|
||||
<SelectableListItem key={item.id} itemId={item.id}>
|
||||
<CommandMenuItem
|
||||
id={item.id}
|
||||
label={t(item.label)}
|
||||
Icon={item.Icon}
|
||||
RightComponent={
|
||||
<CommandMenuItemNumberInput
|
||||
value={stringValue}
|
||||
onChange={onInputChange}
|
||||
onValidate={(value) =>
|
||||
isDefined(value) &&
|
||||
isMinMaxRangeValid(
|
||||
item.id as
|
||||
| CHART_CONFIGURATION_SETTING_IDS.MIN_RANGE
|
||||
| CHART_CONFIGURATION_SETTING_IDS.MAX_RANGE,
|
||||
value,
|
||||
configuration,
|
||||
)
|
||||
}
|
||||
placeholder={
|
||||
item.inputPlaceholder ? t(item.inputPlaceholder) : undefined
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.isBoolean) {
|
||||
return (
|
||||
<SelectableListItem
|
||||
key={item.id}
|
||||
itemId={item.id}
|
||||
onEnter={isDisabled ? undefined : onToggleChange}
|
||||
>
|
||||
<CommandMenuItemToggle
|
||||
LeftIcon={item.Icon}
|
||||
text={t(item.label)}
|
||||
id={item.id}
|
||||
toggled={getChartSettingsValues(item.id) as boolean}
|
||||
onToggleChange={onToggleChange}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectableListItem
|
||||
key={item.id}
|
||||
itemId={item.id}
|
||||
onEnter={isDisabled ? undefined : onDropdownOpen}
|
||||
>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user