Add record page layout tabs editing (#18702)
- Fix duplication of tabs in dashboards that didn't open the duplicated tab in the side panel: replaced the logic that used Math.round with an algorithm that switches the positions of the elements. We will keep relying on integers, but it will work well in all cases. - Make dnd work with record page layouts, where there is a pinned tab - Make tab movements work with pinned tab, too - Allow user to set a tab as the pinned tab - Make backend changes to be able to save tabs with `layoutMode` https://github.com/user-attachments/assets/ce1130fa-71df-49ba-ba2f-6f971e15dd49 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
f2fa958f20
commit
fe4512f80c
@@ -1125,6 +1125,7 @@ export type CreatePageLayoutInput = {
|
||||
};
|
||||
|
||||
export type CreatePageLayoutTabInput = {
|
||||
layoutMode?: InputMaybe<PageLayoutTabLayoutMode>;
|
||||
pageLayoutId: Scalars['UUID'];
|
||||
position?: InputMaybe<Scalars['Float']>;
|
||||
title: Scalars['String'];
|
||||
@@ -1632,6 +1633,7 @@ export enum FeatureFlagKey {
|
||||
IS_NOTE_TARGET_MIGRATED = 'IS_NOTE_TARGET_MIGRATED',
|
||||
IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED',
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED',
|
||||
IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED = 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED',
|
||||
IS_RICH_TEXT_V1_MIGRATED = 'IS_RICH_TEXT_V1_MIGRATED',
|
||||
IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED = 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED',
|
||||
IS_TASK_TARGET_MIGRATED = 'IS_TASK_TARGET_MIGRATED',
|
||||
@@ -5014,6 +5016,7 @@ export type UpdatePageLayoutInput = {
|
||||
|
||||
export type UpdatePageLayoutTabInput = {
|
||||
icon?: InputMaybe<Scalars['String']>;
|
||||
layoutMode?: InputMaybe<PageLayoutTabLayoutMode>;
|
||||
position?: InputMaybe<Scalars['Float']>;
|
||||
title?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
@@ -5021,6 +5024,7 @@ export type UpdatePageLayoutTabInput = {
|
||||
export type UpdatePageLayoutTabWithWidgetsInput = {
|
||||
icon?: InputMaybe<Scalars['String']>;
|
||||
id: Scalars['UUID'];
|
||||
layoutMode?: InputMaybe<PageLayoutTabLayoutMode>;
|
||||
position: Scalars['Float'];
|
||||
title: Scalars['String'];
|
||||
widgets: Array<UpdatePageLayoutWidgetWithIdInput>;
|
||||
|
||||
+15
-1
@@ -2,8 +2,10 @@ import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-c
|
||||
import { useMountedEngineCommandContext } from '@/command-menu-item/engine-command/hooks/useMountedEngineCommandContext';
|
||||
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
|
||||
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const CancelDashboardSingleRecordCommand = () => {
|
||||
const { selectedRecords } = useMountedEngineCommandContext();
|
||||
@@ -15,13 +17,25 @@ export const CancelDashboardSingleRecordCommand = () => {
|
||||
|
||||
const pageLayoutId = selectedRecord.pageLayoutId;
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId: pageLayoutId ?? '',
|
||||
layoutType: PageLayoutType.DASHBOARD,
|
||||
targetRecordIdentifier: {
|
||||
id: selectedRecord.id,
|
||||
targetObjectNameSingular: '',
|
||||
},
|
||||
});
|
||||
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
const { setIsPageLayoutInEditMode } =
|
||||
useSetIsPageLayoutInEditMode(pageLayoutId);
|
||||
|
||||
const { resetDraftPageLayoutToPersistedPageLayout } =
|
||||
useResetDraftPageLayoutToPersistedPageLayout(pageLayoutId);
|
||||
useResetDraftPageLayoutToPersistedPageLayout({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const handleExecute = () => {
|
||||
closeSidePanelMenu();
|
||||
|
||||
+18
-1
@@ -1,10 +1,13 @@
|
||||
import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-command/components/HeadlessEngineCommandWrapperEffect';
|
||||
import { useMountedEngineCommandContext } from '@/command-menu-item/engine-command/hooks/useMountedEngineCommandContext';
|
||||
import { useSelectedRecordIdOrThrow } from '@/command-menu-item/record/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useRecordPageLayoutIdFromRecordStoreOrThrow } from '@/page-layout/hooks/useRecordPageLayoutIdFromRecordStoreOrThrow';
|
||||
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
|
||||
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const CancelRecordPageLayoutSingleRecordCommand = () => {
|
||||
const { objectMetadataItem } = useMountedEngineCommandContext();
|
||||
@@ -15,17 +18,31 @@ export const CancelRecordPageLayoutSingleRecordCommand = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
|
||||
const { pageLayoutId } = useRecordPageLayoutIdFromRecordStoreOrThrow({
|
||||
targetObjectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId,
|
||||
layoutType: PageLayoutType.RECORD_PAGE,
|
||||
targetRecordIdentifier: {
|
||||
id: recordId,
|
||||
targetObjectNameSingular: objectMetadataItem.nameSingular,
|
||||
},
|
||||
});
|
||||
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
const { setIsPageLayoutInEditMode } =
|
||||
useSetIsPageLayoutInEditMode(pageLayoutId);
|
||||
|
||||
const { resetDraftPageLayoutToPersistedPageLayout } =
|
||||
useResetDraftPageLayoutToPersistedPageLayout(pageLayoutId);
|
||||
useResetDraftPageLayoutToPersistedPageLayout({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const handleExecute = () => {
|
||||
closeSidePanelMenu();
|
||||
|
||||
+20
-2
@@ -1,10 +1,13 @@
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { useSelectedRecordIdOrThrow } from '@/command-menu-item/record/single-record/hooks/useSelectedRecordIdOrThrow';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
|
||||
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const CancelDashboardSingleRecordCommand = () => {
|
||||
const recordId = useSelectedRecordIdOrThrow();
|
||||
@@ -13,13 +16,28 @@ export const CancelDashboardSingleRecordCommand = () => {
|
||||
|
||||
const pageLayoutId = recordStore?.pageLayoutId;
|
||||
|
||||
if (!isDefined(pageLayoutId)) {
|
||||
throw new Error(
|
||||
'CancelDashboardSingleRecordCommand requires a valid pageLayoutId from the record store.',
|
||||
);
|
||||
}
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId,
|
||||
layoutType: PageLayoutType.DASHBOARD,
|
||||
targetRecordIdentifier: { id: recordId, targetObjectNameSingular: '' },
|
||||
});
|
||||
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
const { setIsPageLayoutInEditMode } =
|
||||
useSetIsPageLayoutInEditMode(pageLayoutId);
|
||||
|
||||
const { resetDraftPageLayoutToPersistedPageLayout } =
|
||||
useResetDraftPageLayoutToPersistedPageLayout(pageLayoutId);
|
||||
useResetDraftPageLayoutToPersistedPageLayout({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
closeSidePanelMenu();
|
||||
|
||||
@@ -11,9 +11,10 @@ import { PAGE_LAYOUT_GRID_ITEM_DRAGGING_Z_INDEX } from '@/page-layout/constants/
|
||||
import { PAGE_LAYOUT_GRID_ITEM_Z_INDEX } from '@/page-layout/constants/PageLayoutGridItemZIndex';
|
||||
import { PAGE_LAYOUT_GRID_MARGIN } from '@/page-layout/constants/PageLayoutGridMargin';
|
||||
import { PAGE_LAYOUT_GRID_ROW_HEIGHT } from '@/page-layout/constants/PageLayoutGridRowHeight';
|
||||
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
|
||||
import { usePageLayoutHandleLayoutChange } from '@/page-layout/hooks/usePageLayoutHandleLayoutChange';
|
||||
import { usePageLayoutTabWithVisibleWidgetsOrThrow } from '@/page-layout/hooks/usePageLayoutTabWithVisibleWidgetsOrThrow';
|
||||
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutCurrentBreakpointComponentState } from '@/page-layout/states/pageLayoutCurrentBreakpointComponentState';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
|
||||
@@ -24,20 +25,22 @@ import { filterPendingPlaceholderFromLayouts } from '@/page-layout/utils/filterP
|
||||
import { prepareGridLayoutItemsWithPlaceholders } from '@/page-layout/utils/prepareGridLayoutItemsWithPlaceholders';
|
||||
import { WidgetPlaceholder } from '@/page-layout/widgets/components/WidgetPlaceholder';
|
||||
import { WidgetRenderer } from '@/page-layout/widgets/components/WidgetRenderer';
|
||||
import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { css } from '@linaria/core';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import {
|
||||
Responsive,
|
||||
WidthProvider,
|
||||
type Layout,
|
||||
type Layouts,
|
||||
Responsive,
|
||||
type ResponsiveProps,
|
||||
WidthProvider,
|
||||
} from 'react-grid-layout';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { css } from '@linaria/core';
|
||||
|
||||
const disabledTransitionsClass = css`
|
||||
.react-grid-layout {
|
||||
@@ -99,6 +102,14 @@ type PageLayoutGridLayoutProps = {
|
||||
};
|
||||
|
||||
export const PageLayoutGridLayout = ({ tabId }: PageLayoutGridLayoutProps) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
);
|
||||
|
||||
const tabListInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
TabListComponentInstanceContext,
|
||||
);
|
||||
|
||||
const setPageLayoutCurrentBreakpoint = useSetAtomComponentState(
|
||||
pageLayoutCurrentBreakpointComponentState,
|
||||
);
|
||||
@@ -111,7 +122,10 @@ export const PageLayoutGridLayout = ({ tabId }: PageLayoutGridLayoutProps) => {
|
||||
pageLayoutResizingWidgetIdComponentState,
|
||||
);
|
||||
|
||||
const { handleLayoutChange } = usePageLayoutHandleLayoutChange();
|
||||
const { handleLayoutChange } = usePageLayoutHandleLayoutChange({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const handleLayoutChangeWithoutPendingPlaceholder = (
|
||||
currentLayout: Layout[],
|
||||
|
||||
+6
-177
@@ -1,186 +1,15 @@
|
||||
import { metadataStoreState } from '@/metadata-store/states/metadataStoreState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { PageLayoutLeftPanel } from '@/page-layout/components/PageLayoutLeftPanel';
|
||||
import { PageLayoutTabList } from '@/page-layout/components/PageLayoutTabList';
|
||||
import { PageLayoutTabListEffect } from '@/page-layout/components/PageLayoutTabListEffect';
|
||||
import { PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH } from '@/page-layout/constants/PageLayoutLeftPanelContainerWidth';
|
||||
import { useCreatePageLayoutTab } from '@/page-layout/hooks/useCreatePageLayoutTab';
|
||||
import { useCurrentPageLayout } from '@/page-layout/hooks/useCurrentPageLayout';
|
||||
import { useReorderPageLayoutTabs } from '@/page-layout/hooks/useReorderPageLayoutTabs';
|
||||
import { PageLayoutMainContent } from '@/page-layout/PageLayoutMainContent';
|
||||
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { getScrollWrapperInstanceIdFromPageLayoutId } from '@/page-layout/utils/getScrollWrapperInstanceIdFromPageLayoutId';
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { getTabsByDisplayMode } from '@/page-layout/utils/getTabsByDisplayMode';
|
||||
import { getTabsWithVisibleWidgets } from '@/page-layout/utils/getTabsWithVisibleWidgets';
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { PageLayoutTabsRenderer } from '@/page-layout/components/PageLayoutTabsRenderer';
|
||||
import { pageLayoutIsInitializedComponentState } from '@/page-layout/states/pageLayoutIsInitializedComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
const StyledContainer = styled.div<{ hasPinnedTab: boolean }>`
|
||||
display: grid;
|
||||
grid-template-columns: ${({ hasPinnedTab }) =>
|
||||
hasPinnedTab ? `${PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH}px 1fr` : '1fr'};
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTabsAndDashboardContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledPageLayoutTabListContainer = styled.div`
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledScrollWrapperContainer = styled.div`
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
`;
|
||||
|
||||
export const PageLayoutRendererContent = () => {
|
||||
const { currentPageLayout } = useCurrentPageLayout();
|
||||
|
||||
const { isInSidePanel, layoutType, targetRecordIdentifier } =
|
||||
useLayoutRenderingContext();
|
||||
|
||||
const isPageLayoutInEditMode = useIsPageLayoutInEditMode();
|
||||
|
||||
const activeTabId = useAtomComponentStateValue(activeTabIdComponentState);
|
||||
|
||||
const { createPageLayoutTab } = useCreatePageLayoutTab(currentPageLayout?.id);
|
||||
const { reorderTabs } = useReorderPageLayoutTabs(currentPageLayout?.id ?? '');
|
||||
const setPageLayoutTabSettingsOpenTabId = useSetAtomComponentState(
|
||||
pageLayoutTabSettingsOpenTabIdComponentState,
|
||||
);
|
||||
const { navigatePageLayoutSidePanel } = useNavigatePageLayoutSidePanel();
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const metadataStore = useAtomFamilyStateValue(
|
||||
metadataStoreState,
|
||||
'objectMetadataItems',
|
||||
const pageLayoutIsInitialized = useAtomComponentStateValue(
|
||||
pageLayoutIsInitializedComponentState,
|
||||
);
|
||||
|
||||
const isSystemObject =
|
||||
(metadataStore.current as ObjectMetadataItem[]).find(
|
||||
(item) =>
|
||||
item.nameSingular === targetRecordIdentifier?.targetObjectNameSingular,
|
||||
)?.isSystem ?? false;
|
||||
|
||||
if (!isDefined(currentPageLayout)) {
|
||||
if (!pageLayoutIsInitialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleAddTab =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(currentPageLayout.type)
|
||||
? () => {
|
||||
const newTabId = createPageLayoutTab(t`Untitled`);
|
||||
setPageLayoutTabSettingsOpenTabId(newTabId);
|
||||
navigatePageLayoutSidePanel({
|
||||
sidePanelPage: SidePanelPages.PageLayoutTabSettings,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const canEnableTabEditing =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(currentPageLayout.type);
|
||||
|
||||
const tabsWithVisibleWidgets = getTabsWithVisibleWidgets({
|
||||
tabs: currentPageLayout.tabs,
|
||||
isMobile,
|
||||
isInSidePanel,
|
||||
isEditMode: isPageLayoutInEditMode,
|
||||
});
|
||||
|
||||
const SYSTEM_OBJECT_TABS = ['Home', 'Timeline', 'Overview', 'Flow'];
|
||||
|
||||
const tabsForCurrentObject = isSystemObject
|
||||
? tabsWithVisibleWidgets.filter((tab) =>
|
||||
SYSTEM_OBJECT_TABS.includes(tab.title),
|
||||
)
|
||||
: tabsWithVisibleWidgets;
|
||||
|
||||
const { tabsToRenderInTabList, pinnedLeftTab } = getTabsByDisplayMode({
|
||||
tabs: tabsForCurrentObject,
|
||||
pageLayoutType: currentPageLayout.type,
|
||||
isMobile,
|
||||
isInSidePanel,
|
||||
});
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId: currentPageLayout.id,
|
||||
layoutType,
|
||||
targetRecordIdentifier,
|
||||
});
|
||||
|
||||
const sortedTabs = sortTabsByPosition(tabsToRenderInTabList);
|
||||
|
||||
const activeTabExistsInCurrentPageLayout = currentPageLayout.tabs.some(
|
||||
(tab) => tab.id === activeTabId,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainer hasPinnedTab={isDefined(pinnedLeftTab)}>
|
||||
{isDefined(pinnedLeftTab) && (
|
||||
<PageLayoutLeftPanel pinnedLeftTabId={pinnedLeftTab.id} />
|
||||
)}
|
||||
|
||||
<StyledTabsAndDashboardContainer>
|
||||
<PageLayoutTabListEffect
|
||||
tabs={sortedTabs}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
defaultTabToFocusOnMobileAndSidePanelId={
|
||||
currentPageLayout.defaultTabToFocusOnMobileAndSidePanelId ??
|
||||
undefined
|
||||
}
|
||||
/>
|
||||
{(sortedTabs.length > 1 || isPageLayoutInEditMode) && (
|
||||
<StyledPageLayoutTabListContainer>
|
||||
<PageLayoutTabList
|
||||
tabs={sortedTabs}
|
||||
behaveAsLinks={!isInSidePanel && !isPageLayoutInEditMode}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
onAddTab={handleAddTab}
|
||||
isReorderEnabled={canEnableTabEditing}
|
||||
onReorder={canEnableTabEditing ? reorderTabs : undefined}
|
||||
pageLayoutType={currentPageLayout.type}
|
||||
/>
|
||||
</StyledPageLayoutTabListContainer>
|
||||
)}
|
||||
|
||||
<StyledScrollWrapperContainer>
|
||||
<ScrollWrapper
|
||||
componentInstanceId={getScrollWrapperInstanceIdFromPageLayoutId(
|
||||
currentPageLayout.id,
|
||||
)}
|
||||
defaultEnableXScroll={false}
|
||||
>
|
||||
{isDefined(activeTabId) && activeTabExistsInCurrentPageLayout && (
|
||||
<PageLayoutMainContent tabId={activeTabId} />
|
||||
)}
|
||||
</ScrollWrapper>
|
||||
</StyledScrollWrapperContainer>
|
||||
</StyledTabsAndDashboardContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
return <PageLayoutTabsRenderer />;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import {
|
||||
DragDropContext,
|
||||
type DropResult,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
type OnDragUpdateResponder,
|
||||
type ResponderProvided,
|
||||
} from '@hello-pangea/dnd';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@@ -25,30 +25,34 @@ import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/con
|
||||
import { type TabListProps } from '@/ui/layout/tab-list/types/TabListProps';
|
||||
import { NodeDimension } from '@/ui/utilities/dimensions/components/NodeDimension';
|
||||
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS } from '@/page-layout/components/PageLayoutTabListDroppableIds';
|
||||
import { PageLayoutTabListReorderableOverflowDropdown } from '@/page-layout/components/PageLayoutTabListReorderableOverflowDropdown';
|
||||
import { TabListDropdown } from '@/ui/layout/tab-list/components/TabListDropdown';
|
||||
import { PageLayoutTabListVisibleTabs } from '@/page-layout/components/PageLayoutTabListVisibleTabs';
|
||||
import { STANDARD_PAGE_LAYOUT_TAB_TITLE_TRANSLATIONS } from '@/page-layout/constants/StandardPageLayoutTabTitleTranslations';
|
||||
import { useIsCurrentObjectCustom } from '@/page-layout/hooks/useIsCurrentObjectCustom';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutTabListCurrentDragDroppableIdComponentState } from '@/page-layout/states/pageLayoutTabListCurrentDragDroppableIdComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { TabListDropdown } from '@/ui/layout/tab-list/components/TabListDropdown';
|
||||
import { TabListFromUrlOptionalEffect } from '@/ui/layout/tab-list/components/TabListFromUrlOptionalEffect';
|
||||
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
type PageLayoutType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
@@ -109,6 +113,10 @@ export const PageLayoutTabList = ({
|
||||
|
||||
const shouldTranslateTabTitles = !isCustom;
|
||||
|
||||
const isRecordPageGlobalEditionEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED,
|
||||
);
|
||||
|
||||
const tabsWithIcons: SingleTabProps[] = tabs.map((tab) => ({
|
||||
id: tab.id,
|
||||
// TODO: drop once the configuration of all record page layouts has been migrated to the backend.
|
||||
@@ -260,7 +268,10 @@ export const PageLayoutTabList = ({
|
||||
(tabId: string) => {
|
||||
const shouldOpenSettings =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(pageLayoutType);
|
||||
shouldEnableTabEditingFeatures(
|
||||
pageLayoutType,
|
||||
isRecordPageGlobalEditionEnabled,
|
||||
);
|
||||
|
||||
if (shouldOpenSettings && activeTabId === tabId) {
|
||||
openTabSettings(tabId);
|
||||
@@ -275,6 +286,7 @@ export const PageLayoutTabList = ({
|
||||
},
|
||||
[
|
||||
isPageLayoutInEditMode,
|
||||
isRecordPageGlobalEditionEnabled,
|
||||
pageLayoutType,
|
||||
activeTabId,
|
||||
isTabSettingsOpen,
|
||||
@@ -287,7 +299,10 @@ export const PageLayoutTabList = ({
|
||||
(tabId: string) => {
|
||||
const shouldOpenSettings =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(pageLayoutType);
|
||||
shouldEnableTabEditingFeatures(
|
||||
pageLayoutType,
|
||||
isRecordPageGlobalEditionEnabled,
|
||||
);
|
||||
|
||||
if (shouldOpenSettings && activeTabId === tabId) {
|
||||
openTabSettings(tabId);
|
||||
@@ -303,6 +318,7 @@ export const PageLayoutTabList = ({
|
||||
},
|
||||
[
|
||||
isPageLayoutInEditMode,
|
||||
isRecordPageGlobalEditionEnabled,
|
||||
pageLayoutType,
|
||||
activeTabId,
|
||||
isTabSettingsOpen,
|
||||
|
||||
+17
-5
@@ -1,4 +1,3 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import {
|
||||
Draggable,
|
||||
type DraggableProvided,
|
||||
@@ -6,17 +5,18 @@ import {
|
||||
type DraggableStateSnapshot,
|
||||
Droppable,
|
||||
} from '@hello-pangea/dnd';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS } from '@/page-layout/components/PageLayoutTabListDroppableIds';
|
||||
import { PageLayoutTabListDroppableMoreButton } from '@/page-layout/components/PageLayoutTabListDroppableMoreButton';
|
||||
import { PageLayoutTabMenuItemSelectAvatar } from '@/page-layout/components/PageLayoutTabMenuItemSelectAvatar';
|
||||
import { PageLayoutTabRenderClone } from '@/page-layout/components/PageLayoutTabRenderClone';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { isPageLayoutTabDraggingComponentState } from '@/page-layout/states/isPageLayoutTabDraggingComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@@ -26,10 +26,14 @@ import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useContext } from 'react';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { type PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
type PageLayoutType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledOverflowDropdownListDraggableWrapper = styled.div`
|
||||
cursor: grab;
|
||||
@@ -75,8 +79,16 @@ export const PageLayoutTabListReorderableOverflowDropdown = ({
|
||||
|
||||
const isPageLayoutInEditMode = useIsPageLayoutInEditMode();
|
||||
|
||||
const isRecordPageGlobalEditionEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED,
|
||||
);
|
||||
|
||||
const shouldShowEditButton =
|
||||
isPageLayoutInEditMode && shouldEnableTabEditingFeatures(pageLayoutType);
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(
|
||||
pageLayoutType,
|
||||
isRecordPageGlobalEditionEnabled,
|
||||
);
|
||||
|
||||
const isPageLayoutTabDragging = useAtomComponentStateValue(
|
||||
isPageLayoutTabDraggingComponentState,
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
import { metadataStoreState } from '@/metadata-store/states/metadataStoreState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { PageLayoutLeftPanel } from '@/page-layout/components/PageLayoutLeftPanel';
|
||||
import { PageLayoutTabList } from '@/page-layout/components/PageLayoutTabList';
|
||||
import { PageLayoutTabListEffect } from '@/page-layout/components/PageLayoutTabListEffect';
|
||||
import { PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH } from '@/page-layout/constants/PageLayoutLeftPanelContainerWidth';
|
||||
import { useCreatePageLayoutTab } from '@/page-layout/hooks/useCreatePageLayoutTab';
|
||||
import { useCurrentPageLayoutOrThrow } from '@/page-layout/hooks/useCurrentPageLayoutOrThrow';
|
||||
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
|
||||
import { useReorderRecordPageLayoutTabs } from '@/page-layout/hooks/useReorderRecordPageLayoutTabs';
|
||||
import { PageLayoutMainContent } from '@/page-layout/PageLayoutMainContent';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { getScrollWrapperInstanceIdFromPageLayoutId } from '@/page-layout/utils/getScrollWrapperInstanceIdFromPageLayoutId';
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { getTabsByDisplayMode } from '@/page-layout/utils/getTabsByDisplayMode';
|
||||
import { getTabsWithVisibleWidgets } from '@/page-layout/utils/getTabsWithVisibleWidgets';
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledContainer = styled.div<{ hasPinnedTab: boolean }>`
|
||||
display: grid;
|
||||
grid-template-columns: ${({ hasPinnedTab }) =>
|
||||
hasPinnedTab ? `${PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH}px 1fr` : '1fr'};
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTabsAndDashboardContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledPageLayoutTabListContainer = styled.div`
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledScrollWrapperContainer = styled.div`
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
`;
|
||||
|
||||
export const PageLayoutTabsRenderer = () => {
|
||||
const { currentPageLayout } = useCurrentPageLayoutOrThrow();
|
||||
|
||||
const { isInSidePanel, layoutType, targetRecordIdentifier } =
|
||||
useLayoutRenderingContext();
|
||||
|
||||
const isPageLayoutInEditMode = useIsPageLayoutInEditMode();
|
||||
|
||||
const activeTabId = useAtomComponentStateValue(activeTabIdComponentState);
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId: currentPageLayout.id,
|
||||
layoutType,
|
||||
targetRecordIdentifier,
|
||||
});
|
||||
|
||||
const { createPageLayoutTab } = useCreatePageLayoutTab({
|
||||
pageLayoutId: currentPageLayout.id,
|
||||
tabListInstanceId,
|
||||
});
|
||||
const { reorderRecordPageTabs } = useReorderRecordPageLayoutTabs(
|
||||
currentPageLayout.id,
|
||||
);
|
||||
const setPageLayoutTabSettingsOpenTabId = useSetAtomComponentState(
|
||||
pageLayoutTabSettingsOpenTabIdComponentState,
|
||||
);
|
||||
const { navigatePageLayoutSidePanel } = useNavigatePageLayoutSidePanel();
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const isRecordPageGlobalEditionEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED,
|
||||
);
|
||||
|
||||
const metadataStore = useAtomFamilyStateValue(
|
||||
metadataStoreState,
|
||||
'objectMetadataItems',
|
||||
);
|
||||
|
||||
const isSystemObject =
|
||||
(metadataStore.current as ObjectMetadataItem[]).find(
|
||||
(item) =>
|
||||
item.nameSingular === targetRecordIdentifier?.targetObjectNameSingular,
|
||||
)?.isSystem ?? false;
|
||||
|
||||
const handleAddTab =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(
|
||||
currentPageLayout.type,
|
||||
isRecordPageGlobalEditionEnabled,
|
||||
)
|
||||
? () => {
|
||||
const newTabId = createPageLayoutTab(t`Untitled`);
|
||||
setPageLayoutTabSettingsOpenTabId(newTabId);
|
||||
navigatePageLayoutSidePanel({
|
||||
sidePanelPage: SidePanelPages.PageLayoutTabSettings,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const canEnableTabEditing =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(
|
||||
currentPageLayout.type,
|
||||
isRecordPageGlobalEditionEnabled,
|
||||
);
|
||||
|
||||
const tabsWithVisibleWidgets = getTabsWithVisibleWidgets({
|
||||
tabs: currentPageLayout.tabs,
|
||||
isMobile,
|
||||
isInSidePanel,
|
||||
isEditMode: isPageLayoutInEditMode,
|
||||
});
|
||||
|
||||
const SYSTEM_OBJECT_TABS = ['Home', 'Timeline', 'Overview', 'Flow'];
|
||||
|
||||
const tabsForCurrentObject = isSystemObject
|
||||
? tabsWithVisibleWidgets.filter((tab) =>
|
||||
SYSTEM_OBJECT_TABS.includes(tab.title),
|
||||
)
|
||||
: tabsWithVisibleWidgets;
|
||||
|
||||
const { tabsToRenderInTabList, pinnedLeftTab } = getTabsByDisplayMode({
|
||||
tabs: tabsForCurrentObject,
|
||||
pageLayoutType: currentPageLayout.type,
|
||||
isMobile,
|
||||
isInSidePanel,
|
||||
});
|
||||
|
||||
const sortedTabs = sortTabsByPosition(tabsToRenderInTabList);
|
||||
|
||||
const activeTabExistsInCurrentPageLayout = currentPageLayout.tabs.some(
|
||||
(tab) => tab.id === activeTabId,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainer hasPinnedTab={isDefined(pinnedLeftTab)}>
|
||||
{isDefined(pinnedLeftTab) && (
|
||||
<PageLayoutLeftPanel pinnedLeftTabId={pinnedLeftTab.id} />
|
||||
)}
|
||||
|
||||
<StyledTabsAndDashboardContainer>
|
||||
<PageLayoutTabListEffect
|
||||
tabs={sortedTabs}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
defaultTabToFocusOnMobileAndSidePanelId={
|
||||
currentPageLayout.defaultTabToFocusOnMobileAndSidePanelId ??
|
||||
undefined
|
||||
}
|
||||
/>
|
||||
{(sortedTabs.length > 1 || isPageLayoutInEditMode) && (
|
||||
<StyledPageLayoutTabListContainer>
|
||||
<PageLayoutTabList
|
||||
tabs={sortedTabs}
|
||||
behaveAsLinks={!isInSidePanel && !isPageLayoutInEditMode}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
onAddTab={handleAddTab}
|
||||
isReorderEnabled={canEnableTabEditing}
|
||||
onReorder={
|
||||
canEnableTabEditing
|
||||
? (result, provided) =>
|
||||
reorderRecordPageTabs(
|
||||
result,
|
||||
provided,
|
||||
isDefined(pinnedLeftTab),
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
pageLayoutType={currentPageLayout.type}
|
||||
/>
|
||||
</StyledPageLayoutTabListContainer>
|
||||
)}
|
||||
|
||||
<StyledScrollWrapperContainer>
|
||||
<ScrollWrapper
|
||||
componentInstanceId={getScrollWrapperInstanceIdFromPageLayoutId(
|
||||
currentPageLayout.id,
|
||||
)}
|
||||
defaultEnableXScroll={false}
|
||||
>
|
||||
{isDefined(activeTabId) && activeTabExistsInCurrentPageLayout && (
|
||||
<PageLayoutMainContent tabId={activeTabId} />
|
||||
)}
|
||||
</ScrollWrapper>
|
||||
</StyledScrollWrapperContainer>
|
||||
</StyledTabsAndDashboardContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
+19
-9
@@ -2,6 +2,7 @@ import { useCreatePageLayoutGraphWidget } from '@/page-layout/hooks/useCreatePag
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { type GraphWidgetFieldSelection } from '@/page-layout/types/GraphWidgetFieldSelection';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
@@ -48,9 +49,12 @@ describe('useCreatePageLayoutGraphWidget', () => {
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const createWidget = useCreatePageLayoutGraphWidget(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const createWidget = useCreatePageLayoutGraphWidget({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
});
|
||||
return {
|
||||
setActiveTabId,
|
||||
setPageLayoutDraft,
|
||||
@@ -128,9 +132,12 @@ describe('useCreatePageLayoutGraphWidget', () => {
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const createWidget = useCreatePageLayoutGraphWidget(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const createWidget = useCreatePageLayoutGraphWidget({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
});
|
||||
return {
|
||||
setPageLayoutDraft,
|
||||
setActiveTabId,
|
||||
@@ -235,9 +242,12 @@ describe('useCreatePageLayoutGraphWidget', () => {
|
||||
it('should throw an error when activeTabId is null', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const createWidget = useCreatePageLayoutGraphWidget(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const createWidget = useCreatePageLayoutGraphWidget({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
});
|
||||
return { createWidget };
|
||||
},
|
||||
{
|
||||
|
||||
+91
-13
@@ -1,12 +1,15 @@
|
||||
import { useCreatePageLayoutTab } from '@/page-layout/hooks/useCreatePageLayoutTab';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
PageLayoutTabLayoutMode,
|
||||
PageLayoutType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import {
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
PageLayoutTestWrapper,
|
||||
@@ -27,7 +30,12 @@ describe('useCreatePageLayoutTab', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
createTab: useCreatePageLayoutTab({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
pageLayoutDraft: useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
@@ -36,11 +44,6 @@ describe('useCreatePageLayoutTab', () => {
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
activeTabId: useSetAtom(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: PageLayoutTestWrapper,
|
||||
@@ -55,6 +58,9 @@ describe('useCreatePageLayoutTab', () => {
|
||||
expect(result.current.pageLayoutDraft.tabs[0].id).toBe('mock-uuid');
|
||||
expect(result.current.pageLayoutDraft.tabs[0].title).toBe('Tab 1');
|
||||
expect(result.current.pageLayoutDraft.tabs[0].position).toBe(0);
|
||||
expect(result.current.pageLayoutDraft.tabs[0].layoutMode).toBe(
|
||||
PageLayoutTabLayoutMode.GRID,
|
||||
);
|
||||
expect(result.current.pageLayoutDraft.tabs[0].widgets).toEqual([]);
|
||||
|
||||
expect(result.current.pageLayoutCurrentLayouts['mock-uuid']).toEqual({
|
||||
@@ -69,7 +75,12 @@ describe('useCreatePageLayoutTab', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
createTab: useCreatePageLayoutTab({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
pageLayoutDraft: useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
@@ -97,7 +108,12 @@ describe('useCreatePageLayoutTab', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
createTab: useCreatePageLayoutTab({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
pageLayoutDraft: useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
@@ -125,6 +141,53 @@ describe('useCreatePageLayoutTab', () => {
|
||||
expect(result.current.pageLayoutDraft.tabs[1].title).toBe('Tab 2');
|
||||
});
|
||||
|
||||
it('should default layoutMode to VERTICAL_LIST for record page layouts', () => {
|
||||
const uuidModule = require('uuid');
|
||||
uuidModule.v4.mockReturnValue('mock-uuid');
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setPageLayoutDraft = useSetAtomComponentState(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const pageLayoutDraft = useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const createTab = useCreatePageLayoutTab({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
});
|
||||
return { setPageLayoutDraft, pageLayoutDraft, createTab };
|
||||
},
|
||||
{
|
||||
wrapper: PageLayoutTestWrapper,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setPageLayoutDraft({
|
||||
id: 'test-layout',
|
||||
name: 'Test Layout',
|
||||
type: PageLayoutType.RECORD_PAGE,
|
||||
objectMetadataId: null,
|
||||
tabs: [],
|
||||
});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.createTab.createPageLayoutTab();
|
||||
});
|
||||
|
||||
expect(result.current.pageLayoutDraft.tabs).toHaveLength(1);
|
||||
expect(result.current.pageLayoutDraft.tabs[0].layoutMode).toBe(
|
||||
PageLayoutTabLayoutMode.VERTICAL_LIST,
|
||||
);
|
||||
});
|
||||
|
||||
it('should create isolated layouts for multiple tabs', () => {
|
||||
const uuidModule = require('uuid');
|
||||
uuidModule.v4
|
||||
@@ -133,7 +196,12 @@ describe('useCreatePageLayoutTab', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
createTab: useCreatePageLayoutTab({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
pageLayoutCurrentLayouts: useAtomComponentStateValue(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
@@ -179,7 +247,12 @@ describe('useCreatePageLayoutTab', () => {
|
||||
`${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`,
|
||||
);
|
||||
return {
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
createTab: useCreatePageLayoutTab({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
activeTabId: activeTabId,
|
||||
};
|
||||
},
|
||||
@@ -211,7 +284,12 @@ describe('useCreatePageLayoutTab', () => {
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const createTab = useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID);
|
||||
const createTab = useCreatePageLayoutTab({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
});
|
||||
return { setPageLayoutDraft, pageLayoutDraft, createTab };
|
||||
},
|
||||
{
|
||||
|
||||
+21
-5
@@ -1,9 +1,10 @@
|
||||
import { usePageLayoutHandleLayoutChange } from '@/page-layout/hooks/usePageLayoutHandleLayoutChange';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { usePageLayoutHandleLayoutChange } from '@/page-layout/hooks/usePageLayoutHandleLayoutChange';
|
||||
import {
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
PageLayoutTestWrapper,
|
||||
@@ -13,7 +14,12 @@ describe('usePageLayoutHandleLayoutChange', () => {
|
||||
it('should update layouts for specific tab only', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
handler: usePageLayoutHandleLayoutChange(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
handler: usePageLayoutHandleLayoutChange({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
layouts: useAtomComponentStateValue(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
@@ -56,7 +62,12 @@ describe('usePageLayoutHandleLayoutChange', () => {
|
||||
it('should isolate layouts between different tabs', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
handler: usePageLayoutHandleLayoutChange(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
handler: usePageLayoutHandleLayoutChange({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
layouts: useAtomComponentStateValue(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
@@ -91,7 +102,12 @@ describe('usePageLayoutHandleLayoutChange', () => {
|
||||
it('should not update layouts when activeTabId is null', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
handler: usePageLayoutHandleLayoutChange(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
handler: usePageLayoutHandleLayoutChange({
|
||||
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
tabListInstanceId: getTabListInstanceIdFromPageLayoutId(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
layouts: useAtomComponentStateValue(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
|
||||
+8
-5
@@ -7,7 +7,6 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
|
||||
import { createDefaultFrontComponentWidget } from '@/page-layout/utils/createDefaultFrontComponentWidget';
|
||||
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';
|
||||
@@ -19,9 +18,13 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { WidgetType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useCreatePageLayoutFrontComponentWidget = (
|
||||
pageLayoutIdFromProps?: string,
|
||||
) => {
|
||||
export const useCreatePageLayoutFrontComponentWidget = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
@@ -29,7 +32,7 @@ export const useCreatePageLayoutFrontComponentWidget = (
|
||||
|
||||
const activeTabId = useAtomComponentStateValue(
|
||||
activeTabIdComponentState,
|
||||
getTabListInstanceIdFromPageLayoutId(pageLayoutId),
|
||||
tabListInstanceId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useAtomComponentStateCallbackState(
|
||||
|
||||
+8
-7
@@ -1,4 +1,3 @@
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
import { useDateTimeFormat } from '@/localization/hooks/useDateTimeFormat';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
@@ -9,10 +8,10 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
|
||||
import { createDefaultGraphWidget } from '@/page-layout/utils/createDefaultGraphWidget';
|
||||
import { getDefaultWidgetPosition } from '@/page-layout/utils/getDefaultWidgetPosition';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
|
||||
import { getWidgetSize } from '@/page-layout/utils/getWidgetSize';
|
||||
import { getWidgetTitle } from '@/page-layout/utils/getWidgetTitle';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
@@ -26,9 +25,13 @@ import {
|
||||
WidgetType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const useCreatePageLayoutGraphWidget = (
|
||||
pageLayoutIdFromProps?: string,
|
||||
) => {
|
||||
export const useCreatePageLayoutGraphWidget = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
@@ -37,8 +40,6 @@ export const useCreatePageLayoutGraphWidget = (
|
||||
const store = useStore();
|
||||
const { timeZone, calendarStartDay } = useDateTimeFormat();
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
|
||||
const pageLayoutDraftState = useAtomComponentStateCallbackState(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
|
||||
+8
-5
@@ -7,7 +7,6 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
|
||||
import { createDefaultIframeWidget } from '@/page-layout/utils/createDefaultIframeWidget';
|
||||
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';
|
||||
@@ -19,9 +18,13 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { WidgetType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useCreatePageLayoutIframeWidget = (
|
||||
pageLayoutIdFromProps?: string,
|
||||
) => {
|
||||
export const useCreatePageLayoutIframeWidget = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
@@ -29,7 +32,7 @@ export const useCreatePageLayoutIframeWidget = (
|
||||
|
||||
const activeTabId = useAtomComponentStateValue(
|
||||
activeTabIdComponentState,
|
||||
getTabListInstanceIdFromPageLayoutId(pageLayoutId),
|
||||
tabListInstanceId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useAtomComponentStateCallbackState(
|
||||
|
||||
+7
-5
@@ -6,7 +6,6 @@ import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLa
|
||||
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
|
||||
import { createDefaultStandaloneRichTextWidget } from '@/page-layout/utils/createDefaultStandaloneRichTextWidget';
|
||||
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';
|
||||
@@ -21,16 +20,19 @@ import {
|
||||
WidgetType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const useCreatePageLayoutStandaloneRichTextWidget = (
|
||||
pageLayoutIdFromProps?: string,
|
||||
) => {
|
||||
export const useCreatePageLayoutStandaloneRichTextWidget = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useAtomComponentStateCallbackState(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
|
||||
@@ -2,8 +2,8 @@ import { PageLayoutComponentInstanceContext } from '@/page-layout/states/context
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
import { getDefaultTabLayoutMode } from '@/page-layout/utils/getDefaultTabLayoutMode';
|
||||
import { getEmptyTabLayout } from '@/page-layout/utils/getEmptyTabLayout';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
@@ -12,7 +12,13 @@ import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
export const useCreatePageLayoutTab = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
@@ -28,7 +34,6 @@ export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
const setActiveTabId = useSetAtomComponentState(
|
||||
activeTabIdComponentState,
|
||||
tabListInstanceId,
|
||||
@@ -52,6 +57,7 @@ export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
title: title || `Tab ${tabsLength + 1}`,
|
||||
position: maxPosition + 1,
|
||||
pageLayoutId: pageLayoutId,
|
||||
layoutMode: getDefaultTabLayoutMode(pageLayoutDraft.type),
|
||||
widgets: [],
|
||||
isOverridden: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { removeTabLayouts } from '@/page-layout/utils/removeTabLayouts';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
@@ -10,7 +9,13 @@ import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/h
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
export const useDeletePageLayoutTab = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
@@ -27,7 +32,6 @@ export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
|
||||
const activeTabIdAtom = activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
import { generateDuplicatedTimestamps } from '@/page-layout/utils/generateDuplicatedTimestamps';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { calculateNewPosition } from '@/ui/layout/draggable-list/utils/calculateNewPosition';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
@@ -16,10 +14,17 @@ import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/h
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { appendCopySuffix, isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const useDuplicatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
export const useDuplicatePageLayoutTab = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
@@ -37,7 +42,6 @@ export const useDuplicatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
const setActiveTabId = useSetAtomComponentState(
|
||||
activeTabIdComponentState,
|
||||
tabListInstanceId,
|
||||
@@ -128,14 +132,14 @@ export const useDuplicatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
|
||||
setActiveTabId(newTabId);
|
||||
|
||||
setPageLayoutTabSettingsOpenTabId(newTabId);
|
||||
|
||||
navigatePageLayoutSidePanel({
|
||||
sidePanelPage: SidePanelPages.PageLayoutTabSettings,
|
||||
pageTitle: newTab.title,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
|
||||
setPageLayoutTabSettingsOpenTabId(newTabId);
|
||||
|
||||
return newTabId;
|
||||
},
|
||||
[
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { calculateNewPosition } from '@/ui/layout/draggable-list/utils/calculateNewPosition';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
import { useStore } from 'jotai';
|
||||
@@ -25,23 +24,25 @@ export const useMovePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
store.set(pageLayoutDraftState, (prev) => {
|
||||
const sorted = sortTabsByPosition(prev.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
if (index <= 0) return prev;
|
||||
if (index <= 0) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
const items = sorted.filter((t) => t.id !== tabId);
|
||||
const destinationIndex = index - 1;
|
||||
const sourceIndex = index;
|
||||
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex,
|
||||
sourceIndex,
|
||||
items,
|
||||
});
|
||||
const neighborId = sorted[index - 1].id;
|
||||
const currentPosition = sorted[index].position;
|
||||
const neighborPosition = sorted[index - 1].position;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, position: newPosition } : t,
|
||||
),
|
||||
tabs: prev.tabs.map((t) => {
|
||||
if (t.id === tabId) {
|
||||
return { ...t, position: neighborPosition };
|
||||
}
|
||||
if (t.id === neighborId) {
|
||||
return { ...t, position: currentPosition };
|
||||
}
|
||||
return t;
|
||||
}),
|
||||
};
|
||||
});
|
||||
},
|
||||
@@ -53,23 +54,25 @@ export const useMovePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
store.set(pageLayoutDraftState, (prev) => {
|
||||
const sorted = sortTabsByPosition(prev.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
if (index < 0 || index >= sorted.length - 1) return prev;
|
||||
if (index < 0 || index >= sorted.length - 1) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
const items = sorted.filter((t) => t.id !== tabId);
|
||||
const destinationIndex = index + 1;
|
||||
const sourceIndex = index;
|
||||
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex,
|
||||
sourceIndex,
|
||||
items,
|
||||
});
|
||||
const neighborId = sorted[index + 1].id;
|
||||
const currentPosition = sorted[index].position;
|
||||
const neighborPosition = sorted[index + 1].position;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, position: newPosition } : t,
|
||||
),
|
||||
tabs: prev.tabs.map((t) => {
|
||||
if (t.id === tabId) {
|
||||
return { ...t, position: neighborPosition };
|
||||
}
|
||||
if (t.id === neighborId) {
|
||||
return { ...t, position: currentPosition };
|
||||
}
|
||||
return t;
|
||||
}),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
+11
-9
@@ -1,26 +1,28 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { convertLayoutsToWidgets } from '@/page-layout/utils/convertLayoutsToWidgets';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
import { useStore } from 'jotai';
|
||||
import { type Layout, type Layouts } from 'react-grid-layout';
|
||||
import { useCallback } from 'react';
|
||||
import { type Layout, type Layouts } from 'react-grid-layout';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { convertLayoutsToWidgets } from '@/page-layout/utils/convertLayoutsToWidgets';
|
||||
|
||||
export const usePageLayoutHandleLayoutChange = (
|
||||
pageLayoutIdFromProps?: string,
|
||||
) => {
|
||||
export const usePageLayoutHandleLayoutChange = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useAtomComponentStateCallbackState(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
|
||||
@@ -2,7 +2,6 @@ import { PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS } from '@/page-layout/components/Pag
|
||||
import { useCurrentPageLayout } from '@/page-layout/hooks/useCurrentPageLayout';
|
||||
import { usePageLayoutDraftState } from '@/page-layout/hooks/usePageLayoutDraftState';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { calculateNewPosition } from '@/ui/layout/draggable-list/utils/calculateNewPosition';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { type DropResult } from '@hello-pangea/dnd';
|
||||
import { useCallback } from 'react';
|
||||
@@ -41,53 +40,40 @@ export const useReorderPageLayoutTabs = (pageLayoutIdFromProps?: string) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
const isDropOnMoreButton =
|
||||
destination.droppableId ===
|
||||
PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS.MORE_BUTTON
|
||||
) {
|
||||
const maxPosition =
|
||||
sortedTabs.length > 0
|
||||
? Math.max(...sortedTabs.map((tab) => tab.position))
|
||||
: 0;
|
||||
PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS.MORE_BUTTON;
|
||||
|
||||
setPageLayoutDraft((prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) =>
|
||||
tab.id === draggableId
|
||||
? { ...tab, position: maxPosition + 1 }
|
||||
: tab,
|
||||
),
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const tabsWithoutDragged = sortedTabs.filter(
|
||||
(tab) => tab.id !== draggableId,
|
||||
);
|
||||
const orderedIds = sortedTabs
|
||||
.map((tab) => tab.id)
|
||||
.filter((id) => id !== draggableId);
|
||||
|
||||
const movingBetweenDroppables =
|
||||
source.droppableId !== destination.droppableId;
|
||||
|
||||
const destinationIndexAdjusted =
|
||||
movingBetweenDroppables && destination.index > source.index
|
||||
const insertIndex = isDropOnMoreButton
|
||||
? orderedIds.length
|
||||
: movingBetweenDroppables && destination.index > source.index
|
||||
? destination.index - 1
|
||||
: destination.index;
|
||||
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex: destinationIndexAdjusted,
|
||||
sourceIndex: source.index,
|
||||
items: tabsWithoutDragged,
|
||||
});
|
||||
orderedIds.splice(insertIndex, 0, draggableId);
|
||||
|
||||
const newPositionById = new Map(
|
||||
orderedIds.map((id, index) => [id, index]),
|
||||
);
|
||||
|
||||
setPageLayoutDraft((prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) =>
|
||||
tab.id === draggableId ? { ...tab, position: newPosition } : tab,
|
||||
),
|
||||
tabs: prev.tabs.map((tab) => {
|
||||
const newPosition = newPositionById.get(tab.id);
|
||||
return isDefined(newPosition)
|
||||
? { ...tab, position: newPosition }
|
||||
: tab;
|
||||
}),
|
||||
}));
|
||||
|
||||
return false;
|
||||
return isDropOnMoreButton;
|
||||
},
|
||||
[currentPageLayout, setPageLayoutDraft],
|
||||
);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useReorderPageLayoutTabs } from '@/page-layout/hooks/useReorderPageLayoutTabs';
|
||||
import { type DropResult, type ResponderProvided } from '@hello-pangea/dnd';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useReorderRecordPageLayoutTabs = (
|
||||
pageLayoutIdFromProps?: string,
|
||||
) => {
|
||||
const { reorderTabs } = useReorderPageLayoutTabs(pageLayoutIdFromProps);
|
||||
|
||||
const reorderRecordPageTabs = useCallback(
|
||||
(
|
||||
result: DropResult,
|
||||
provided: ResponderProvided,
|
||||
hasPinnedTab: boolean,
|
||||
): boolean => {
|
||||
if (!hasPinnedTab) {
|
||||
return reorderTabs(result);
|
||||
}
|
||||
|
||||
const { source, destination } = result;
|
||||
|
||||
if (!isDefined(destination)) {
|
||||
return reorderTabs(result);
|
||||
}
|
||||
|
||||
const adjustedResult: DropResult = {
|
||||
...result,
|
||||
source: {
|
||||
...source,
|
||||
index: source.index + 1,
|
||||
},
|
||||
destination: {
|
||||
...destination,
|
||||
index: destination.index + 1,
|
||||
},
|
||||
};
|
||||
|
||||
return reorderTabs(adjustedResult);
|
||||
},
|
||||
[reorderTabs],
|
||||
);
|
||||
|
||||
return { reorderRecordPageTabs };
|
||||
};
|
||||
+8
-7
@@ -10,7 +10,6 @@ import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pag
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
|
||||
import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLayoutToTabLayouts';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
@@ -18,17 +17,19 @@ import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useResetDraftPageLayoutToPersistedPageLayout = (
|
||||
pageLayoutIdFromProps?: string,
|
||||
) => {
|
||||
export const useResetDraftPageLayoutToPersistedPageLayout = ({
|
||||
pageLayoutId: pageLayoutIdFromProps,
|
||||
tabListInstanceId,
|
||||
}: {
|
||||
pageLayoutId?: string;
|
||||
tabListInstanceId: string;
|
||||
}) => {
|
||||
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
const tabListComponentInstanceId =
|
||||
getTabListInstanceIdFromPageLayoutId(componentInstanceId);
|
||||
|
||||
const pageLayoutDraftState = useAtomComponentStateCallbackState(
|
||||
pageLayoutDraftComponentState,
|
||||
@@ -46,7 +47,7 @@ export const useResetDraftPageLayoutToPersistedPageLayout = (
|
||||
);
|
||||
|
||||
const activeTabId = activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListComponentInstanceId,
|
||||
instanceId: tabListInstanceId,
|
||||
});
|
||||
|
||||
const fieldsWidgetGroupsDraftState = useAtomComponentStateCallbackState(
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useSetAsPinnedTab = (pageLayoutIdFromProps?: string) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
PageLayoutComponentInstanceContext,
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useAtomComponentStateCallbackState(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const setAsPinnedTab = useCallback(
|
||||
(tabId: string) => {
|
||||
store.set(pageLayoutDraftState, (prev) => {
|
||||
const minPosition = Math.min(...prev.tabs.map((t) => t.position));
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, position: minPosition - 1 } : t,
|
||||
),
|
||||
};
|
||||
});
|
||||
},
|
||||
[pageLayoutDraftState, store],
|
||||
);
|
||||
|
||||
return { setAsPinnedTab };
|
||||
};
|
||||
+20
@@ -443,4 +443,24 @@ describe('getTabsByDisplayMode', () => {
|
||||
expect(resultBothTrue.pinnedLeftTab).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('pinned tab selection by position', () => {
|
||||
it('should pin the tab with the lowest position regardless of array order', () => {
|
||||
const tab1 = { ...createMockTab('tab-1'), position: 2 };
|
||||
const tab2 = { ...createMockTab('tab-2'), position: 0 };
|
||||
const tab3 = { ...createMockTab('tab-3'), position: 1 };
|
||||
|
||||
const result = getTabsByDisplayMode({
|
||||
tabs: [tab1, tab2, tab3],
|
||||
pageLayoutType: PageLayoutType.RECORD_PAGE,
|
||||
isMobile: false,
|
||||
isInSidePanel: false,
|
||||
});
|
||||
|
||||
expect(result.pinnedLeftTab?.id).toBe('tab-2');
|
||||
expect(result.tabsToRenderInTabList).toHaveLength(2);
|
||||
expect(result.tabsToRenderInTabList[0].id).toBe('tab-3');
|
||||
expect(result.tabsToRenderInTabList[1].id).toBe('tab-1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+30
-5
@@ -7,29 +7,54 @@ describe('shouldEnableTabEditingFeatures', () => {
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for RECORD_PAGE layout type', () => {
|
||||
it('should return false for RECORD_PAGE layout type without flag', () => {
|
||||
const result = shouldEnableTabEditingFeatures(PageLayoutType.RECORD_PAGE);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for RECORD_PAGE layout type with flag enabled', () => {
|
||||
const result = shouldEnableTabEditingFeatures(
|
||||
PageLayoutType.RECORD_PAGE,
|
||||
true,
|
||||
);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for RECORD_PAGE layout type with flag disabled', () => {
|
||||
const result = shouldEnableTabEditingFeatures(
|
||||
PageLayoutType.RECORD_PAGE,
|
||||
false,
|
||||
);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for RECORD_INDEX layout type', () => {
|
||||
const result = shouldEnableTabEditingFeatures(PageLayoutType.RECORD_INDEX);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for RECORD_INDEX layout type even with flag', () => {
|
||||
const result = shouldEnableTabEditingFeatures(
|
||||
PageLayoutType.RECORD_INDEX,
|
||||
true,
|
||||
);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
describe('behavior validation', () => {
|
||||
it('should enable tab editing features only for dashboards', () => {
|
||||
// Dashboards should allow adding tabs and opening settings on click
|
||||
it('should enable tab editing features only for dashboards and record pages with flag', () => {
|
||||
expect(shouldEnableTabEditingFeatures(PageLayoutType.DASHBOARD)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
// Record pages should NOT allow adding tabs or opening settings on click
|
||||
expect(shouldEnableTabEditingFeatures(PageLayoutType.RECORD_PAGE)).toBe(
|
||||
false,
|
||||
);
|
||||
|
||||
// Record index pages should NOT allow adding tabs or opening settings on click
|
||||
expect(
|
||||
shouldEnableTabEditingFeatures(PageLayoutType.RECORD_PAGE, true),
|
||||
).toBe(true);
|
||||
|
||||
expect(shouldEnableTabEditingFeatures(PageLayoutType.RECORD_INDEX)).toBe(
|
||||
false,
|
||||
);
|
||||
|
||||
+1
@@ -49,6 +49,7 @@ export const convertPageLayoutDraftToUpdateInput = (
|
||||
id: tab.id,
|
||||
title: tab.title,
|
||||
position: tab.position,
|
||||
layoutMode: tab.layoutMode,
|
||||
widgets: tab.widgets
|
||||
.filter((widget) => !isDynamicRelationWidget(widget))
|
||||
.map((widget, widgetIndex) => ({
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import {
|
||||
PageLayoutTabLayoutMode,
|
||||
PageLayoutType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const getDefaultTabLayoutMode = (
|
||||
pageLayoutType: PageLayoutType,
|
||||
): PageLayoutTabLayoutMode => {
|
||||
if (pageLayoutType === PageLayoutType.RECORD_PAGE) {
|
||||
return PageLayoutTabLayoutMode.VERTICAL_LIST;
|
||||
}
|
||||
|
||||
return PageLayoutTabLayoutMode.GRID;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
type GetTabsByDisplayModeParams = {
|
||||
@@ -32,8 +33,10 @@ export const getTabsByDisplayMode = ({
|
||||
};
|
||||
}
|
||||
|
||||
const tabsToRenderInTabList = tabs.slice(1);
|
||||
const pinnedLeftTab = tabs[0];
|
||||
const sortedTabs = sortTabsByPosition(tabs);
|
||||
|
||||
const tabsToRenderInTabList = sortedTabs.slice(1);
|
||||
const pinnedLeftTab = sortedTabs[0];
|
||||
|
||||
return {
|
||||
tabsToRenderInTabList,
|
||||
|
||||
+13
-1
@@ -2,6 +2,18 @@ import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const shouldEnableTabEditingFeatures = (
|
||||
pageLayoutType: PageLayoutType,
|
||||
isRecordPageGlobalEditionEnabled?: boolean,
|
||||
): boolean => {
|
||||
return pageLayoutType === PageLayoutType.DASHBOARD;
|
||||
if (pageLayoutType === PageLayoutType.DASHBOARD) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
pageLayoutType === PageLayoutType.RECORD_PAGE &&
|
||||
isRecordPageGlobalEditionEnabled
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import { SidePanelPageLayoutInfoContent } from '@/side-panel/components/SidePanelPageLayoutInfoContent';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SidePanelDashboardPageLayoutInfo = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
|
||||
if (!isDefined(pageLayoutId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <SidePanelPageLayoutInfoContent pageLayoutId={pageLayoutId} />;
|
||||
};
|
||||
+6
-23
@@ -1,30 +1,13 @@
|
||||
import { SidePanelDashboardPageLayoutInfo } from '@/side-panel/components/SidePanelDashboardPageLayoutInfo';
|
||||
import { SidePanelRecordPageLayoutInfo } from '@/side-panel/components/SidePanelRecordPageLayoutInfo';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { SidePanelPageLayoutInfoContent } from '@/side-panel/components/SidePanelPageLayoutInfoContent';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SidePanelPageLayoutInfo = () => {
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
if (!isDefined(contextStoreCurrentObjectMetadataItemId)) {
|
||||
throw new Error('Object metadata ID is not defined');
|
||||
if (!isDefined(pageLayoutId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: contextStoreCurrentObjectMetadataItemId,
|
||||
});
|
||||
|
||||
const isDashboardContext =
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Dashboard;
|
||||
|
||||
if (isDashboardContext) {
|
||||
return <SidePanelDashboardPageLayoutInfo />;
|
||||
}
|
||||
|
||||
return <SidePanelRecordPageLayoutInfo />;
|
||||
return <SidePanelPageLayoutInfoContent pageLayoutId={pageLayoutId} />;
|
||||
};
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { SidePanelPageLayoutInfoContent } from '@/side-panel/components/SidePanelPageLayoutInfoContent';
|
||||
import { usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord';
|
||||
|
||||
export const SidePanelRecordPageLayoutInfo = () => {
|
||||
const { pageLayoutId } =
|
||||
usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord();
|
||||
|
||||
return <SidePanelPageLayoutInfoContent pageLayoutId={pageLayoutId} />;
|
||||
};
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { ChartFiltersSettingsInitializeStateEffect } from '@/side-panel/pages/page-layout/components/ChartFiltersSettingsInitializeStateEffect';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { type ChartWidget } from '@/side-panel/pages/page-layout/types/ChartWidget';
|
||||
import { type ChartWidgetConfiguration } from '@/side-panel/pages/page-layout/types/ChartWidgetConfiguration';
|
||||
@@ -41,7 +41,7 @@ export const ChartFiltersSettings = ({
|
||||
objectMetadataItemId: objectMetadataItem.id,
|
||||
});
|
||||
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import { ChartTypeSelectionSection } from '@/side-panel/pages/page-layout/compon
|
||||
import { CHART_SETTINGS_HEADINGS } from '@/side-panel/pages/page-layout/constants/ChartSettingsHeadings';
|
||||
import { GRAPH_TYPE_INFORMATION } from '@/side-panel/pages/page-layout/constants/GraphTypeInformation';
|
||||
import { useChartSettingsValues } from '@/side-panel/pages/page-layout/hooks/useChartSettingsValues';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useGetConfigToUpdateAfterGraphTypeChange } from '@/side-panel/pages/page-layout/hooks/useUpdateGraphTypeConfig';
|
||||
import { CHART_CONFIGURATION_SETTING_IDS } from '@/side-panel/pages/page-layout/types/ChartConfigurationSettingIds';
|
||||
@@ -34,7 +34,7 @@ const StyledSidePanelContainer = styled.div`
|
||||
|
||||
export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
|
||||
const { updateSidePanelPageInfo } = useUpdateSidePanelPageInfo();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
+2
-2
@@ -2,14 +2,14 @@ import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMeta
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { ChartFiltersSettings } from '@/side-panel/pages/page-layout/components/ChartFiltersSettings';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { isChartWidget } from '@/side-panel/pages/page-layout/utils/isChartWidget';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SidePanelChartFilterSubPage = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const pageLayoutDraft = useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
|
||||
+2
-3
@@ -1,7 +1,7 @@
|
||||
import { useTemporaryFieldsConfiguration } from '@/page-layout/hooks/useTemporaryFieldsConfiguration';
|
||||
import { FieldsConfigurationEditor } from '@/page-layout/widgets/fields/components/FieldsConfigurationEditor';
|
||||
import { FieldsWidgetGroupsDraftInitializationEffect } from '@/page-layout/widgets/fields/components/FieldsWidgetGroupsDraftInitializationEffect';
|
||||
import { usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { styled } from '@linaria/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -21,8 +21,7 @@ const StyledFieldsLayoutContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const SidePanelFieldsLayoutSubPage = () => {
|
||||
const { pageLayoutId } =
|
||||
usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const temporaryFieldsConfiguration = useTemporaryFieldsConfiguration();
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pa
|
||||
import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext';
|
||||
import { ChartSettings } from '@/side-panel/pages/page-layout/components/ChartSettings';
|
||||
import { WidgetSettingsFooter } from '@/side-panel/pages/page-layout/components/WidgetSettingsFooter';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { isChartWidget } from '@/side-panel/pages/page-layout/utils/isChartWidget';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { styled } from '@linaria/react';
|
||||
@@ -16,7 +16,7 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const SidePanelPageLayoutChartSettings = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const pageLayoutDraft = useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
|
||||
import { NewFieldDefaultVisibilityToggle } from '@/side-panel/pages/page-layout/components/NewFieldDefaultVisibilityToggle';
|
||||
import { WidgetSettingsFooter } from '@/side-panel/pages/page-layout/components/WidgetSettingsFooter';
|
||||
import { usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
|
||||
@@ -34,7 +34,7 @@ export const SidePanelPageLayoutFieldsSettings = () => {
|
||||
const { t } = useLingui();
|
||||
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
|
||||
const { pageLayoutId, objectNameSingular } =
|
||||
usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord();
|
||||
usePageLayoutIdFromContextStore();
|
||||
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { WidgetSettingsFooter } from '@/side-panel/pages/page-layout/components/WidgetSettingsFooter';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
|
||||
import { useUpdatePageLayoutWidget } from '@/page-layout/hooks/useUpdatePageLayoutWidget';
|
||||
@@ -26,7 +26,7 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const SidePanelPageLayoutIframeSettings = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
|
||||
+8
-119
@@ -1,129 +1,18 @@
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { TAB_SETTINGS_SELECTABLE_ITEM_IDS } from '@/side-panel/pages/page-layout/constants/settings/TabSettingsSelectableItemIds';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { useDeletePageLayoutTab } from '@/page-layout/hooks/useDeletePageLayoutTab';
|
||||
import { useDuplicatePageLayoutTab } from '@/page-layout/hooks/useDuplicatePageLayoutTab';
|
||||
import { useMovePageLayoutTab } from '@/page-layout/hooks/useMovePageLayoutTab';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { SidePanelPageLayoutTabSettingsContent } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutTabSettingsContent';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconChevronLeft,
|
||||
IconChevronRight,
|
||||
IconCopyPlus,
|
||||
IconTrash,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const SidePanelPageLayoutTabSettings = () => {
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId, recordId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const pageLayoutDraft = useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const [pageLayoutTabSettingsOpenTabId, setPageLayoutTabSettingsOpenTabId] =
|
||||
useAtomComponentState(
|
||||
pageLayoutTabSettingsOpenTabIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const { moveLeft, moveRight } = useMovePageLayoutTab(pageLayoutId);
|
||||
const { deleteTab } = useDeletePageLayoutTab(pageLayoutId);
|
||||
const { duplicateTab } = useDuplicatePageLayoutTab(pageLayoutId);
|
||||
|
||||
if (!isDefined(pageLayoutTabSettingsOpenTabId)) {
|
||||
if (!isDefined(pageLayoutId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tabsSorted = sortTabsByPosition(pageLayoutDraft.tabs);
|
||||
const currentIndex = tabsSorted.findIndex(
|
||||
(t) => t.id === pageLayoutTabSettingsOpenTabId,
|
||||
);
|
||||
if (currentIndex < 0) return null;
|
||||
const tab = tabsSorted[currentIndex];
|
||||
const canMoveLeft = currentIndex > 0;
|
||||
const canMoveRight = currentIndex < tabsSorted.length - 1;
|
||||
const canDelete = tabsSorted.length > 1;
|
||||
|
||||
const handleDelete = () => {
|
||||
deleteTab(tab.id);
|
||||
setPageLayoutTabSettingsOpenTabId(null);
|
||||
closeSidePanelMenu();
|
||||
};
|
||||
|
||||
const selectableItemIds = [
|
||||
...(canMoveLeft ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_LEFT] : []),
|
||||
...(canMoveRight ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_RIGHT] : []),
|
||||
TAB_SETTINGS_SELECTABLE_ITEM_IDS.DUPLICATE,
|
||||
...(canDelete ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE] : []),
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidePanelList commandGroups={[]} selectableItemIds={selectableItemIds}>
|
||||
<SidePanelGroup heading={t`Settings`}>
|
||||
{canMoveLeft && (
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_LEFT}
|
||||
onEnter={() => moveLeft(tab.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_LEFT}
|
||||
Icon={IconChevronLeft}
|
||||
label={t`Move left`}
|
||||
onClick={() => moveLeft(tab.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{canMoveRight && (
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_RIGHT}
|
||||
onEnter={() => moveRight(tab.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_RIGHT}
|
||||
Icon={IconChevronRight}
|
||||
label={t`Move right`}
|
||||
onClick={() => moveRight(tab.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DUPLICATE}
|
||||
onEnter={() => duplicateTab(tab.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DUPLICATE}
|
||||
Icon={IconCopyPlus}
|
||||
label={t`Duplicate`}
|
||||
onClick={() => duplicateTab(tab.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
{canDelete && (
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE}
|
||||
onEnter={handleDelete}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE}
|
||||
Icon={IconTrash}
|
||||
label={t`Delete`}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
</SidePanelGroup>
|
||||
</SidePanelList>
|
||||
</>
|
||||
<SidePanelPageLayoutTabSettingsContent
|
||||
pageLayoutId={pageLayoutId}
|
||||
recordId={recordId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { useDeletePageLayoutTab } from '@/page-layout/hooks/useDeletePageLayoutTab';
|
||||
import { useDuplicatePageLayoutTab } from '@/page-layout/hooks/useDuplicatePageLayoutTab';
|
||||
import { useMovePageLayoutTab } from '@/page-layout/hooks/useMovePageLayoutTab';
|
||||
import { useSetAsPinnedTab } from '@/page-layout/hooks/useSetAsPinnedTab';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { TAB_SETTINGS_SELECTABLE_ITEM_IDS } from '@/side-panel/pages/page-layout/constants/settings/TabSettingsSelectableItemIds';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
IconChevronLeft,
|
||||
IconChevronRight,
|
||||
IconCopyPlus,
|
||||
IconPinned,
|
||||
IconTrash,
|
||||
} from 'twenty-ui/display';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
type SidePanelPageLayoutTabSettingsContentProps = {
|
||||
pageLayoutId: string;
|
||||
recordId: string;
|
||||
};
|
||||
|
||||
export const SidePanelPageLayoutTabSettingsContent = ({
|
||||
pageLayoutId,
|
||||
recordId,
|
||||
}: SidePanelPageLayoutTabSettingsContentProps) => {
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
const pageLayoutDraft = useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId,
|
||||
layoutType: pageLayoutDraft.type,
|
||||
targetRecordIdentifier: { id: recordId, targetObjectNameSingular: '' },
|
||||
});
|
||||
|
||||
const [pageLayoutTabSettingsOpenTabId, setPageLayoutTabSettingsOpenTabId] =
|
||||
useAtomComponentState(
|
||||
pageLayoutTabSettingsOpenTabIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const { moveLeft, moveRight } = useMovePageLayoutTab(pageLayoutId);
|
||||
const { deleteTab } = useDeletePageLayoutTab({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
const { duplicateTab } = useDuplicatePageLayoutTab({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
const { setAsPinnedTab } = useSetAsPinnedTab(pageLayoutId);
|
||||
|
||||
if (!isDefined(pageLayoutTabSettingsOpenTabId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const tabsSorted = sortTabsByPosition(pageLayoutDraft.tabs);
|
||||
const currentIndex = tabsSorted.findIndex(
|
||||
(t) => t.id === pageLayoutTabSettingsOpenTabId,
|
||||
);
|
||||
if (currentIndex < 0) return null;
|
||||
const tab = tabsSorted[currentIndex];
|
||||
const isRecordPage = pageLayoutDraft.type === PageLayoutType.RECORD_PAGE;
|
||||
const hasPinnedTab = isRecordPage && tabsSorted.length > 1;
|
||||
const canMoveLeft = hasPinnedTab ? currentIndex > 1 : currentIndex > 0;
|
||||
const canMoveRight = currentIndex < tabsSorted.length - 1;
|
||||
const canDelete = tabsSorted.length > 1;
|
||||
const isAlreadyPinned = currentIndex === 0;
|
||||
const canSetAsPinned =
|
||||
isRecordPage && !isAlreadyPinned && tabsSorted.length > 1;
|
||||
|
||||
const handleDelete = () => {
|
||||
deleteTab(tab.id);
|
||||
setPageLayoutTabSettingsOpenTabId(null);
|
||||
closeSidePanelMenu();
|
||||
};
|
||||
|
||||
const selectableItemIds = [
|
||||
...(canMoveLeft ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_LEFT] : []),
|
||||
...(canMoveRight ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_RIGHT] : []),
|
||||
...(canSetAsPinned ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.SET_AS_PINNED] : []),
|
||||
TAB_SETTINGS_SELECTABLE_ITEM_IDS.DUPLICATE,
|
||||
...(canDelete ? [TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE] : []),
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidePanelList commandGroups={[]} selectableItemIds={selectableItemIds}>
|
||||
<SidePanelGroup heading={t`Settings`}>
|
||||
{canMoveLeft && (
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_LEFT}
|
||||
onEnter={() => moveLeft(tab.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_LEFT}
|
||||
Icon={IconChevronLeft}
|
||||
label={t`Move left`}
|
||||
onClick={() => moveLeft(tab.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{canMoveRight && (
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_RIGHT}
|
||||
onEnter={() => moveRight(tab.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.MOVE_RIGHT}
|
||||
Icon={IconChevronRight}
|
||||
label={t`Move right`}
|
||||
onClick={() => moveRight(tab.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{canSetAsPinned && (
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.SET_AS_PINNED}
|
||||
onEnter={() => setAsPinnedTab(tab.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.SET_AS_PINNED}
|
||||
Icon={IconPinned}
|
||||
label={t`Set as pinned tab`}
|
||||
onClick={() => setAsPinnedTab(tab.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DUPLICATE}
|
||||
onEnter={() => duplicateTab(tab.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DUPLICATE}
|
||||
Icon={IconCopyPlus}
|
||||
label={t`Duplicate`}
|
||||
onClick={() => duplicateTab(tab.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
{canDelete && (
|
||||
<SelectableListItem
|
||||
itemId={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE}
|
||||
onEnter={handleDelete}
|
||||
>
|
||||
<CommandMenuItem
|
||||
id={TAB_SETTINGS_SELECTABLE_ITEM_IDS.DELETE}
|
||||
Icon={IconTrash}
|
||||
label={t`Delete`}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
</SidePanelGroup>
|
||||
</SidePanelList>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+36
-19
@@ -1,11 +1,4 @@
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { getFrontComponentWidgetTypeSelectItemId } from '@/side-panel/pages/page-layout/utils/getFrontComponentWidgetTypeSelectItemId';
|
||||
import { isExistingWidgetMissingOrDifferentType } from '@/side-panel/pages/page-layout/utils/isExistingWidgetMissingOrDifferentType';
|
||||
import { FIND_MANY_FRONT_COMPONENTS } from '@/front-components/graphql/queries/findManyFrontComponents';
|
||||
import { useCreatePageLayoutFrontComponentWidget } from '@/page-layout/hooks/useCreatePageLayoutFrontComponentWidget';
|
||||
import { useCreatePageLayoutGraphWidget } from '@/page-layout/hooks/useCreatePageLayoutGraphWidget';
|
||||
@@ -15,6 +8,14 @@ import { useOpportunityDefaultChartConfig } from '@/page-layout/hooks/useOpportu
|
||||
import { useRemovePageLayoutWidgetAndPreservePosition } from '@/page-layout/hooks/useRemovePageLayoutWidgetAndPreservePosition';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { getFrontComponentWidgetTypeSelectItemId } from '@/side-panel/pages/page-layout/utils/getFrontComponentWidgetTypeSelectItemId';
|
||||
import { isExistingWidgetMissingOrDifferentType } from '@/side-panel/pages/page-layout/utils/isExistingWidgetMissingOrDifferentType';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
@@ -36,7 +37,7 @@ import {
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const SidePanelPageLayoutWidgetTypeSelect = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId, recordId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
|
||||
@@ -44,17 +45,38 @@ export const SidePanelPageLayoutWidgetTypeSelect = () => {
|
||||
|
||||
const { buildBarChartFieldSelection } = useOpportunityDefaultChartConfig();
|
||||
|
||||
const { createPageLayoutGraphWidget } =
|
||||
useCreatePageLayoutGraphWidget(pageLayoutId);
|
||||
const pageLayoutDraft = useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const { createPageLayoutIframeWidget } =
|
||||
useCreatePageLayoutIframeWidget(pageLayoutId);
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId,
|
||||
layoutType: pageLayoutDraft.type,
|
||||
targetRecordIdentifier: { id: recordId, targetObjectNameSingular: '' },
|
||||
});
|
||||
|
||||
const { createPageLayoutGraphWidget } = useCreatePageLayoutGraphWidget({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const { createPageLayoutIframeWidget } = useCreatePageLayoutIframeWidget({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const { createPageLayoutStandaloneRichTextWidget } =
|
||||
useCreatePageLayoutStandaloneRichTextWidget(pageLayoutId);
|
||||
useCreatePageLayoutStandaloneRichTextWidget({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const { createPageLayoutFrontComponentWidget } =
|
||||
useCreatePageLayoutFrontComponentWidget(pageLayoutId);
|
||||
useCreatePageLayoutFrontComponentWidget({
|
||||
pageLayoutId,
|
||||
tabListInstanceId,
|
||||
});
|
||||
|
||||
const { removePageLayoutWidgetAndPreservePosition } =
|
||||
useRemovePageLayoutWidgetAndPreservePosition(pageLayoutId);
|
||||
@@ -84,11 +106,6 @@ export const SidePanelPageLayoutWidgetTypeSelect = () => {
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraft = useAtomComponentStateValue(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const existingWidget = isDefined(pageLayoutEditingWidgetId)
|
||||
? pageLayoutDraft.tabs
|
||||
.flatMap((tab) => tab.widgets)
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import { CommandMenuItemToggle } from '@/command-menu/components/CommandMenuItem
|
||||
import { SIDE_PANEL_SELECTABLE_LIST_ID } from '@/side-panel/constants/SidePanelSelectableListId';
|
||||
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
|
||||
import { useChartSettingsValues } from '@/side-panel/pages/page-layout/hooks/useChartSettingsValues';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateChartSettingInput } from '@/side-panel/pages/page-layout/hooks/useUpdateChartSettingInput';
|
||||
import { useUpdateChartSettingTextInput } from '@/side-panel/pages/page-layout/hooks/useUpdateChartSettingTextInput';
|
||||
import { useUpdateChartSettingToggle } from '@/side-panel/pages/page-layout/hooks/useUpdateChartSettingToggle';
|
||||
@@ -35,7 +35,7 @@ export const ChartSettingItem = ({
|
||||
objectMetadataId,
|
||||
configuration,
|
||||
}: ChartSettingItemProps) => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
|
||||
const { openDropdown } = useOpenDropdown();
|
||||
const { setSelectedItemId } = useSelectableList(
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
@@ -30,7 +30,7 @@ export const ChartAggregateOperationSelectableListItem = ({
|
||||
label: string;
|
||||
currentFieldMetadataId: string;
|
||||
}) => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { ChartAggregateOperationSelectableListItem } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem';
|
||||
import { ChartRatioAggregateOperationSelectableListItem } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem';
|
||||
import { ChartRatioOptionValueSelectionDropdownContent } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
@@ -35,7 +35,7 @@ export const ChartAggregateOperationSelectionDropdownContent = ({
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [isOptionValueMenuOpen, setIsOptionValueMenuOpen] = useState(false);
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const configuration = widgetInEditMode?.configuration;
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { getChartAxisNameDisplayOptions } from '@/side-panel/pages/page-layout/utils/getChartAxisNameDisplayOptions';
|
||||
@@ -15,7 +15,7 @@ import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import { AxisNameDisplay } from '~/generated-metadata/graphql';
|
||||
|
||||
export const ChartAxisNameSelectionDropdownContent = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const configuration = widgetInEditMode?.configuration;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { ChartColorGradientOption } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartColorGradientOption';
|
||||
import { ChartColorPaletteOption } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartColorPaletteOption';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { type ChartConfiguration } from '@/side-panel/pages/page-layout/types/ChartConfiguration';
|
||||
@@ -28,7 +28,7 @@ type ColorOption = {
|
||||
|
||||
export const ChartColorSelectionDropdownContent = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const dropdownId = useAvailableComponentInstanceIdOrThrow(
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useResetChartDraftFiltersSettings } from '@/side-panel/pages/page-layout/hooks/useResetChartDraftFiltersSettings';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
@@ -32,7 +32,7 @@ export const ChartDataSourceDropdownContent = () => {
|
||||
useState(false);
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { type ChartConfiguration } from '@/side-panel/pages/page-layout/types/ChartConfiguration';
|
||||
@@ -75,7 +75,7 @@ const isChartConfiguration = (
|
||||
export const ChartDateGranularitySelectionDropdownContent = ({
|
||||
axis,
|
||||
}: ChartDateGranularitySelectionDropdownContentProps) => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const configuration = widgetInEditMode?.configuration;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { ChartAggregateOperationSelectionDropdownContent } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectionDropdownContent';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
@@ -25,7 +25,7 @@ export const ChartFieldSelectionForAggregateOperationDropdownContent = () => {
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const configuration = widgetInEditMode?.configuration;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { ChartGroupByFieldSelectionCompositeFieldView } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionCompositeFieldView';
|
||||
import { ChartGroupByFieldSelectionRelationFieldView } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionRelationFieldView';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { type ChartConfiguration } from '@/side-panel/pages/page-layout/types/ChartConfiguration';
|
||||
@@ -51,7 +51,7 @@ export const ChartGroupByFieldSelectionDropdownContentBase = <
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { type DropResult } from '@hello-pangea/dnd';
|
||||
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
|
||||
@@ -32,7 +32,7 @@ export const ChartManualSortSubMenuContent = ({
|
||||
axis,
|
||||
onBack,
|
||||
}: ChartManualSortSubMenuContentProps) => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
@@ -17,7 +17,7 @@ export const ChartRatioAggregateOperationSelectableListItem = ({
|
||||
label: string;
|
||||
onSelect: () => void;
|
||||
}) => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const dropdownId = useAvailableComponentInstanceIdOrThrow(
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
@@ -21,7 +21,7 @@ export const ChartRatioOptionBooleanSelectableListItem = ({
|
||||
label: string;
|
||||
currentFieldMetadataId: string;
|
||||
}) => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
@@ -23,7 +23,7 @@ export const ChartRatioOptionSelectSelectableListItem = ({
|
||||
color: ThemeColor | undefined;
|
||||
currentFieldMetadataId: string;
|
||||
}) => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { ChartRatioOptionBooleanSelectableListItem } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem';
|
||||
import { ChartRatioOptionSelectSelectableListItem } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { isWidgetConfigurationOfType } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfType';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
@@ -35,7 +35,7 @@ export const ChartRatioOptionValueSelectionDropdownContent = ({
|
||||
}) => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const dropdownId = useAvailableComponentInstanceIdOrThrow(
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
||||
import { ChartManualSortSubMenuContent } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartManualSortSubMenuContent';
|
||||
import { AGGREGATE_SORT_BY_OPTIONS } from '@/side-panel/pages/page-layout/constants/AggregateSortByOptions';
|
||||
import { useGraphGroupBySortOptionLabels } from '@/side-panel/pages/page-layout/hooks/useGraphGroupBySortOptionLabels';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { filterSortOptionsByFieldType } from '@/side-panel/pages/page-layout/utils/filterSortOptionsByFieldType';
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
|
||||
export const ChartSortByGroupByFieldDropdownContent = () => {
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false);
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { updateCurrentWidgetConfig } =
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
||||
import { ChartManualSortSubMenuContent } from '@/side-panel/pages/page-layout/components/dropdown-content/ChartManualSortSubMenuContent';
|
||||
import { X_SORT_BY_OPTIONS } from '@/side-panel/pages/page-layout/constants/XSortByOptions';
|
||||
import { useGraphXSortOptionLabels } from '@/side-panel/pages/page-layout/hooks/useGraphXSortOptionLabels';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { filterSortOptionsByFieldType } from '@/side-panel/pages/page-layout/utils/filterSortOptionsByFieldType';
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
|
||||
export const ChartSortBySelectionDropdownContent = () => {
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false);
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { updateCurrentWidgetConfig } =
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
export const TAB_SETTINGS_SELECTABLE_ITEM_IDS = {
|
||||
MOVE_LEFT: 'tab-move-left',
|
||||
MOVE_RIGHT: 'tab-move-right',
|
||||
SET_AS_PINNED: 'tab-set-as-pinned',
|
||||
DUPLICATE: 'tab-duplicate',
|
||||
DELETE: 'tab-delete',
|
||||
} as const;
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
export const usePageLayoutIdFromContextStoreTargetedRecord = () => {
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
if (
|
||||
!(
|
||||
contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1
|
||||
)
|
||||
) {
|
||||
throw new Error('Only one recordStore should be selected');
|
||||
}
|
||||
|
||||
const recordId: string = contextStoreTargetedRecordsRule.selectedRecordIds[0];
|
||||
|
||||
const recordStore = useAtomFamilyStateValue(recordStoreFamilyState, recordId);
|
||||
|
||||
const currentPageLayoutId = useAtomStateValue(currentPageLayoutIdState);
|
||||
|
||||
return {
|
||||
pageLayoutId: recordStore?.pageLayoutId ?? currentPageLayoutId,
|
||||
};
|
||||
};
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { useRecordPageLayoutIdFromRecordStoreOrThrow } from '@/page-layout/hooks/useRecordPageLayoutIdFromRecordStoreOrThrow';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord =
|
||||
() => {
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
if (!isDefined(contextStoreCurrentObjectMetadataItemId)) {
|
||||
throw new Error('Object metadata ID is not defined');
|
||||
}
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: contextStoreCurrentObjectMetadataItemId ?? undefined,
|
||||
});
|
||||
|
||||
if (
|
||||
!(
|
||||
contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1
|
||||
)
|
||||
) {
|
||||
throw new Error('Only one record should be selected');
|
||||
}
|
||||
|
||||
const { pageLayoutId } = useRecordPageLayoutIdFromRecordStoreOrThrow({
|
||||
targetObjectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
return {
|
||||
pageLayoutId,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
};
|
||||
};
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
|
||||
import { recordPageLayoutByObjectMetadataIdFamilySelector } from '@/page-layout/states/selectors/recordPageLayoutByObjectMetadataIdFamilySelector';
|
||||
import { getDefaultRecordPageLayoutId } from '@/page-layout/utils/getDefaultRecordPageLayoutId';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const usePageLayoutIdFromContextStore = () => {
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
if (!isDefined(contextStoreCurrentObjectMetadataItemId)) {
|
||||
throw new Error('Object metadata ID is not defined');
|
||||
}
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: contextStoreCurrentObjectMetadataItemId,
|
||||
});
|
||||
|
||||
if (
|
||||
!(
|
||||
contextStoreTargetedRecordsRule.mode === 'selection' &&
|
||||
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1
|
||||
)
|
||||
) {
|
||||
throw new Error('Only one record should be selected');
|
||||
}
|
||||
|
||||
const recordId: string = contextStoreTargetedRecordsRule.selectedRecordIds[0];
|
||||
|
||||
const isDashboardContext =
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Dashboard;
|
||||
|
||||
const recordStore = useAtomFamilyStateValue(recordStoreFamilyState, recordId);
|
||||
const currentPageLayoutId = useAtomStateValue(currentPageLayoutIdState);
|
||||
|
||||
const recordPageLayout = useAtomFamilySelectorValue(
|
||||
recordPageLayoutByObjectMetadataIdFamilySelector,
|
||||
{ objectMetadataId: objectMetadataItem.id },
|
||||
);
|
||||
|
||||
const pageLayoutId = isDashboardContext
|
||||
? (recordStore?.pageLayoutId ?? currentPageLayoutId)
|
||||
: isDefined(recordPageLayout)
|
||||
? recordPageLayout.id
|
||||
: getDefaultRecordPageLayoutId({
|
||||
targetObjectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
return {
|
||||
pageLayoutId,
|
||||
recordId,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
};
|
||||
};
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { getChartFiltersSettingsInstanceId } from '@/side-panel/pages/page-layout/utils/getChartFiltersSettingsInstanceId';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
@@ -11,7 +11,7 @@ import { useStore } from 'jotai';
|
||||
|
||||
export const useResetChartDraftFiltersSettings = () => {
|
||||
const store = useStore();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
|
||||
@@ -1641,6 +1641,7 @@ enum FeatureFlagKey {
|
||||
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED
|
||||
IS_DRAFT_EMAIL_ENABLED
|
||||
IS_RICH_TEXT_V1_MIGRATED
|
||||
IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED
|
||||
}
|
||||
|
||||
type SSOIdentityProvider {
|
||||
@@ -3445,12 +3446,14 @@ input CreatePageLayoutTabInput {
|
||||
title: String!
|
||||
position: Float
|
||||
pageLayoutId: UUID!
|
||||
layoutMode: PageLayoutTabLayoutMode = GRID
|
||||
}
|
||||
|
||||
input UpdatePageLayoutTabInput {
|
||||
title: String
|
||||
position: Float
|
||||
icon: String
|
||||
layoutMode: PageLayoutTabLayoutMode
|
||||
}
|
||||
|
||||
input CreatePageLayoutInput {
|
||||
@@ -3477,6 +3480,7 @@ input UpdatePageLayoutTabWithWidgetsInput {
|
||||
title: String!
|
||||
position: Float!
|
||||
icon: String
|
||||
layoutMode: PageLayoutTabLayoutMode = GRID
|
||||
widgets: [UpdatePageLayoutWidgetWithIdInput!]!
|
||||
}
|
||||
|
||||
|
||||
@@ -1338,7 +1338,7 @@ export interface FeatureFlag {
|
||||
__typename: 'FeatureFlag'
|
||||
}
|
||||
|
||||
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_APPLICATION_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_DASHBOARD_V2_ENABLED' | 'IS_ATTACHMENT_MIGRATED' | 'IS_NOTE_TARGET_MIGRATED' | 'IS_TASK_TARGET_MIGRATED' | 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_ENABLED' | 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED'
|
||||
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_APPLICATION_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_DASHBOARD_V2_ENABLED' | 'IS_ATTACHMENT_MIGRATED' | 'IS_NOTE_TARGET_MIGRATED' | 'IS_TASK_TARGET_MIGRATED' | 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_ENABLED' | 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED' | 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED'
|
||||
|
||||
export interface SSOIdentityProvider {
|
||||
id: Scalars['UUID']
|
||||
@@ -5981,9 +5981,9 @@ export interface GridPositionInput {row: Scalars['Float'],column: Scalars['Float
|
||||
|
||||
export interface UpdatePageLayoutWidgetInput {title?: (Scalars['String'] | null),type?: (WidgetType | null),objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null)}
|
||||
|
||||
export interface CreatePageLayoutTabInput {title: Scalars['String'],position?: (Scalars['Float'] | null),pageLayoutId: Scalars['UUID']}
|
||||
export interface CreatePageLayoutTabInput {title: Scalars['String'],position?: (Scalars['Float'] | null),pageLayoutId: Scalars['UUID'],layoutMode?: (PageLayoutTabLayoutMode | null)}
|
||||
|
||||
export interface UpdatePageLayoutTabInput {title?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),icon?: (Scalars['String'] | null)}
|
||||
export interface UpdatePageLayoutTabInput {title?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),icon?: (Scalars['String'] | null),layoutMode?: (PageLayoutTabLayoutMode | null)}
|
||||
|
||||
export interface CreatePageLayoutInput {name: Scalars['String'],type?: (PageLayoutType | null),objectMetadataId?: (Scalars['UUID'] | null)}
|
||||
|
||||
@@ -5991,7 +5991,7 @@ export interface UpdatePageLayoutInput {name?: (Scalars['String'] | null),type?:
|
||||
|
||||
export interface UpdatePageLayoutWithTabsInput {name: Scalars['String'],type: PageLayoutType,objectMetadataId?: (Scalars['UUID'] | null),tabs: UpdatePageLayoutTabWithWidgetsInput[]}
|
||||
|
||||
export interface UpdatePageLayoutTabWithWidgetsInput {id: Scalars['UUID'],title: Scalars['String'],position: Scalars['Float'],icon?: (Scalars['String'] | null),widgets: UpdatePageLayoutWidgetWithIdInput[]}
|
||||
export interface UpdatePageLayoutTabWithWidgetsInput {id: Scalars['UUID'],title: Scalars['String'],position: Scalars['Float'],icon?: (Scalars['String'] | null),layoutMode?: (PageLayoutTabLayoutMode | null),widgets: UpdatePageLayoutWidgetWithIdInput[]}
|
||||
|
||||
export interface UpdatePageLayoutWidgetWithIdInput {id: Scalars['UUID'],pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition: GridPositionInput,position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null)}
|
||||
|
||||
@@ -8685,7 +8685,8 @@ export const enumFeatureFlagKey = {
|
||||
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' as const,
|
||||
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED: 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' as const,
|
||||
IS_DRAFT_EMAIL_ENABLED: 'IS_DRAFT_EMAIL_ENABLED' as const,
|
||||
IS_RICH_TEXT_V1_MIGRATED: 'IS_RICH_TEXT_V1_MIGRATED' as const
|
||||
IS_RICH_TEXT_V1_MIGRATED: 'IS_RICH_TEXT_V1_MIGRATED' as const,
|
||||
IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED: 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED' as const
|
||||
}
|
||||
|
||||
export const enumRelationType = {
|
||||
|
||||
@@ -8824,6 +8824,9 @@ export default {
|
||||
"pageLayoutId": [
|
||||
3
|
||||
],
|
||||
"layoutMode": [
|
||||
79
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
@@ -8838,6 +8841,9 @@ export default {
|
||||
"icon": [
|
||||
1
|
||||
],
|
||||
"layoutMode": [
|
||||
79
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
@@ -8900,6 +8906,9 @@ export default {
|
||||
"icon": [
|
||||
1
|
||||
],
|
||||
"layoutMode": [
|
||||
79
|
||||
],
|
||||
"widgets": [
|
||||
360
|
||||
],
|
||||
|
||||
+1
@@ -4,4 +4,5 @@ export const FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES = [
|
||||
'title',
|
||||
'position',
|
||||
'icon',
|
||||
'layoutMode',
|
||||
] as const satisfies MetadataEntityPropertyName<'pageLayoutTab'>[];
|
||||
|
||||
+2
-1
@@ -56,7 +56,8 @@ export const fromCreatePageLayoutTabInputToFlatPageLayoutTabToCreate = ({
|
||||
widgetIds: [],
|
||||
widgetUniversalIdentifiers: [],
|
||||
icon: null,
|
||||
layoutMode: PageLayoutTabLayoutMode.GRID,
|
||||
layoutMode:
|
||||
createPageLayoutTabInput.layoutMode ?? PageLayoutTabLayoutMode.GRID,
|
||||
overrides: null,
|
||||
};
|
||||
};
|
||||
|
||||
+10
@@ -1,12 +1,14 @@
|
||||
import { Field, Float, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@@ -26,4 +28,12 @@ export class CreatePageLayoutTabInput {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
pageLayoutId: string;
|
||||
|
||||
@Field(() => PageLayoutTabLayoutMode, {
|
||||
nullable: true,
|
||||
defaultValue: PageLayoutTabLayoutMode.GRID,
|
||||
})
|
||||
@IsEnum(PageLayoutTabLayoutMode)
|
||||
@IsOptional()
|
||||
layoutMode?: PageLayoutTabLayoutMode;
|
||||
}
|
||||
|
||||
+10
@@ -3,6 +3,7 @@ import { Field, Float, InputType } from '@nestjs/graphql';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
IsUUID,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { UpdatePageLayoutWidgetWithIdInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget-with-id.input';
|
||||
@@ -35,6 +37,14 @@ export class UpdatePageLayoutTabWithWidgetsInput {
|
||||
@IsOptional()
|
||||
icon?: string | null;
|
||||
|
||||
@Field(() => PageLayoutTabLayoutMode, {
|
||||
nullable: true,
|
||||
defaultValue: PageLayoutTabLayoutMode.GRID,
|
||||
})
|
||||
@IsEnum(PageLayoutTabLayoutMode)
|
||||
@IsOptional()
|
||||
layoutMode?: PageLayoutTabLayoutMode;
|
||||
|
||||
@Field(() => [UpdatePageLayoutWidgetWithIdInput])
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
|
||||
+7
-1
@@ -1,6 +1,7 @@
|
||||
import { Field, Float, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
|
||||
|
||||
@InputType()
|
||||
export class UpdatePageLayoutTabInput {
|
||||
@@ -18,4 +19,9 @@ export class UpdatePageLayoutTabInput {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
icon?: string | null;
|
||||
|
||||
@Field(() => PageLayoutTabLayoutMode, { nullable: true })
|
||||
@IsEnum(PageLayoutTabLayoutMode)
|
||||
@IsOptional()
|
||||
layoutMode?: PageLayoutTabLayoutMode;
|
||||
}
|
||||
|
||||
+3
-1
@@ -294,7 +294,7 @@ export class PageLayoutUpdateService {
|
||||
widgetIds: [],
|
||||
widgetUniversalIdentifiers: [],
|
||||
icon: null,
|
||||
layoutMode: PageLayoutTabLayoutMode.GRID,
|
||||
layoutMode: tabInput.layoutMode ?? PageLayoutTabLayoutMode.GRID,
|
||||
overrides: null,
|
||||
};
|
||||
},
|
||||
@@ -311,6 +311,7 @@ export class PageLayoutUpdateService {
|
||||
...existingTab,
|
||||
title: tabInput.title,
|
||||
position: tabInput.position,
|
||||
layoutMode: tabInput.layoutMode ?? existingTab.layoutMode,
|
||||
updatedAt: now.toISOString(),
|
||||
};
|
||||
},
|
||||
@@ -327,6 +328,7 @@ export class PageLayoutUpdateService {
|
||||
...existingTab,
|
||||
title: tabInput.title,
|
||||
position: tabInput.position,
|
||||
layoutMode: tabInput.layoutMode ?? existingTab.layoutMode,
|
||||
deletedAt: null,
|
||||
updatedAt: now.toISOString(),
|
||||
};
|
||||
|
||||
+1
@@ -243,6 +243,7 @@ describe('WorkspaceEntityManager', () => {
|
||||
IS_NAVIGATION_MENU_ITEM_ENABLED: false,
|
||||
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED: false,
|
||||
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED: false,
|
||||
IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED: false,
|
||||
IS_MARKETPLACE_ENABLED: false,
|
||||
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: false,
|
||||
IS_DRAFT_EMAIL_ENABLED: false,
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
import { FeatureFlagKey } from 'twenty-shared/types';
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
|
||||
const tableName = 'featureFlag';
|
||||
|
||||
@@ -105,6 +105,11 @@ export const seedFeatureFlags = async ({
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED,
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
};
|
||||
|
||||
@@ -19,4 +19,5 @@ export enum FeatureFlagKey {
|
||||
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED = 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED',
|
||||
IS_DRAFT_EMAIL_ENABLED = 'IS_DRAFT_EMAIL_ENABLED',
|
||||
IS_RICH_TEXT_V1_MIGRATED = 'IS_RICH_TEXT_V1_MIGRATED',
|
||||
IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED = 'IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user