d10ab5f67c
This PR is the first part of the creation of the Chart editor. https://github.com/user-attachments/assets/8b0af8ea-be41-4506-84cb-e40f5521cbf0 Done: - Bar chart settings (except filters) - Line chart settings (except filters) In progress: - Pie chart settings - Number chart settings - Gauge chart settings Left to do: - Loosen the backend validation to allow the user to save a partial configuration, validate the graph configuration in the frontend and display a error friendly message in the graph if the config is not completed yet - Implement the filter edition - Finish the other graph types settings
140 lines
4.8 KiB
TypeScript
140 lines
4.8 KiB
TypeScript
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
|
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
|
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
|
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
|
|
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
|
|
import { createDefaultGraphWidget } from '@/page-layout/utils/createDefaultGraphWidget';
|
|
import {
|
|
getWidgetSize,
|
|
getWidgetTitle,
|
|
} from '@/page-layout/utils/getDefaultWidgetData';
|
|
import { getDefaultWidgetPosition } from '@/page-layout/utils/getDefaultWidgetPosition';
|
|
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
|
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
|
|
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
|
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
|
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
|
import { useRecoilCallback } from 'recoil';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { type GraphType } from '~/generated-metadata/graphql';
|
|
import { WidgetType, type PageLayoutWidget } from '~/generated/graphql';
|
|
|
|
export const useCreatePageLayoutGraphWidget = (
|
|
pageLayoutIdFromProps?: string,
|
|
) => {
|
|
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
|
PageLayoutComponentInstanceContext,
|
|
pageLayoutIdFromProps,
|
|
);
|
|
|
|
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
|
|
|
const activeTabIdState = useRecoilComponentCallbackState(
|
|
activeTabIdComponentState,
|
|
tabListInstanceId,
|
|
);
|
|
|
|
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
|
pageLayoutDraftComponentState,
|
|
pageLayoutId,
|
|
);
|
|
|
|
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
|
pageLayoutCurrentLayoutsComponentState,
|
|
pageLayoutId,
|
|
);
|
|
|
|
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
|
pageLayoutDraggedAreaComponentState,
|
|
pageLayoutId,
|
|
);
|
|
|
|
const createPageLayoutGraphWidget = useRecoilCallback(
|
|
({ snapshot, set }) =>
|
|
(graphType: GraphType): PageLayoutWidget => {
|
|
const activeTabId = snapshot.getLoadable(activeTabIdState).getValue();
|
|
|
|
if (!isDefined(activeTabId)) {
|
|
throw new Error(
|
|
'A tab must be selected to create a new graph widget',
|
|
);
|
|
}
|
|
|
|
const pageLayoutDraft = snapshot
|
|
.getLoadable(pageLayoutDraftState)
|
|
.getValue();
|
|
|
|
const allTabLayouts = snapshot
|
|
.getLoadable(pageLayoutCurrentLayoutsState)
|
|
.getValue();
|
|
|
|
const pageLayoutDraggedArea = snapshot
|
|
.getLoadable(pageLayoutDraggedAreaState)
|
|
.getValue();
|
|
|
|
const allWidgets = pageLayoutDraft.tabs.flatMap((tab) => tab.widgets);
|
|
const existingWidgetCount = allWidgets.filter(
|
|
(w) =>
|
|
w.type === WidgetType.GRAPH &&
|
|
w.configuration &&
|
|
'graphType' in w.configuration &&
|
|
w.configuration.graphType === graphType,
|
|
).length;
|
|
const title = getWidgetTitle(graphType, existingWidgetCount);
|
|
const widgetId = uuidv4();
|
|
|
|
const defaultSize = getWidgetSize(graphType);
|
|
const position = getDefaultWidgetPosition(
|
|
pageLayoutDraggedArea,
|
|
defaultSize,
|
|
);
|
|
|
|
const newWidget = createDefaultGraphWidget({
|
|
id: widgetId,
|
|
pageLayoutTabId: activeTabId,
|
|
title,
|
|
graphType,
|
|
gridPosition: {
|
|
row: position.y,
|
|
column: position.x,
|
|
rowSpan: position.h,
|
|
columnSpan: position.w,
|
|
},
|
|
});
|
|
|
|
const newLayout = {
|
|
i: widgetId,
|
|
x: position.x,
|
|
y: position.y,
|
|
w: position.w,
|
|
h: position.h,
|
|
};
|
|
|
|
const updatedLayouts = getUpdatedTabLayouts(
|
|
allTabLayouts,
|
|
activeTabId,
|
|
newLayout,
|
|
);
|
|
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
|
|
|
set(pageLayoutDraftState, (prev) => ({
|
|
...prev,
|
|
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
|
}));
|
|
|
|
set(pageLayoutDraggedAreaState, null);
|
|
|
|
return newWidget;
|
|
},
|
|
[
|
|
activeTabIdState,
|
|
pageLayoutCurrentLayoutsState,
|
|
pageLayoutDraftState,
|
|
pageLayoutDraggedAreaState,
|
|
],
|
|
);
|
|
|
|
return { createPageLayoutGraphWidget };
|
|
};
|