Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { commandMenuItemsDraftState } from '@/command-menu-item/server-items/edit/states/commandMenuItemsDraftState';
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState';
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useStore } from 'jotai';
import { useCallback } from 'react';

export const useExitLayoutCustomizationMode = () => {
const store = useStore();

const { closeSidePanelMenu } = useSidePanelMenu();

const setNavigationMenuItemsDraft = useSetAtomState(
Expand All @@ -27,9 +26,6 @@ export const useExitLayoutCustomizationMode = () => {
setNavigationMenuItemsDraft(null);
setSelectedNavigationMenuItemIdInEditMode(null);
store.set(commandMenuItemsDraftState.atom, null);

store.set(currentPageLayoutIdState.atom, null);
store.set(activeCustomizationPageLayoutIdsState.atom, []);
setIsLayoutCustomizationModeEnabled(false);
closeSidePanelMenu();
}, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ export const computeDndReorderPosition = ({
const listWithoutDragged = sortedList.filter(
(item) => item.id !== draggableId,
);
const adjustedIndex =
sourceIndexInList < destinationIndex &&
destinationIndex <= listWithoutDragged.length
? destinationIndex - 1
: destinationIndex;
let adjustedIndex = destinationIndex;
if (sourceIndexInList < destinationIndex) {
adjustedIndex -= 1;
}
adjustedIndex = Math.min(adjustedIndex, listWithoutDragged.length);
const prevItem = listWithoutDragged[adjustedIndex - 1];
const nextItem = listWithoutDragged[adjustedIndex];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type NavigationSections } from '@/navigation-menu-item/common/constants/NavigationSections.constants';
import { NavigationDropTargetContext } from '@/navigation-menu-item/common/contexts/NavigationDropTargetContext';

const StyledDropTarget = styled.div<{ $compact?: boolean }>`
const StyledDropTarget = styled.div<{
$compact?: boolean;
$highlightPosition?: 'top' | 'bottom';
}>`
min-height: ${({ $compact }) =>
$compact ? 0 : themeCssVariables.spacing[2]};
position: relative;
Expand All @@ -17,13 +20,22 @@ const StyledDropTarget = styled.div<{ $compact?: boolean }>`
&::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: ${themeCssVariables.color.blue};
${({ $highlightPosition }) =>
$highlightPosition === 'top'
? `
top: 0;
border-radius: 0 0 ${themeCssVariables.border.radius.sm}
${themeCssVariables.border.radius.sm};
`
: `
bottom: 0;
border-radius: ${themeCssVariables.border.radius.sm}
${themeCssVariables.border.radius.sm} 0 0;
`}
}
}

Expand All @@ -39,6 +51,7 @@ type NavigationItemDropTargetProps = {
children?: ReactNode;
compact?: boolean;
dropTargetIdOverride?: string;
highlightPosition?: 'top' | 'bottom';
};

export const NavigationItemDropTarget = ({
Expand All @@ -48,6 +61,7 @@ export const NavigationItemDropTarget = ({
children,
compact = false,
dropTargetIdOverride,
highlightPosition = 'bottom',
}: NavigationItemDropTargetProps) => {
const { activeDropTargetId, forbiddenDropTargetId } = useContext(
NavigationDropTargetContext,
Expand All @@ -60,6 +74,7 @@ export const NavigationItemDropTarget = ({
return (
<StyledDropTarget
$compact={compact}
$highlightPosition={highlightPosition}
data-drag-over={isDragOver && !isDropForbidden ? 'true' : undefined}
data-drop-forbidden={isDropForbidden ? 'true' : undefined}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NavigationMenuItemType } from 'twenty-shared/types';
import { type NavigationMenuItem } from '~/generated-metadata/graphql';

import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { getWorkspaceSidebarOrphanItemsInDisplayOrder } from '@/navigation-menu-item/display/utils/getWorkspaceSidebarOrphanItemsInDisplayOrder';
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
Expand All @@ -22,6 +23,9 @@ export const useNavigationMenuItemSectionItems = (): NavigationMenuItem[] => {
const { workspaceNavigationMenuItemsSorted } = useSortedNavigationMenuItems();
const { workspaceNavigationMenuItemsByFolder } =
useNavigationMenuItemsByFolder();
const isLayoutCustomizationModeEnabled = useAtomStateValue(
isLayoutCustomizationModeEnabledState,
);
const views = useAtomStateValue(viewsSelector);
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
Expand All @@ -39,6 +43,7 @@ export const useNavigationMenuItemSectionItems = (): NavigationMenuItem[] => {
objectMetadataItems,
views,
objectPermissionsByObjectMetadataId,
includeInaccessibleObjectBackedItems: isLayoutCustomizationModeEnabled,
});

return flatItems.flatMap((item) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import type { ReactNode } from 'react';
import { t } from '@lingui/core/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { Fragment, type ReactNode, useContext } from 'react';

import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay';
import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback';
import { recordIdentifierToObjectRecordIdentifier } from '@/navigation-menu-item/common/utils/recordIdentifierToObjectRecordIdentifier';
import { getNavigationMenuItemComputedLink } from '@/navigation-menu-item/display/utils/getNavigationMenuItemComputedLink';
import { getNavigationMenuItemLabel } from '@/navigation-menu-item/display/utils/getNavigationMenuItemLabel';
import { recordIdentifierToObjectRecordIdentifier } from '@/navigation-menu-item/common/utils/recordIdentifierToObjectRecordIdentifier';
import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay';
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback';
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
Expand All @@ -19,7 +23,8 @@ import {
NavigationMenuItemType,
} from 'twenty-shared/types';
import { getAppPath, isDefined } from 'twenty-shared/utils';
import { Avatar, useIcons } from 'twenty-ui/display';
import { Avatar, IconLock, useIcons } from 'twenty-ui/display';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { type NavigationMenuItem } from '~/generated-metadata/graphql';

export type NavigationDrawerItemForObjectMetadataItemProps = {
Expand All @@ -44,12 +49,19 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
const isLayoutCustomizationModeEnabled = useAtomStateValue(
isLayoutCustomizationModeEnabledState,
);
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
const { theme } = useContext(ThemeContext);
const lastVisitedViewPerObjectMetadataItem = useAtomStateValue(
lastVisitedViewPerObjectMetadataItemState,
);
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
const views = useAtomStateValue(viewsSelector);

const canReadObjectRecords = getObjectPermissionsForObject(
objectPermissionsByObjectMetadataId,
objectMetadataItem.id,
).canReadObjectRecords;

const lastVisitedViewId =
lastVisitedViewPerObjectMetadataItem?.[objectMetadataItem.id];

Expand Down Expand Up @@ -107,12 +119,19 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
? getNavigationMenuItemLabel(navigationMenuItem, objectMetadataItems, views)
: objectMetadataItem.labelPlural;

const label = isRecord
? itemLabel
: isViewWithResolvedView
const primaryLabel =
isRecord || isViewWithResolvedView
? itemLabel
: objectMetadataItem.labelPlural;

const needsInaccessibleRecordPlaceholder =
isLayoutCustomizationModeEnabled &&
isRecord &&
!canReadObjectRecords &&
!isNonEmptyString(primaryLabel.trim());

const label = needsInaccessibleRecordPlaceholder ? t`Record` : primaryLabel;

const recordIdentifier =
isRecord && isDefined(navigationMenuItem?.targetRecordIdentifier)
? recordIdentifierToObjectRecordIdentifier({
Expand Down Expand Up @@ -151,6 +170,9 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
? objectMetadataItem.labelSingular
: undefined;

const showInaccessibleLock =
isLayoutCustomizationModeEnabled && !canReadObjectRecords;

return (
<NavigationDrawerItem
label={label}
Expand All @@ -169,7 +191,20 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
isSelectedInEditMode={isSelectedInEditMode}
isDragging={isDragging}
triggerEvent={isLayoutCustomizationModeEnabled ? 'CLICK' : undefined}
rightOptions={rightOptions}
alwaysShowRightOptions={showInaccessibleLock}
rightOptions={
showInaccessibleLock ? (
<Fragment>
<IconLock
size={theme.icon.size.sm}
stroke={theme.icon.stroke.sm}
color={themeCssVariables.font.color.tertiary}
/>
</Fragment>
) : (
rightOptions
)
}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type NavigationMenuItemOrphanDropTargetProps = {
children?: ReactNode;
sectionId?: NavigationSections;
droppableId?: string;
highlightPosition?: 'top' | 'bottom';
};

export const NavigationMenuItemOrphanDropTarget = ({
Expand All @@ -19,13 +20,15 @@ export const NavigationMenuItemOrphanDropTarget = ({
children,
sectionId = NavigationSections.WORKSPACE,
droppableId = NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS,
highlightPosition = 'bottom',
}: NavigationMenuItemOrphanDropTargetProps) => (
<NavigationItemDropTarget
folderId={null}
index={index}
sectionId={sectionId}
compact={compact}
dropTargetIdOverride={getDndKitDropTargetId(droppableId, index)}
highlightPosition={highlightPosition}
>
{children}
</NavigationItemDropTarget>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export const WorkspaceSectionContainer = ({
return false;
});

const workspaceOrphanItemsForSection = isLayoutCustomizationModeEnabled
? flatItems
: filteredItems;

const getEditModeProps = (item: NavigationMenuItem): EditModeProps => {
const itemId = item.id;
return {
Expand Down Expand Up @@ -159,14 +163,14 @@ export const WorkspaceSectionContainer = ({
<Suspense
fallback={
<WorkspaceSectionListEditModeFallback
filteredItems={filteredItems}
filteredItems={workspaceOrphanItemsForSection}
folderChildrenById={folderChildrenById}
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
/>
}
>
<LazyWorkspaceSectionListDndKit
filteredItems={filteredItems}
filteredItems={workspaceOrphanItemsForSection}
getEditModeProps={getEditModeProps}
folderChildrenById={folderChildrenById}
onNavigationMenuItemClick={onNavigationMenuItemClick}
Expand Down
Loading
Loading