refactor(twenty-front): reorganize NavigationMenuItem module into common/display/edit subfolders (#18691)
## Summary - Reorganizes the `navigation-menu-item` frontend module from a flat structure into `common/`, `display/`, and `edit/` subfolders with type-specific subdirectories (`link/`, `folder/`, `object/`, `view/`, `record/`) - Every file is now in a leaf folder describing its type: `components/`, `hooks/`, `utils/`, `types/`, or `constants/` - Moves 14 NavigationMenuItem-related components out of `object-metadata/` and `side-panel/pages/` into the `navigation-menu-item` module where they belong - Creates type-specific display utility functions (e.g., `getLinkNavigationMenuItemLabel`, `getObjectNavigationMenuItemComputedLink`) to replace generic switch-based functions - Unifies the Favorites section drag-and-drop from `@hello-pangea/dnd` to `@dnd-kit/react`, matching the Workspace section's DnD library - Renames Favorites section components from `CurrentWorkspaceMember*` to `Favorites*` for clarity - Deletes unused `FavoritesDragDropProviderContent` and `NavbarDragProvider` ## Test plan - [x] `npx nx typecheck twenty-front` passes - [x] `npx nx lint:diff-with-main twenty-front` passes (0 warnings, 0 errors) - [x] `npx nx test twenty-front` passes (763 suites, 4467 tests) - [ ] Verify favorites drag-and-drop still works in the UI (reorder items, move between folders) - [ ] Verify workspace edit mode drag-and-drop still works - [ ] Verify "add to navigation" drag from command menu/side panel still works
This commit is contained in:
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { lazy, Suspense, useContext, type ReactNode } from 'react';
|
||||
|
||||
import { ADD_TO_NAV_SOURCE_DROPPABLE_ID } from '@/navigation-menu-item/constants/AddToNavSourceDroppableId';
|
||||
import { NavigationDragSourceContext } from '@/navigation-menu-item/contexts/NavigationDragSourceContext';
|
||||
import { ADD_TO_NAV_SOURCE_DROPPABLE_ID } from '@/navigation-menu-item/common/constants/AddToNavSourceDroppableId';
|
||||
import { NavigationDragSourceContext } from '@/navigation-menu-item/common/contexts/NavigationDragSourceContext';
|
||||
|
||||
import type { AddToNavDroppableProvided } from '@/command-menu/components/CommandMenuAddToNavDroppableTypes';
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ import { useIcons } from 'twenty-ui/display';
|
||||
import { SidePanelPageInfoLayout } from '@/side-panel/components/SidePanelPageInfoLayout';
|
||||
import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState';
|
||||
import { sidePanelShouldFocusTitleInputComponentState } from '@/side-panel/states/sidePanelShouldFocusTitleInputComponentState';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon';
|
||||
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/common/constants/FolderIconDefault';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useUpdateFolderInDraft } from '@/navigation-menu-item/hooks/useUpdateFolderInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useUpdateFolderInDraft } from '@/navigation-menu-item/edit/folder/hooks/useUpdateFolderInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/display/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||
import { TitleInput } from '@/ui/input/components/TitleInput';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { AddToNavigationDragHandle } from '@/navigation-menu-item/components/AddToNavigationDragHandle';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/states/addToNavPayloadRegistryState';
|
||||
import type { AddToNavigationDragPayload } from '@/navigation-menu-item/types/add-to-navigation-drag-payload';
|
||||
import { AddToNavigationDragHandle } from '@/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/states/addToNavPayloadRegistryState';
|
||||
import type { AddToNavigationDragPayload } from '@/navigation-menu-item/common/types/add-to-navigation-drag-payload';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
const CommandMenuItemWithAddToNavigationDragDndKit = lazy(() =>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconLink, IconWorld } from 'twenty-ui/display';
|
||||
|
||||
import { LinkIconWithLinkOverlay } from '@/navigation-menu-item/components/LinkIconWithLinkOverlay';
|
||||
import { LinkIconWithLinkOverlay } from '@/navigation-menu-item/display/link/components/LinkIconWithLinkOverlay';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useUpdateLinkInDraft } from '@/navigation-menu-item/edit/link/hooks/useUpdateLinkInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/display/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { SidePanelPageInfoLayout } from '@/side-panel/components/SidePanelPageInfoLayout';
|
||||
import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState';
|
||||
import { sidePanelShouldFocusTitleInputComponentState } from '@/side-panel/states/sidePanelShouldFocusTitleInputComponentState';
|
||||
|
||||
+4
-4
@@ -2,11 +2,11 @@ import { useLingui } from '@lingui/react/macro';
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
|
||||
import { SidePanelPageInfoLayout } from '@/side-panel/components/SidePanelPageInfoLayout';
|
||||
import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon';
|
||||
import { NavigationMenuItemIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemIcon';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
|
||||
import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel';
|
||||
import { useSelectedNavigationMenuItemEditItemObjectMetadata } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemObjectMetadata';
|
||||
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/edit/hooks/useSelectedNavigationMenuItemEditItem';
|
||||
import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/edit/hooks/useSelectedNavigationMenuItemEditItemLabel';
|
||||
import { useSelectedNavigationMenuItemEditItemObjectMetadata } from '@/navigation-menu-item/edit/hooks/useSelectedNavigationMenuItemEditItemObjectMetadata';
|
||||
|
||||
export const SidePanelObjectViewRecordInfo = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
import { NavigationMenuItemType, SidePanelPages } from 'twenty-shared/types';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/display/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { SidePanelAskAIInfo } from '@/side-panel/components/SidePanelAskAIInfo';
|
||||
import { SidePanelFolderInfo } from '@/side-panel/components/SidePanelFolderInfo';
|
||||
import { SidePanelLinkInfo } from '@/side-panel/components/SidePanelLinkInfo';
|
||||
|
||||
@@ -3,8 +3,8 @@ import { SidePanelAskAIPage } from '@/side-panel/pages/ask-ai/components/SidePan
|
||||
import { SidePanelCalendarEventPage } from '@/side-panel/pages/calendar-event/components/SidePanelCalendarEventPage';
|
||||
import { SidePanelFrontComponentPage } from '@/side-panel/pages/front-component/components/SidePanelFrontComponentPage';
|
||||
import { SidePanelMessageThreadPage } from '@/side-panel/pages/message-thread/components/SidePanelMessageThreadPage';
|
||||
import { SidePanelNavigationMenuItemEditPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNavigationMenuItemEditPage';
|
||||
import { SidePanelNewSidebarItemPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemPage';
|
||||
import { SidePanelNavigationMenuItemEditPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNavigationMenuItemEditPage';
|
||||
import { SidePanelNewSidebarItemPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemPage';
|
||||
import { SidePanelPageLayoutChartSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutChartSettings';
|
||||
import { SidePanelPageLayoutFieldsSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutFieldsSettings';
|
||||
import { SidePanelPageLayoutIframeSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutIframeSettings';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { SidePanelEditFolderPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditFolderPickerSubPage';
|
||||
import { SidePanelNewSidebarItemObjectSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemObjectSubPage';
|
||||
import { SidePanelNewSidebarItemObjectSystemPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemObjectSystemPickerSubPage';
|
||||
import { SidePanelNewSidebarItemRecordSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemRecordSubPage';
|
||||
import { SidePanelNewSidebarItemViewObjectPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewObjectPickerSubPage';
|
||||
import { SidePanelNewSidebarItemViewPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewPickerSubPage';
|
||||
import { SidePanelNewSidebarItemViewSystemPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewSystemPickerSubPage';
|
||||
import { SidePanelEditFolderPickerSubPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelEditFolderPickerSubPage';
|
||||
import { SidePanelNewSidebarItemObjectSubPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemObjectSubPage';
|
||||
import { SidePanelNewSidebarItemObjectSystemPickerSubPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemObjectSystemPickerSubPage';
|
||||
import { SidePanelNewSidebarItemRecordSubPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemRecordSubPage';
|
||||
import { SidePanelNewSidebarItemViewObjectPickerSubPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubPage';
|
||||
import { SidePanelNewSidebarItemViewPickerSubPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewPickerSubPage';
|
||||
import { SidePanelNewSidebarItemViewSystemPickerSubPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemPickerSubPage';
|
||||
import { SidePanelChartFilterSubPage } from '@/side-panel/pages/page-layout/components/SidePanelChartFilterSubPage';
|
||||
import { SidePanelFieldsLayoutSubPage } from '@/side-panel/pages/page-layout/components/SidePanelFieldsLayoutSubPage';
|
||||
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
|
||||
|
||||
@@ -3,9 +3,9 @@ import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
|
||||
import { isSidePanelClosingState } from '@/side-panel/states/isSidePanelClosingState';
|
||||
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/states/addToNavPayloadRegistryState';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/states/addToNavPayloadRegistryState';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/common/states/isNavigationMenuInEditModeState';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
|
||||
import { emitSidePanelOpenEvent } from '@/ui/layout/side-panel/utils/emitSidePanelOpenEvent';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown';
|
||||
import { useUpdateNavigationMenuItemInDraft } from '@/navigation-menu-item/hooks/useUpdateNavigationMenuItemInDraft';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useState } from 'react';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { IconColorSwatch } from 'twenty-ui/display';
|
||||
import {
|
||||
DEFAULT_COLOR_LABELS,
|
||||
MenuItemSelectColor,
|
||||
} from 'twenty-ui/navigation';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { type ThemeColor, MAIN_COLOR_NAMES } from 'twenty-ui/theme';
|
||||
const NAVIGATION_MENU_ITEM_COLOR_DROPDOWN_ID = 'navigation-menu-item-color';
|
||||
|
||||
const StyledMenuStyleText = styled.span`
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-size: 13px;
|
||||
`;
|
||||
|
||||
type SidePanelEditColorOptionProps = {
|
||||
navigationMenuItemId: string;
|
||||
color: ThemeColor;
|
||||
};
|
||||
|
||||
export const SidePanelEditColorOption = ({
|
||||
navigationMenuItemId,
|
||||
color,
|
||||
}: SidePanelEditColorOptionProps) => {
|
||||
const { t } = useLingui();
|
||||
const { updateNavigationMenuItemInDraft } =
|
||||
useUpdateNavigationMenuItemInDraft();
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const themeColor = color ?? 'gray';
|
||||
const colorLabel = DEFAULT_COLOR_LABELS[themeColor] ?? capitalize(themeColor);
|
||||
|
||||
const query = searchValue.trim().toLowerCase();
|
||||
|
||||
const filteredColorNames = isNonEmptyString(query)
|
||||
? MAIN_COLOR_NAMES.filter(
|
||||
(colorName) =>
|
||||
colorName.toLowerCase().includes(query) ||
|
||||
(DEFAULT_COLOR_LABELS[colorName] ?? '').toLowerCase().includes(query),
|
||||
)
|
||||
: MAIN_COLOR_NAMES;
|
||||
|
||||
const handleSelectColor = (selectedColor: ThemeColor) => {
|
||||
updateNavigationMenuItemInDraft(navigationMenuItemId, {
|
||||
color: selectedColor,
|
||||
});
|
||||
closeDropdown(NAVIGATION_MENU_ITEM_COLOR_DROPDOWN_ID);
|
||||
};
|
||||
|
||||
return (
|
||||
<CommandMenuItemDropdown
|
||||
id="edit-navigation-menu-item-color"
|
||||
label={t`Color`}
|
||||
Icon={IconColorSwatch}
|
||||
dropdownId={NAVIGATION_MENU_ITEM_COLOR_DROPDOWN_ID}
|
||||
dropdownPlacement="bottom-start"
|
||||
RightComponent={<StyledMenuStyleText>{colorLabel}</StyledMenuStyleText>}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuSearchInput
|
||||
placeholder={t`Search colors...`}
|
||||
value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{filteredColorNames.map((colorName) => (
|
||||
<MenuItemSelectColor
|
||||
key={colorName}
|
||||
onClick={() => handleSelectColor(colorName)}
|
||||
color={colorName}
|
||||
selected={colorName === themeColor}
|
||||
colorLabels={DEFAULT_COLOR_LABELS}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
|
||||
import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
|
||||
import { useFolderPickerSelectionData } from '@/side-panel/pages/navigation-menu-item/hooks/useFolderPickerSelectionData';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { filterBySearchQuery } from '~/utils/filterBySearchQuery';
|
||||
|
||||
export const SidePanelEditFolderPickerSubPage = () => {
|
||||
const { t } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const { foldersToShow, includeNoFolderOption, handleSelectFolder } =
|
||||
useFolderPickerSelectionData();
|
||||
|
||||
const filteredFolders = filterBySearchQuery({
|
||||
items: foldersToShow,
|
||||
searchQuery: searchValue,
|
||||
getSearchableValues: (folder) => [folder.name],
|
||||
});
|
||||
const isEmpty = filteredFolders.length === 0 && !includeNoFolderOption;
|
||||
const selectableItemIds = [
|
||||
...(includeNoFolderOption ? ['no-folder'] : []),
|
||||
...(filteredFolders.length > 0 ? filteredFolders.map((f) => f.id) : []),
|
||||
];
|
||||
const noResultsText =
|
||||
searchValue.trim().length > 0
|
||||
? t`No results found`
|
||||
: t`No folders available`;
|
||||
|
||||
return (
|
||||
<SidePanelSubViewWithSearch
|
||||
searchPlaceholder={t`Search a folder...`}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
>
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
<SidePanelGroup heading={t`Folders`}>
|
||||
{includeNoFolderOption && (
|
||||
<SelectableListItem
|
||||
itemId="no-folder"
|
||||
onEnter={() => handleSelectFolder(null)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
label={t`No folder`}
|
||||
id="no-folder"
|
||||
onClick={() => handleSelectFolder(null)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{filteredFolders.map((folder) => {
|
||||
const FolderIcon = getIcon(folder.icon ?? FOLDER_ICON_DEFAULT);
|
||||
const folderColor =
|
||||
folder.color ?? DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER;
|
||||
return (
|
||||
<SelectableListItem
|
||||
key={folder.id}
|
||||
itemId={folder.id}
|
||||
onEnter={() => handleSelectFolder(folder.id)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
LeftComponent={
|
||||
<NavigationMenuItemStyleIcon
|
||||
Icon={FolderIcon}
|
||||
color={folderColor}
|
||||
/>
|
||||
}
|
||||
label={folder.name}
|
||||
id={folder.id}
|
||||
onClick={() => handleSelectFolder(folder.id)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
})}
|
||||
</SidePanelGroup>
|
||||
</SidePanelList>
|
||||
</SidePanelSubViewWithSearch>
|
||||
);
|
||||
};
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useState } from 'react';
|
||||
import { getAbsoluteUrl } from 'twenty-shared/utils';
|
||||
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
import { extractDomainFromUrl } from '@/navigation-menu-item/utils/extractDomainFromUrl';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import {
|
||||
type OrganizeActionsProps,
|
||||
SidePanelEditOrganizeActions,
|
||||
} from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOrganizeActions';
|
||||
import { SidePanelEditOwnerSection } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOwnerSection';
|
||||
import { getOrganizeActionsSelectableItemIds } from '@/side-panel/pages/navigation-menu-item/utils/getOrganizeActionsSelectableItemIds';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
|
||||
type SidePanelEditLinkItemViewProps = OrganizeActionsProps & {
|
||||
selectedItem: NavigationMenuItem;
|
||||
onUpdateLink: (
|
||||
linkId: string,
|
||||
updates: { link?: string; name?: string },
|
||||
) => void;
|
||||
onOpenFolderPicker: () => void;
|
||||
};
|
||||
|
||||
export const SidePanelEditLinkItemView = ({
|
||||
selectedItem,
|
||||
onUpdateLink,
|
||||
canMoveUp,
|
||||
canMoveDown,
|
||||
onOpenFolderPicker,
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onRemove,
|
||||
onAddBefore,
|
||||
onAddAfter,
|
||||
}: SidePanelEditLinkItemViewProps) => {
|
||||
const { t } = useLingui();
|
||||
const [urlEditInput, setUrlEditInput] = useState('');
|
||||
const [lastAutoSetName, setLastAutoSetName] = useState<string | null>(null);
|
||||
|
||||
const defaultLabel = t`Link label`;
|
||||
const selectableItemIds = getOrganizeActionsSelectableItemIds(true);
|
||||
|
||||
const currentName = selectedItem.name ?? defaultLabel;
|
||||
const currentDomain = selectedItem.link
|
||||
? extractDomainFromUrl(getAbsoluteUrl(selectedItem.link))
|
||||
: undefined;
|
||||
const canAutoUpdateName =
|
||||
currentName === defaultLabel ||
|
||||
currentName === currentDomain ||
|
||||
currentName === lastAutoSetName;
|
||||
|
||||
const handleUrlChange = (value: string) => {
|
||||
setUrlEditInput(value);
|
||||
if (!canAutoUpdateName) return;
|
||||
const trimmed = value.trim();
|
||||
if (!isNonEmptyString(trimmed)) return;
|
||||
const domain = extractDomainFromUrl(getAbsoluteUrl(trimmed));
|
||||
if (domain !== undefined) {
|
||||
setLastAutoSetName(domain);
|
||||
onUpdateLink(selectedItem.id, { name: domain });
|
||||
}
|
||||
};
|
||||
|
||||
const handleUrlBlur = (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value.trim();
|
||||
if (isNonEmptyString(value)) {
|
||||
onUpdateLink(selectedItem.id, { link: getAbsoluteUrl(value) });
|
||||
setUrlEditInput('');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SidePanelList commandGroups={[]} selectableItemIds={selectableItemIds}>
|
||||
<SidePanelGroup heading={t`Customize`}>
|
||||
<TextInput
|
||||
fullWidth
|
||||
placeholder="www.google.com"
|
||||
value={urlEditInput || selectedItem.link || ''}
|
||||
onChange={handleUrlChange}
|
||||
onBlur={handleUrlBlur}
|
||||
/>
|
||||
</SidePanelGroup>
|
||||
<SidePanelEditOrganizeActions
|
||||
canMoveUp={canMoveUp}
|
||||
canMoveDown={canMoveDown}
|
||||
onMoveUp={onMoveUp}
|
||||
onMoveDown={onMoveDown}
|
||||
onRemove={onRemove}
|
||||
onAddBefore={onAddBefore}
|
||||
onAddAfter={onAddAfter}
|
||||
showMoveToFolder
|
||||
onMoveToFolder={onOpenFolderPicker}
|
||||
moveToFolderHasSubMenu
|
||||
/>
|
||||
<SidePanelEditOwnerSection applicationId={selectedItem.applicationId} />
|
||||
</SidePanelList>
|
||||
);
|
||||
};
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
import { useObjectNavItemColor } from '@/navigation-menu-item/hooks/useObjectNavItemColor';
|
||||
import { navigationMenuItemsSelector } from '@/navigation-menu-item/states/navigationMenuItemsSelector';
|
||||
import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/utils/getEffectiveNavigationMenuItemColor';
|
||||
import { getNavigationMenuItemObjectNameSingular } from '@/navigation-menu-item/utils/getNavigationMenuItemObjectNameSingular';
|
||||
import { parseThemeColor } from '@/navigation-menu-item/utils/parseThemeColor';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelEditColorOption } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditColorOption';
|
||||
import {
|
||||
type OrganizeActionsProps,
|
||||
SidePanelEditOrganizeActions,
|
||||
} from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOrganizeActions';
|
||||
import { getOrganizeActionsSelectableItemIds } from '@/side-panel/pages/navigation-menu-item/utils/getOrganizeActionsSelectableItemIds';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
type SidePanelEditObjectViewBaseProps = OrganizeActionsProps & {
|
||||
onOpenFolderPicker: () => void;
|
||||
showColorOption?: boolean;
|
||||
selectedItem?: NavigationMenuItem | null;
|
||||
};
|
||||
|
||||
export const SidePanelEditObjectViewBase = ({
|
||||
onOpenFolderPicker,
|
||||
canMoveUp,
|
||||
canMoveDown,
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onRemove,
|
||||
onAddBefore,
|
||||
onAddAfter,
|
||||
showColorOption = false,
|
||||
selectedItem,
|
||||
}: SidePanelEditObjectViewBaseProps) => {
|
||||
const { t } = useLingui();
|
||||
const selectableItemIds = getOrganizeActionsSelectableItemIds(true);
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
const views = useAtomStateValue(viewsSelector);
|
||||
|
||||
const objectNameSingular = isDefined(selectedItem)
|
||||
? (getNavigationMenuItemObjectNameSingular(
|
||||
selectedItem,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
) ?? '')
|
||||
: '';
|
||||
|
||||
const objectColor = useObjectNavItemColor(objectNameSingular);
|
||||
|
||||
const navigationMenuItems = useAtomStateValue(navigationMenuItemsSelector);
|
||||
const persistedNavItem = navigationMenuItems.find(
|
||||
(item) => item.id === selectedItem?.id,
|
||||
);
|
||||
const hasUserChangedColor =
|
||||
isNonEmptyString(selectedItem?.color) &&
|
||||
selectedItem.color !== (persistedNavItem?.color ?? undefined);
|
||||
|
||||
const effectiveColor = isDefined(selectedItem)
|
||||
? getEffectiveNavigationMenuItemColor(selectedItem, objectColor)
|
||||
: undefined;
|
||||
const displayColor = hasUserChangedColor
|
||||
? selectedItem.color
|
||||
: effectiveColor;
|
||||
|
||||
return (
|
||||
<SidePanelList commandGroups={[]} selectableItemIds={selectableItemIds}>
|
||||
{showColorOption && isDefined(selectedItem) && (
|
||||
<SidePanelGroup heading={t`Customize`}>
|
||||
<SidePanelEditColorOption
|
||||
navigationMenuItemId={selectedItem.id}
|
||||
color={parseThemeColor(displayColor)}
|
||||
/>
|
||||
</SidePanelGroup>
|
||||
)}
|
||||
<SidePanelEditOrganizeActions
|
||||
canMoveUp={canMoveUp}
|
||||
canMoveDown={canMoveDown}
|
||||
onMoveUp={onMoveUp}
|
||||
onMoveDown={onMoveDown}
|
||||
onRemove={onRemove}
|
||||
onAddBefore={onAddBefore}
|
||||
onAddAfter={onAddAfter}
|
||||
showMoveToFolder
|
||||
onMoveToFolder={onOpenFolderPicker}
|
||||
/>
|
||||
</SidePanelList>
|
||||
);
|
||||
};
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconChevronUp,
|
||||
IconFolderSymlink,
|
||||
IconRowInsertBottom,
|
||||
IconRowInsertTop,
|
||||
IconTrash,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { SidePanelNavigationItemActions } from '@/side-panel/pages/navigation-menu-item/side-panel-navigation-item-actions';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
|
||||
export type OrganizeActionsProps = {
|
||||
canMoveUp: boolean;
|
||||
canMoveDown: boolean;
|
||||
onMoveUp: () => void;
|
||||
onMoveDown: () => void;
|
||||
onRemove: () => void;
|
||||
onAddBefore?: () => void;
|
||||
onAddAfter?: () => void;
|
||||
};
|
||||
|
||||
type SidePanelEditOrganizeActionsProps = OrganizeActionsProps & {
|
||||
showMoveToFolder?: boolean;
|
||||
onMoveToFolder?: () => void;
|
||||
moveToFolderHasSubMenu?: boolean;
|
||||
};
|
||||
|
||||
export const SidePanelEditOrganizeActions = ({
|
||||
canMoveUp,
|
||||
canMoveDown,
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onRemove,
|
||||
onAddBefore,
|
||||
onAddAfter,
|
||||
showMoveToFolder = false,
|
||||
onMoveToFolder,
|
||||
moveToFolderHasSubMenu = false,
|
||||
}: SidePanelEditOrganizeActionsProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
return (
|
||||
<SidePanelGroup heading={t`Organize`}>
|
||||
<SelectableListItem
|
||||
itemId={SidePanelNavigationItemActions.MOVE_UP}
|
||||
onEnter={canMoveUp ? onMoveUp : undefined}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={IconChevronUp}
|
||||
label={t`Move up`}
|
||||
id={SidePanelNavigationItemActions.MOVE_UP}
|
||||
onClick={onMoveUp}
|
||||
disabled={!canMoveUp}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SelectableListItem
|
||||
itemId={SidePanelNavigationItemActions.MOVE_DOWN}
|
||||
onEnter={canMoveDown ? onMoveDown : undefined}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={IconChevronDown}
|
||||
label={t`Move down`}
|
||||
id={SidePanelNavigationItemActions.MOVE_DOWN}
|
||||
onClick={onMoveDown}
|
||||
disabled={!canMoveDown}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
{showMoveToFolder && onMoveToFolder && (
|
||||
<SelectableListItem
|
||||
itemId={SidePanelNavigationItemActions.MOVE_TO_FOLDER}
|
||||
onEnter={onMoveToFolder}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={IconFolderSymlink}
|
||||
label={t`Move to folder`}
|
||||
id={SidePanelNavigationItemActions.MOVE_TO_FOLDER}
|
||||
hasSubMenu={moveToFolderHasSubMenu}
|
||||
onClick={onMoveToFolder}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{onAddBefore && (
|
||||
<SelectableListItem
|
||||
itemId={SidePanelNavigationItemActions.ADD_BEFORE}
|
||||
onEnter={onAddBefore}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={IconRowInsertTop}
|
||||
label={t`Add menu item before`}
|
||||
id={SidePanelNavigationItemActions.ADD_BEFORE}
|
||||
onClick={onAddBefore}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
{onAddAfter && (
|
||||
<SelectableListItem
|
||||
itemId={SidePanelNavigationItemActions.ADD_AFTER}
|
||||
onEnter={onAddAfter}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={IconRowInsertBottom}
|
||||
label={t`Add menu item after`}
|
||||
id={SidePanelNavigationItemActions.ADD_AFTER}
|
||||
onClick={onAddAfter}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
<SelectableListItem
|
||||
itemId={SidePanelNavigationItemActions.REMOVE}
|
||||
onEnter={onRemove}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={IconTrash}
|
||||
label={t`Remove from sidebar`}
|
||||
id={SidePanelNavigationItemActions.REMOVE}
|
||||
onClick={onRemove}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SidePanelGroup>
|
||||
);
|
||||
};
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconApps } from 'twenty-ui/display';
|
||||
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useQuery } from '@apollo/client/react';
|
||||
import { FindOneApplicationDocument } from '~/generated-metadata/graphql';
|
||||
|
||||
type SidePanelEditOwnerSectionProps = {
|
||||
applicationId?: string | null;
|
||||
};
|
||||
|
||||
export const SidePanelEditOwnerSection = ({
|
||||
applicationId: applicationIdProp,
|
||||
}: SidePanelEditOwnerSectionProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { selectedItem } = useSelectedNavigationMenuItemEditItem();
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
|
||||
const applicationIdFromDraft =
|
||||
isDefined(selectedItem) && isDefined(currentDraft)
|
||||
? currentDraft.find((item) => item.id === selectedItem.id)?.applicationId
|
||||
: undefined;
|
||||
|
||||
const applicationId = applicationIdProp ?? applicationIdFromDraft;
|
||||
|
||||
const { data } = useQuery(FindOneApplicationDocument, {
|
||||
variables: { id: applicationId ?? '' },
|
||||
skip: !isDefined(applicationId),
|
||||
});
|
||||
|
||||
const applicationName = data?.findOneApplication?.name;
|
||||
|
||||
if (!isDefined(applicationName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SidePanelGroup heading={t`Owner`}>
|
||||
<SelectableListItem itemId="owner-app" onEnter={() => {}}>
|
||||
<CommandMenuItem
|
||||
Icon={IconApps}
|
||||
label={applicationName}
|
||||
id="owner-app"
|
||||
disabled
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SidePanelGroup>
|
||||
);
|
||||
};
|
||||
-206
@@ -1,206 +0,0 @@
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/hooks/useNavigationMenuItemsDraftState';
|
||||
import { useOpenAddItemToFolderPage } from '@/navigation-menu-item/hooks/useOpenAddItemToFolderPage';
|
||||
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
|
||||
import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel';
|
||||
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { parseThemeColor } from '@/navigation-menu-item/utils/parseThemeColor';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
|
||||
import { SidePanelEditColorOption } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditColorOption';
|
||||
import { SidePanelEditLinkItemView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditLinkItemView';
|
||||
import { SidePanelEditObjectViewBase } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditObjectViewBase';
|
||||
import { SidePanelEditOrganizeActions } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOrganizeActions';
|
||||
import { SidePanelEditOwnerSection } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOwnerSection';
|
||||
import { useNavigationMenuItemEditOrganizeActions } from '@/side-panel/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions';
|
||||
import { getOrganizeActionsSelectableItemIds } from '@/side-panel/pages/navigation-menu-item/utils/getOrganizeActionsSelectableItemIds';
|
||||
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconPlus } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const ADD_ITEM_TO_FOLDER_ACTION_ID = 'add-item-to-folder';
|
||||
|
||||
const StyledSidePanelPlaceholder = styled.p`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
`;
|
||||
|
||||
const StyledSidePanelPageContainer = styled.div`
|
||||
padding: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
export const SidePanelNavigationMenuItemEditPage = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const { selectedItemLabel } = useSelectedNavigationMenuItemEditItemLabel();
|
||||
const { selectedItem } = useSelectedNavigationMenuItemEditItem();
|
||||
const selectedItemType = selectedItem?.type ?? null;
|
||||
|
||||
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
|
||||
const openFolderPicker = () =>
|
||||
navigateToSidePanelSubPage(SidePanelSubPages.EditFolderPicker);
|
||||
|
||||
const {
|
||||
canMoveUp,
|
||||
canMoveDown,
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onRemove,
|
||||
onAddBefore,
|
||||
onAddAfter,
|
||||
} = useNavigationMenuItemEditOrganizeActions();
|
||||
|
||||
const { updateLinkInDraft } = useUpdateLinkInDraft();
|
||||
const { openAddItemToFolderPage } = useOpenAddItemToFolderPage();
|
||||
const { workspaceNavigationMenuItems } = useNavigationMenuItemsDraftState();
|
||||
|
||||
const handleAddItemToFolder = () => {
|
||||
if (!selectedItem || selectedItem.type !== NavigationMenuItemType.FOLDER) {
|
||||
return;
|
||||
}
|
||||
const folderItemCount = workspaceNavigationMenuItems.filter(
|
||||
(item) => (item.folderId ?? null) === selectedItem.id,
|
||||
).length;
|
||||
openAddItemToFolderPage({
|
||||
targetFolderId: selectedItem.id,
|
||||
targetIndex: folderItemCount,
|
||||
resetNavigationStack: false,
|
||||
});
|
||||
};
|
||||
|
||||
if (!selectedNavigationMenuItemInEditMode || !selectedItemLabel) {
|
||||
return (
|
||||
<StyledSidePanelPageContainer>
|
||||
<StyledSidePanelPlaceholder>
|
||||
{t`Select a navigation item to edit`}
|
||||
</StyledSidePanelPlaceholder>
|
||||
</StyledSidePanelPageContainer>
|
||||
);
|
||||
}
|
||||
|
||||
switch (selectedItemType) {
|
||||
case NavigationMenuItemType.OBJECT:
|
||||
return (
|
||||
<SidePanelEditObjectViewBase
|
||||
onOpenFolderPicker={openFolderPicker}
|
||||
canMoveUp={canMoveUp}
|
||||
canMoveDown={canMoveDown}
|
||||
onMoveUp={onMoveUp}
|
||||
onMoveDown={onMoveDown}
|
||||
onRemove={onRemove}
|
||||
onAddBefore={onAddBefore}
|
||||
onAddAfter={onAddAfter}
|
||||
showColorOption={isDefined(selectedItem)}
|
||||
selectedItem={selectedItem}
|
||||
/>
|
||||
);
|
||||
case NavigationMenuItemType.VIEW:
|
||||
return (
|
||||
<SidePanelEditObjectViewBase
|
||||
onOpenFolderPicker={openFolderPicker}
|
||||
canMoveUp={canMoveUp}
|
||||
canMoveDown={canMoveDown}
|
||||
onMoveUp={onMoveUp}
|
||||
onMoveDown={onMoveDown}
|
||||
onRemove={onRemove}
|
||||
onAddBefore={onAddBefore}
|
||||
onAddAfter={onAddAfter}
|
||||
/>
|
||||
);
|
||||
case NavigationMenuItemType.LINK:
|
||||
if (
|
||||
isDefined(selectedItem) &&
|
||||
selectedItem.type === NavigationMenuItemType.LINK
|
||||
) {
|
||||
return (
|
||||
<SidePanelEditLinkItemView
|
||||
key={selectedItem.id}
|
||||
selectedItem={selectedItem}
|
||||
onUpdateLink={(linkId, updates) =>
|
||||
updateLinkInDraft(linkId, updates)
|
||||
}
|
||||
onOpenFolderPicker={openFolderPicker}
|
||||
canMoveUp={canMoveUp}
|
||||
canMoveDown={canMoveDown}
|
||||
onMoveUp={onMoveUp}
|
||||
onMoveDown={onMoveDown}
|
||||
onRemove={onRemove}
|
||||
onAddBefore={onAddBefore}
|
||||
onAddAfter={onAddAfter}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
case NavigationMenuItemType.FOLDER:
|
||||
return (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={[
|
||||
ADD_ITEM_TO_FOLDER_ACTION_ID,
|
||||
...getOrganizeActionsSelectableItemIds(false),
|
||||
]}
|
||||
>
|
||||
{selectedItem && (
|
||||
<SidePanelGroup heading={t`Customize`}>
|
||||
<SelectableListItem
|
||||
itemId={ADD_ITEM_TO_FOLDER_ACTION_ID}
|
||||
onEnter={handleAddItemToFolder}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={IconPlus}
|
||||
label={t`Add item to folder`}
|
||||
id={ADD_ITEM_TO_FOLDER_ACTION_ID}
|
||||
onClick={handleAddItemToFolder}
|
||||
hasSubMenu
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SidePanelEditColorOption
|
||||
navigationMenuItemId={selectedItem.id}
|
||||
color={parseThemeColor(selectedItem.color)}
|
||||
/>
|
||||
</SidePanelGroup>
|
||||
)}
|
||||
<SidePanelEditOrganizeActions
|
||||
canMoveUp={canMoveUp}
|
||||
canMoveDown={canMoveDown}
|
||||
onMoveUp={onMoveUp}
|
||||
onMoveDown={onMoveDown}
|
||||
onRemove={onRemove}
|
||||
onAddBefore={onAddBefore}
|
||||
onAddAfter={onAddAfter}
|
||||
/>
|
||||
<SidePanelEditOwnerSection />
|
||||
</SidePanelList>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={getOrganizeActionsSelectableItemIds(true)}
|
||||
>
|
||||
<SidePanelEditOrganizeActions
|
||||
canMoveUp={canMoveUp}
|
||||
canMoveDown={canMoveDown}
|
||||
onMoveUp={onMoveUp}
|
||||
onMoveDown={onMoveDown}
|
||||
onRemove={onRemove}
|
||||
onAddBefore={onAddBefore}
|
||||
onAddAfter={onAddAfter}
|
||||
showMoveToFolder
|
||||
onMoveToFolder={openFolderPicker}
|
||||
/>
|
||||
</SidePanelList>
|
||||
);
|
||||
}
|
||||
};
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
Avatar,
|
||||
IconBuildingSkyscraper,
|
||||
IconFolder,
|
||||
IconLink,
|
||||
IconTable,
|
||||
} from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelItemWithAddToNavigationDrag } from '@/side-panel/components/SidePanelItemWithAddToNavigationDrag';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { useAddFolderToNavigationMenu } from '@/side-panel/pages/navigation-menu-item/hooks/useAddFolderToNavigationMenu';
|
||||
import { useAddLinkToNavigationMenu } from '@/side-panel/pages/navigation-menu-item/hooks/useAddLinkToNavigationMenu';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
type SidePanelNewSidebarItemMainMenuProps = {
|
||||
onSelectObject: () => void;
|
||||
onSelectView: () => void;
|
||||
onSelectRecord: () => void;
|
||||
};
|
||||
|
||||
const MAIN_MENU_ITEM_TYPES = [
|
||||
NavigationMenuItemType.OBJECT,
|
||||
NavigationMenuItemType.VIEW,
|
||||
NavigationMenuItemType.RECORD,
|
||||
NavigationMenuItemType.FOLDER,
|
||||
NavigationMenuItemType.LINK,
|
||||
] as const;
|
||||
|
||||
export const SidePanelNewSidebarItemMainMenu = ({
|
||||
onSelectObject,
|
||||
onSelectView,
|
||||
onSelectRecord,
|
||||
}: SidePanelNewSidebarItemMainMenuProps) => {
|
||||
const { t } = useLingui();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const { handleAddFolder } = useAddFolderToNavigationMenu();
|
||||
const { handleAddLink } = useAddLinkToNavigationMenu();
|
||||
|
||||
const isAddingToFolder = isDefined(
|
||||
addMenuItemInsertionContext?.targetFolderId,
|
||||
);
|
||||
const isDragDisabled = addMenuItemInsertionContext?.disableDrag === true;
|
||||
const selectableItemIds = isAddingToFolder
|
||||
? MAIN_MENU_ITEM_TYPES.filter(
|
||||
(type) => type !== NavigationMenuItemType.FOLDER,
|
||||
)
|
||||
: [...MAIN_MENU_ITEM_TYPES];
|
||||
|
||||
return (
|
||||
<SidePanelAddToNavigationDroppable>
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<SidePanelList commandGroups={[]} selectableItemIds={selectableItemIds}>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<div ref={innerRef} {...droppableProps}>
|
||||
<SidePanelGroup heading={t`Data`}>
|
||||
<SelectableListItem
|
||||
itemId={NavigationMenuItemType.OBJECT}
|
||||
onEnter={onSelectObject}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={() => (
|
||||
<NavigationMenuItemStyleIcon
|
||||
Icon={IconBuildingSkyscraper}
|
||||
color="blue"
|
||||
/>
|
||||
)}
|
||||
label={t`Object`}
|
||||
id={NavigationMenuItemType.OBJECT}
|
||||
hasSubMenu={true}
|
||||
onClick={onSelectObject}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SelectableListItem
|
||||
itemId={NavigationMenuItemType.VIEW}
|
||||
onEnter={onSelectView}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={() => (
|
||||
<NavigationMenuItemStyleIcon
|
||||
Icon={IconTable}
|
||||
color="gray"
|
||||
/>
|
||||
)}
|
||||
label={t`View`}
|
||||
id={NavigationMenuItemType.VIEW}
|
||||
hasSubMenu={true}
|
||||
onClick={onSelectView}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SelectableListItem
|
||||
itemId={NavigationMenuItemType.RECORD}
|
||||
onEnter={onSelectRecord}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={() => (
|
||||
<Avatar
|
||||
placeholder="L"
|
||||
type="rounded"
|
||||
backgroundColor={themeCssVariables.color.green4}
|
||||
/>
|
||||
)}
|
||||
label={t`Record`}
|
||||
id={NavigationMenuItemType.RECORD}
|
||||
hasSubMenu={true}
|
||||
onClick={onSelectRecord}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SidePanelGroup>
|
||||
<SidePanelGroup heading={t`Other`}>
|
||||
<SelectableListItem
|
||||
itemId={NavigationMenuItemType.FOLDER}
|
||||
onEnter={isAddingToFolder ? undefined : handleAddFolder}
|
||||
>
|
||||
<SidePanelItemWithAddToNavigationDrag
|
||||
icon={IconFolder}
|
||||
label={t`Folder`}
|
||||
id={NavigationMenuItemType.FOLDER}
|
||||
onClick={handleAddFolder}
|
||||
dragIndex={isDragDisabled ? undefined : 3}
|
||||
payload={{
|
||||
type: NavigationMenuItemType.FOLDER,
|
||||
folderId: 'new',
|
||||
name: t`New folder`,
|
||||
}}
|
||||
disabled={isAddingToFolder}
|
||||
disableDrag={isDragDisabled}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SelectableListItem
|
||||
itemId={NavigationMenuItemType.LINK}
|
||||
onEnter={handleAddLink}
|
||||
>
|
||||
<SidePanelItemWithAddToNavigationDrag
|
||||
icon={IconLink}
|
||||
label={t`Link`}
|
||||
id={NavigationMenuItemType.LINK}
|
||||
onClick={handleAddLink}
|
||||
dragIndex={isDragDisabled ? undefined : 4}
|
||||
payload={{
|
||||
type: NavigationMenuItemType.LINK,
|
||||
linkId: 'new',
|
||||
name: t`Link label`,
|
||||
link: 'https://www.example.com',
|
||||
}}
|
||||
disableDrag={isDragDisabled}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SidePanelGroup>
|
||||
{placeholder}
|
||||
</div>
|
||||
</SidePanelList>
|
||||
)}
|
||||
</SidePanelAddToNavigationDroppable>
|
||||
);
|
||||
};
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useAddObjectToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft';
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
|
||||
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
|
||||
import { SidePanelObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelObjectPickerSubView';
|
||||
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
|
||||
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
export const SidePanelNewSidebarItemObjectSubPage = () => {
|
||||
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
|
||||
const { getIcon } = useIcons();
|
||||
const [objectSearchInput, setObjectSearchInput] = useState('');
|
||||
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { addObjectToDraft } = useAddObjectToNavigationMenuDraft();
|
||||
const { openNavigationMenuItemInSidePanel } =
|
||||
useOpenNavigationMenuItemInSidePanel();
|
||||
const { activeNonSystemObjectMetadataItems } =
|
||||
useFilteredObjectMetadataItems();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const {
|
||||
views,
|
||||
objectMetadataIdsWithIndexView,
|
||||
objectMetadataIdsInWorkspace,
|
||||
} = useNavigationMenuObjectMetadataFromDraft(currentDraft);
|
||||
|
||||
const objectMetadataIdsWithDisplayableViews = new Set(
|
||||
views
|
||||
.filter((view) => view.key !== ViewKey.INDEX)
|
||||
.map((view) => view.objectMetadataId),
|
||||
);
|
||||
|
||||
const { availableObjectMetadataItems } =
|
||||
getAvailableObjectMetadataForNewSidebarItem({
|
||||
objectMetadataItems,
|
||||
activeNonSystemObjectMetadataItems,
|
||||
objectMetadataIdsWithIndexView,
|
||||
objectMetadataIdsWithDisplayableViews,
|
||||
});
|
||||
|
||||
const handleSelectObject = (objectMetadataItem: ObjectMetadataItem) => {
|
||||
if (objectMetadataIdsInWorkspace.has(objectMetadataItem.id)) {
|
||||
return;
|
||||
}
|
||||
const itemId = addObjectToDraft(
|
||||
objectMetadataItem.id,
|
||||
currentDraft,
|
||||
addMenuItemInsertionContext?.targetFolderId,
|
||||
addMenuItemInsertionContext?.targetIndex,
|
||||
getStandardObjectIconColor(objectMetadataItem.nameSingular),
|
||||
);
|
||||
setAddMenuItemInsertionContext(null);
|
||||
openNavigationMenuItemInSidePanel({
|
||||
itemId,
|
||||
pageTitle: objectMetadataItem.labelSingular,
|
||||
pageIcon: getIcon(objectMetadataItem.icon),
|
||||
});
|
||||
};
|
||||
|
||||
const disableDrag = addMenuItemInsertionContext?.disableDrag === true;
|
||||
|
||||
return (
|
||||
<SidePanelObjectPickerSubView
|
||||
objects={availableObjectMetadataItems}
|
||||
searchValue={objectSearchInput}
|
||||
onSearchChange={setObjectSearchInput}
|
||||
onOpenSystemPicker={() =>
|
||||
navigateToSidePanelSubPage(
|
||||
SidePanelSubPages.NewSidebarItemObjectSystemPicker,
|
||||
)
|
||||
}
|
||||
isViewItem={false}
|
||||
onChangeObject={handleSelectObject}
|
||||
objectMenuItemVariant="add"
|
||||
disableDrag={disableDrag}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { useAddObjectToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft';
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
|
||||
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SidePanelSystemObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelSystemObjectPickerSubView';
|
||||
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { useState } from 'react';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
export const SidePanelNewSidebarItemObjectSystemPickerSubPage = () => {
|
||||
const { t } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { addObjectToDraft } = useAddObjectToNavigationMenuDraft();
|
||||
const { openNavigationMenuItemInSidePanel } =
|
||||
useOpenNavigationMenuItemInSidePanel();
|
||||
const { activeNonSystemObjectMetadataItems } =
|
||||
useFilteredObjectMetadataItems();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const {
|
||||
views,
|
||||
objectMetadataIdsWithIndexView,
|
||||
objectMetadataIdsInWorkspace,
|
||||
} = useNavigationMenuObjectMetadataFromDraft(currentDraft);
|
||||
|
||||
const objectMetadataIdsWithDisplayableViews = new Set(
|
||||
views
|
||||
.filter((view) => view.key !== ViewKey.INDEX)
|
||||
.map((view) => view.objectMetadataId),
|
||||
);
|
||||
|
||||
const { availableSystemObjectMetadataItems } =
|
||||
getAvailableObjectMetadataForNewSidebarItem({
|
||||
objectMetadataItems,
|
||||
activeNonSystemObjectMetadataItems,
|
||||
objectMetadataIdsWithIndexView,
|
||||
objectMetadataIdsWithDisplayableViews,
|
||||
});
|
||||
|
||||
const handleSelectObject = (objectMetadataItem: ObjectMetadataItem) => {
|
||||
if (objectMetadataIdsInWorkspace.has(objectMetadataItem.id)) {
|
||||
return;
|
||||
}
|
||||
const itemId = addObjectToDraft(
|
||||
objectMetadataItem.id,
|
||||
currentDraft,
|
||||
addMenuItemInsertionContext?.targetFolderId,
|
||||
addMenuItemInsertionContext?.targetIndex,
|
||||
getStandardObjectIconColor(objectMetadataItem.nameSingular),
|
||||
);
|
||||
setAddMenuItemInsertionContext(null);
|
||||
openNavigationMenuItemInSidePanel({
|
||||
itemId,
|
||||
pageTitle: objectMetadataItem.labelSingular,
|
||||
pageIcon: getIcon(objectMetadataItem.icon),
|
||||
});
|
||||
};
|
||||
|
||||
const disableDrag = addMenuItemInsertionContext?.disableDrag === true;
|
||||
|
||||
return (
|
||||
<SidePanelSystemObjectPickerSubView
|
||||
systemObjects={availableSystemObjectMetadataItems}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
isViewItem={false}
|
||||
onChangeObject={handleSelectObject}
|
||||
objectMenuItemVariant="add"
|
||||
emptyNoResultsText={t`All system objects are already in the sidebar`}
|
||||
disableDrag={disableDrag}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
|
||||
import { SidePanelNewSidebarItemMainMenu } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemMainMenu';
|
||||
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
|
||||
|
||||
export const SidePanelNewSidebarItemPage = () => {
|
||||
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
|
||||
|
||||
return (
|
||||
<SidePanelNewSidebarItemMainMenu
|
||||
onSelectRecord={() =>
|
||||
navigateToSidePanelSubPage(SidePanelSubPages.NewSidebarItemRecord)
|
||||
}
|
||||
onSelectObject={() =>
|
||||
navigateToSidePanelSubPage(SidePanelSubPages.NewSidebarItemObjectPicker)
|
||||
}
|
||||
onSelectView={() =>
|
||||
navigateToSidePanelSubPage(
|
||||
SidePanelSubPages.NewSidebarItemViewObjectPicker,
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
import { Avatar, IconFolder, useIcons } from 'twenty-ui/display';
|
||||
|
||||
import { useAddRecordToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddRecordToNavigationMenuDraft';
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import type { AddToNavigationDragPayload } from '@/navigation-menu-item/types/add-to-navigation-drag-payload';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { SidePanelItemWithAddToNavigationDrag } from '@/side-panel/components/SidePanelItemWithAddToNavigationDrag';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import {
|
||||
CoreObjectNameSingular,
|
||||
NavigationMenuItemType,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
type SearchRecord = {
|
||||
recordId: string;
|
||||
objectNameSingular: string;
|
||||
label: string;
|
||||
imageUrl?: string | null;
|
||||
};
|
||||
|
||||
type SidePanelNewSidebarItemRecordItemProps = {
|
||||
record: SearchRecord;
|
||||
dragIndex?: number;
|
||||
disableDrag?: boolean;
|
||||
};
|
||||
|
||||
export const SidePanelNewSidebarItemRecordItem = ({
|
||||
record,
|
||||
dragIndex,
|
||||
disableDrag = false,
|
||||
}: SidePanelNewSidebarItemRecordItemProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
const { addRecordToDraft } = useAddRecordToNavigationMenuDraft();
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const { openNavigationMenuItemInSidePanel } =
|
||||
useOpenNavigationMenuItemInSidePanel();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.nameSingular === record.objectNameSingular,
|
||||
);
|
||||
const recordPayload: AddToNavigationDragPayload = {
|
||||
type: NavigationMenuItemType.RECORD,
|
||||
recordId: record.recordId,
|
||||
objectMetadataId: objectMetadataItem?.id ?? '',
|
||||
objectNameSingular: record.objectNameSingular,
|
||||
label: record.label,
|
||||
imageUrl: record.imageUrl,
|
||||
};
|
||||
|
||||
const handleSelectRecord = () => {
|
||||
const itemId = addRecordToDraft(
|
||||
{
|
||||
recordId: record.recordId,
|
||||
objectNameSingular: record.objectNameSingular,
|
||||
label: record.label,
|
||||
imageUrl: record.imageUrl,
|
||||
},
|
||||
currentDraft,
|
||||
addMenuItemInsertionContext?.targetFolderId ?? null,
|
||||
addMenuItemInsertionContext?.targetIndex,
|
||||
);
|
||||
setAddMenuItemInsertionContext(null);
|
||||
openNavigationMenuItemInSidePanel({
|
||||
itemId,
|
||||
pageTitle: record.label,
|
||||
pageIcon: objectMetadataItem
|
||||
? getIcon(objectMetadataItem.icon)
|
||||
: IconFolder,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<SelectableListItem itemId={record.recordId} onEnter={handleSelectRecord}>
|
||||
<SidePanelItemWithAddToNavigationDrag
|
||||
customIconContent={
|
||||
<Avatar
|
||||
type={
|
||||
record.objectNameSingular === CoreObjectNameSingular.Company
|
||||
? 'squared'
|
||||
: 'rounded'
|
||||
}
|
||||
avatarUrl={record.imageUrl}
|
||||
placeholderColorSeed={record.recordId}
|
||||
placeholder={record.label}
|
||||
/>
|
||||
}
|
||||
label={record.label}
|
||||
description={
|
||||
objectMetadataItem?.labelSingular ?? record.objectNameSingular
|
||||
}
|
||||
id={record.recordId}
|
||||
onClick={handleSelectRecord}
|
||||
dragIndex={dragIndex}
|
||||
disableDrag={disableDrag}
|
||||
payload={recordPayload}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
};
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
|
||||
import { MAX_SEARCH_RESULTS } from '@/command-menu/constants/MaxSearchResults';
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getObjectPermissionsFromMapByObjectMetadataId } from '@/settings/roles/role-permissions/objects-permissions/utils/getObjectPermissionsFromMapByObjectMetadataId';
|
||||
import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
|
||||
import { SidePanelNewSidebarItemRecordItem } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemRecordItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useQuery } from '@apollo/client/react';
|
||||
import { SearchDocument } from '~/generated/graphql';
|
||||
|
||||
type SearchRecordBase = {
|
||||
recordId: string;
|
||||
objectNameSingular: string;
|
||||
label: string;
|
||||
imageUrl?: string | null;
|
||||
};
|
||||
|
||||
export const SidePanelNewSidebarItemRecordSubPage = () => {
|
||||
const { t } = useLingui();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const disableDrag = addMenuItemInsertionContext?.disableDrag === true;
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const [recordSearchInput, setRecordSearchInput] = useState('');
|
||||
const [deferredRecordSearchInput] = useDebounce(recordSearchInput, 300);
|
||||
const coreClient = useApolloCoreClient();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const nonReadableObjectMetadataItemsNameSingular = objectMetadataItems
|
||||
.filter(
|
||||
(objectMetadataItem) =>
|
||||
!getObjectPermissionsFromMapByObjectMetadataId({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
})?.canReadObjectRecords,
|
||||
)
|
||||
.map((objectMetadataItem) => objectMetadataItem.nameSingular);
|
||||
|
||||
const { data: searchData, loading: recordSearchLoading } = useQuery(
|
||||
SearchDocument,
|
||||
{
|
||||
client: coreClient,
|
||||
variables: {
|
||||
searchInput: deferredRecordSearchInput ?? '',
|
||||
limit: MAX_SEARCH_RESULTS,
|
||||
excludedObjectNameSingulars: [
|
||||
'workspaceMember',
|
||||
...nonReadableObjectMetadataItemsNameSingular,
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const workspaceRecordIds = new Set(
|
||||
currentDraft.flatMap((item) =>
|
||||
isDefined(item.targetRecordId) ? [item.targetRecordId] : [],
|
||||
),
|
||||
);
|
||||
|
||||
const searchRecords =
|
||||
searchData?.search?.edges?.map((edge) => edge.node) ?? [];
|
||||
const availableSearchRecords = searchRecords.filter(
|
||||
(record) => !workspaceRecordIds.has(record.recordId),
|
||||
) as SearchRecordBase[];
|
||||
|
||||
const isEmpty = availableSearchRecords.length === 0 && !recordSearchLoading;
|
||||
const selectableItemIds = isEmpty
|
||||
? []
|
||||
: availableSearchRecords.map((record) => record.recordId);
|
||||
const noResultsText =
|
||||
deferredRecordSearchInput.length > 0
|
||||
? t`No results found`
|
||||
: t`Type to search records`;
|
||||
|
||||
return (
|
||||
<SidePanelSubViewWithSearch
|
||||
searchPlaceholder={t`Search records...`}
|
||||
searchValue={recordSearchInput}
|
||||
onSearchChange={setRecordSearchInput}
|
||||
>
|
||||
<SidePanelAddToNavigationDroppable>
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
loading={recordSearchLoading}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<div ref={innerRef} {...droppableProps}>
|
||||
<SidePanelGroup heading={t`Results`}>
|
||||
{availableSearchRecords.map((record, index) => (
|
||||
<SidePanelNewSidebarItemRecordItem
|
||||
key={record.recordId}
|
||||
record={record}
|
||||
dragIndex={disableDrag ? undefined : index}
|
||||
disableDrag={disableDrag}
|
||||
/>
|
||||
))}
|
||||
</SidePanelGroup>
|
||||
{placeholder}
|
||||
</div>
|
||||
</SidePanelList>
|
||||
)}
|
||||
</SidePanelAddToNavigationDroppable>
|
||||
</SidePanelSubViewWithSearch>
|
||||
);
|
||||
};
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
|
||||
import { SidePanelNewSidebarItemViewObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewObjectPickerSubView';
|
||||
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
|
||||
import { selectedObjectMetadataIdForViewFlowState } from '@/side-panel/states/selectedObjectMetadataIdForViewFlowState';
|
||||
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const SidePanelNewSidebarItemViewObjectPickerSubPage = () => {
|
||||
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const setSelectedObjectMetadataIdForViewFlow = useSetAtomComponentState(
|
||||
selectedObjectMetadataIdForViewFlowState,
|
||||
);
|
||||
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { activeNonSystemObjectMetadataItems } =
|
||||
useFilteredObjectMetadataItems();
|
||||
const { views, objectMetadataIdsWithIndexView } =
|
||||
useNavigationMenuObjectMetadataFromDraft(currentDraft);
|
||||
|
||||
const objectMetadataIdsWithDisplayableViews = new Set(
|
||||
views
|
||||
.filter(
|
||||
(view) =>
|
||||
view.key !== ViewKey.INDEX && view.type !== ViewType.FIELDS_WIDGET,
|
||||
)
|
||||
.map((view) => view.objectMetadataId),
|
||||
);
|
||||
|
||||
const {
|
||||
objectMetadataItemsWithViews,
|
||||
availableSystemObjectMetadataItemsForView,
|
||||
} = getAvailableObjectMetadataForNewSidebarItem({
|
||||
objectMetadataItems,
|
||||
activeNonSystemObjectMetadataItems,
|
||||
objectMetadataIdsWithIndexView,
|
||||
objectMetadataIdsWithDisplayableViews,
|
||||
});
|
||||
|
||||
const handleSelectObject = (objectMetadataItem: ObjectMetadataItem) => {
|
||||
setSelectedObjectMetadataIdForViewFlow(objectMetadataItem.id);
|
||||
navigateToSidePanelSubPage(
|
||||
SidePanelSubPages.NewSidebarItemViewPicker,
|
||||
objectMetadataItem.labelPlural,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SidePanelNewSidebarItemViewObjectPickerSubView
|
||||
objects={objectMetadataItemsWithViews}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
onOpenSystemPicker={() =>
|
||||
navigateToSidePanelSubPage(
|
||||
SidePanelSubPages.NewSidebarItemViewSystemPicker,
|
||||
)
|
||||
}
|
||||
onSelectObject={handleSelectObject}
|
||||
showSystemObjectsOption={
|
||||
availableSystemObjectMetadataItemsForView.length > 0
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconSettings, useIcons } from 'twenty-ui/display';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
|
||||
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
|
||||
type SidePanelNewSidebarItemViewObjectPickerSubViewProps = {
|
||||
objects: ObjectMetadataItem[];
|
||||
searchValue: string;
|
||||
onSearchChange: (value: string) => void;
|
||||
onOpenSystemPicker: () => void;
|
||||
onSelectObject: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
showSystemObjectsOption?: boolean;
|
||||
};
|
||||
|
||||
export const SidePanelNewSidebarItemViewObjectPickerSubView = ({
|
||||
objects,
|
||||
searchValue,
|
||||
onSearchChange,
|
||||
onOpenSystemPicker,
|
||||
onSelectObject,
|
||||
showSystemObjectsOption = true,
|
||||
}: SidePanelNewSidebarItemViewObjectPickerSubViewProps) => {
|
||||
const { t } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const { filteredItems, selectableItemIds, isEmpty, hasSearchQuery } =
|
||||
useSidePanelFilteredPickerItems({
|
||||
items: objects,
|
||||
searchQuery: searchValue,
|
||||
getSearchableValues: (item) => [item.labelPlural],
|
||||
appendSelectableIds: showSystemObjectsOption ? ['system'] : [],
|
||||
});
|
||||
const noResultsText = hasSearchQuery
|
||||
? t`No results found`
|
||||
: t`No objects with views found`;
|
||||
|
||||
return (
|
||||
<SidePanelSubViewWithSearch
|
||||
searchPlaceholder={t`Search an object...`}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={onSearchChange}
|
||||
>
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
<SidePanelGroup heading={t`Objects`}>
|
||||
{filteredItems.map((objectMetadataItem) => (
|
||||
<SelectableListItem
|
||||
key={objectMetadataItem.id}
|
||||
itemId={objectMetadataItem.id}
|
||||
onEnter={() => onSelectObject(objectMetadataItem)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={() => (
|
||||
<NavigationMenuItemStyleIcon
|
||||
Icon={getIcon(objectMetadataItem.icon)}
|
||||
color={getStandardObjectIconColor(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
label={objectMetadataItem.labelPlural}
|
||||
id={objectMetadataItem.id}
|
||||
hasSubMenu
|
||||
onClick={() => onSelectObject(objectMetadataItem)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
{showSystemObjectsOption && (
|
||||
<SelectableListItem itemId="system" onEnter={onOpenSystemPicker}>
|
||||
<CommandMenuItem
|
||||
Icon={() => <NavigationMenuItemStyleIcon Icon={IconSettings} />}
|
||||
label={t`System objects`}
|
||||
id="system"
|
||||
hasSubMenu={true}
|
||||
onClick={onOpenSystemPicker}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
)}
|
||||
</SidePanelGroup>
|
||||
</SidePanelList>
|
||||
</SidePanelSubViewWithSearch>
|
||||
);
|
||||
};
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import { SidePanelNewSidebarItemViewPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewPickerSubView';
|
||||
import { selectedObjectMetadataIdForViewFlowState } from '@/side-panel/states/selectedObjectMetadataIdForViewFlowState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const SidePanelNewSidebarItemViewPickerSubPage = () => {
|
||||
const selectedObjectMetadataIdForViewFlow = useAtomComponentStateValue(
|
||||
selectedObjectMetadataIdForViewFlowState,
|
||||
);
|
||||
|
||||
if (!isDefined(selectedObjectMetadataIdForViewFlow)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SidePanelNewSidebarItemViewPickerSubView
|
||||
selectedObjectMetadataIdForView={selectedObjectMetadataIdForViewFlow}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useAddViewToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft';
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
|
||||
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelItemWithAddToNavigationDrag } from '@/side-panel/components/SidePanelItemWithAddToNavigationDrag';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
|
||||
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { type View } from '@/views/types/View';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { ViewVisibility } from '~/generated-metadata/graphql';
|
||||
|
||||
type SidePanelNewSidebarItemViewPickerSubViewProps = {
|
||||
selectedObjectMetadataIdForView: string;
|
||||
};
|
||||
|
||||
export const SidePanelNewSidebarItemViewPickerSubView = ({
|
||||
selectedObjectMetadataIdForView,
|
||||
}: SidePanelNewSidebarItemViewPickerSubViewProps) => {
|
||||
const { t } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const { addViewToDraft } = useAddViewToNavigationMenuDraft();
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const { openNavigationMenuItemInSidePanel } =
|
||||
useOpenNavigationMenuItemInSidePanel();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { views } = useNavigationMenuObjectMetadataFromDraft(currentDraft);
|
||||
|
||||
const viewsForSelectedObject = views
|
||||
.filter(
|
||||
(view) =>
|
||||
view.objectMetadataId === selectedObjectMetadataIdForView &&
|
||||
view.key !== ViewKey.INDEX &&
|
||||
view.type !== ViewType.FIELDS_WIDGET &&
|
||||
view.visibility === ViewVisibility.WORKSPACE,
|
||||
)
|
||||
.sort((a, b) => a.position - b.position);
|
||||
|
||||
const selectedObjectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === selectedObjectMetadataIdForView,
|
||||
);
|
||||
|
||||
const {
|
||||
filteredItems: filteredViews,
|
||||
selectableItemIds,
|
||||
isEmpty,
|
||||
hasSearchQuery,
|
||||
} = useSidePanelFilteredPickerItems({
|
||||
items: viewsForSelectedObject,
|
||||
searchQuery: searchValue,
|
||||
getSearchableValues: (view) => [view.name],
|
||||
});
|
||||
const noResultsText = hasSearchQuery
|
||||
? t`No results found`
|
||||
: t`No custom views available`;
|
||||
|
||||
const isDragDisabled = addMenuItemInsertionContext?.disableDrag === true;
|
||||
|
||||
const handleSelectView = (view: View) => {
|
||||
const itemId = addViewToDraft(
|
||||
view.id,
|
||||
currentDraft,
|
||||
addMenuItemInsertionContext?.targetFolderId ?? null,
|
||||
addMenuItemInsertionContext?.targetIndex,
|
||||
isDefined(selectedObjectMetadataItem)
|
||||
? getStandardObjectIconColor(selectedObjectMetadataItem.nameSingular)
|
||||
: undefined,
|
||||
);
|
||||
setAddMenuItemInsertionContext(null);
|
||||
openNavigationMenuItemInSidePanel({
|
||||
itemId,
|
||||
pageTitle: view.name,
|
||||
pageIcon: getIcon(view.icon),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<SidePanelSubViewWithSearch
|
||||
searchPlaceholder={t`Search a view...`}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
>
|
||||
<SidePanelAddToNavigationDroppable>
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<div ref={innerRef} {...droppableProps}>
|
||||
<SidePanelGroup heading={t`Views`}>
|
||||
{filteredViews.map((view, index) => (
|
||||
<SelectableListItem
|
||||
key={view.id}
|
||||
itemId={view.id}
|
||||
onEnter={() => handleSelectView(view)}
|
||||
>
|
||||
<SidePanelItemWithAddToNavigationDrag
|
||||
customIconContent={
|
||||
selectedObjectMetadataItem ? (
|
||||
<ObjectIconWithViewOverlay
|
||||
ObjectIcon={getIcon(
|
||||
selectedObjectMetadataItem.icon,
|
||||
)}
|
||||
ViewIcon={getIcon(view.icon)}
|
||||
objectColor={getStandardObjectIconColor(
|
||||
selectedObjectMetadataItem.nameSingular,
|
||||
)}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
icon={
|
||||
selectedObjectMetadataItem
|
||||
? undefined
|
||||
: getIcon(view.icon)
|
||||
}
|
||||
label={view.name}
|
||||
id={view.id}
|
||||
onClick={() => handleSelectView(view)}
|
||||
dragIndex={isDragDisabled ? undefined : index}
|
||||
disableDrag={isDragDisabled}
|
||||
payload={{
|
||||
type: NavigationMenuItemType.VIEW,
|
||||
viewId: view.id,
|
||||
label: view.name,
|
||||
}}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SidePanelGroup>
|
||||
{placeholder}
|
||||
</div>
|
||||
</SidePanelList>
|
||||
)}
|
||||
</SidePanelAddToNavigationDroppable>
|
||||
</SidePanelSubViewWithSearch>
|
||||
);
|
||||
};
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
|
||||
import { SidePanelNewSidebarItemViewSystemSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewSystemSubView';
|
||||
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
|
||||
import { selectedObjectMetadataIdForViewFlowState } from '@/side-panel/states/selectedObjectMetadataIdForViewFlowState';
|
||||
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
|
||||
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const SidePanelNewSidebarItemViewSystemPickerSubPage = () => {
|
||||
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const setSelectedObjectMetadataIdForViewFlow = useSetAtomComponentState(
|
||||
selectedObjectMetadataIdForViewFlowState,
|
||||
);
|
||||
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { activeNonSystemObjectMetadataItems } =
|
||||
useFilteredObjectMetadataItems();
|
||||
const { views, objectMetadataIdsWithIndexView } =
|
||||
useNavigationMenuObjectMetadataFromDraft(currentDraft);
|
||||
|
||||
const objectMetadataIdsWithDisplayableViews = new Set(
|
||||
views
|
||||
.filter(
|
||||
(view) =>
|
||||
view.key !== ViewKey.INDEX && view.type !== ViewType.FIELDS_WIDGET,
|
||||
)
|
||||
.map((view) => view.objectMetadataId),
|
||||
);
|
||||
|
||||
const { availableSystemObjectMetadataItemsForView } =
|
||||
getAvailableObjectMetadataForNewSidebarItem({
|
||||
objectMetadataItems,
|
||||
activeNonSystemObjectMetadataItems,
|
||||
objectMetadataIdsWithIndexView,
|
||||
objectMetadataIdsWithDisplayableViews,
|
||||
});
|
||||
|
||||
const handleSelectObject = (objectMetadataItem: ObjectMetadataItem) => {
|
||||
setSelectedObjectMetadataIdForViewFlow(objectMetadataItem.id);
|
||||
navigateToSidePanelSubPage(
|
||||
SidePanelSubPages.NewSidebarItemViewPicker,
|
||||
objectMetadataItem.labelPlural,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SidePanelNewSidebarItemViewSystemSubView
|
||||
systemObjects={availableSystemObjectMetadataItemsForView}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
onSelectObject={handleSelectObject}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
|
||||
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
|
||||
type SidePanelNewSidebarItemViewSystemSubViewProps = {
|
||||
systemObjects: ObjectMetadataItem[];
|
||||
searchValue: string;
|
||||
onSearchChange: (value: string) => void;
|
||||
onSelectObject: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
};
|
||||
|
||||
export const SidePanelNewSidebarItemViewSystemSubView = ({
|
||||
systemObjects,
|
||||
searchValue,
|
||||
onSearchChange,
|
||||
onSelectObject,
|
||||
}: SidePanelNewSidebarItemViewSystemSubViewProps) => {
|
||||
const { t } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const {
|
||||
filteredItems: filteredSystemObjectMetadataItems,
|
||||
selectableItemIds,
|
||||
isEmpty,
|
||||
hasSearchQuery,
|
||||
} = useSidePanelFilteredPickerItems({
|
||||
items: systemObjects,
|
||||
searchQuery: searchValue,
|
||||
getSearchableValues: (item) => [item.labelPlural],
|
||||
});
|
||||
const noResultsText = hasSearchQuery
|
||||
? t`No results found`
|
||||
: t`No system objects with views found`;
|
||||
|
||||
return (
|
||||
<SidePanelSubViewWithSearch
|
||||
searchPlaceholder={t`Search a system object...`}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={onSearchChange}
|
||||
>
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
<SidePanelGroup heading={t`System objects`}>
|
||||
{filteredSystemObjectMetadataItems.map((objectMetadataItem) => (
|
||||
<SelectableListItem
|
||||
key={objectMetadataItem.id}
|
||||
itemId={objectMetadataItem.id}
|
||||
onEnter={() => onSelectObject(objectMetadataItem)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={() => (
|
||||
<NavigationMenuItemStyleIcon
|
||||
Icon={getIcon(objectMetadataItem.icon)}
|
||||
color={getStandardObjectIconColor(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
label={objectMetadataItem.labelPlural}
|
||||
id={objectMetadataItem.id}
|
||||
onClick={() => onSelectObject(objectMetadataItem)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SidePanelGroup>
|
||||
</SidePanelList>
|
||||
</SidePanelSubViewWithSearch>
|
||||
);
|
||||
};
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
|
||||
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SidePanelItemWithAddToNavigationDrag } from '@/side-panel/components/SidePanelItemWithAddToNavigationDrag';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { indexViewIdFromObjectMetadataItemFamilySelector } from '@/views/states/selectors/indexViewIdFromObjectMetadataItemFamilySelector';
|
||||
|
||||
type SidePanelObjectMenuItemProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
onSelect: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
variant: 'add' | 'edit';
|
||||
dragIndex?: number;
|
||||
disableDrag?: boolean;
|
||||
};
|
||||
|
||||
export const SidePanelObjectMenuItem = ({
|
||||
objectMetadataItem,
|
||||
onSelect,
|
||||
variant,
|
||||
dragIndex,
|
||||
disableDrag = false,
|
||||
}: SidePanelObjectMenuItemProps) => {
|
||||
const { t } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const { currentDraft } = useDraftNavigationMenuItems();
|
||||
const { objectMetadataIdsInWorkspace } =
|
||||
useNavigationMenuObjectMetadataFromDraft(currentDraft);
|
||||
const isAlreadyInNavbar = objectMetadataIdsInWorkspace.has(
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
const defaultViewId = useAtomFamilySelectorValue(
|
||||
indexViewIdFromObjectMetadataItemFamilySelector,
|
||||
{ objectMetadataItemId: objectMetadataItem.id },
|
||||
);
|
||||
const Icon = getIcon(objectMetadataItem.icon);
|
||||
const iconColor = getStandardObjectIconColor(objectMetadataItem.nameSingular);
|
||||
const isDisabled = isAlreadyInNavbar || !isDefined(defaultViewId);
|
||||
|
||||
const handleClick = () => {
|
||||
if (isDisabled || !defaultViewId) {
|
||||
return;
|
||||
}
|
||||
onSelect(objectMetadataItem);
|
||||
};
|
||||
|
||||
const styledIcon = () => (
|
||||
<NavigationMenuItemStyleIcon Icon={Icon} color={iconColor} />
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectableListItem itemId={objectMetadataItem.id} onEnter={handleClick}>
|
||||
{variant === 'add' && !isDisabled ? (
|
||||
<SidePanelItemWithAddToNavigationDrag
|
||||
icon={styledIcon}
|
||||
label={objectMetadataItem.labelPlural}
|
||||
id={objectMetadataItem.id}
|
||||
onClick={handleClick}
|
||||
dragIndex={dragIndex}
|
||||
disableDrag={disableDrag}
|
||||
payload={{
|
||||
type: NavigationMenuItemType.OBJECT,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
label: objectMetadataItem.labelPlural,
|
||||
iconColor,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<CommandMenuItem
|
||||
Icon={styledIcon}
|
||||
label={objectMetadataItem.labelPlural}
|
||||
id={objectMetadataItem.id}
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
description={isAlreadyInNavbar ? t`Already in navbar` : undefined}
|
||||
/>
|
||||
)}
|
||||
</SelectableListItem>
|
||||
);
|
||||
};
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SidePanelObjectMenuItem } from '@/side-panel/pages/navigation-menu-item/components/SidePanelObjectMenuItem';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
|
||||
type SidePanelObjectPickerItemProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
isViewItem: boolean;
|
||||
onSelectObjectForViewEdit?: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
onChangeObject: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
objectMenuItemVariant?: 'add' | 'edit';
|
||||
dragIndex?: number;
|
||||
disableDrag?: boolean;
|
||||
};
|
||||
|
||||
export const SidePanelObjectPickerItem = ({
|
||||
objectMetadataItem,
|
||||
isViewItem,
|
||||
onSelectObjectForViewEdit,
|
||||
onChangeObject,
|
||||
objectMenuItemVariant = 'edit',
|
||||
dragIndex,
|
||||
disableDrag = false,
|
||||
}: SidePanelObjectPickerItemProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
if (isViewItem && isDefined(onSelectObjectForViewEdit)) {
|
||||
return (
|
||||
<SelectableListItem
|
||||
itemId={objectMetadataItem.id}
|
||||
onEnter={() => onSelectObjectForViewEdit(objectMetadataItem)}
|
||||
>
|
||||
<CommandMenuItem
|
||||
Icon={() => (
|
||||
<NavigationMenuItemStyleIcon
|
||||
Icon={getIcon(objectMetadataItem.icon)}
|
||||
color={getStandardObjectIconColor(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
label={objectMetadataItem.labelPlural}
|
||||
id={objectMetadataItem.id}
|
||||
onClick={() => onSelectObjectForViewEdit(objectMetadataItem)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SidePanelObjectMenuItem
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
onSelect={onChangeObject}
|
||||
variant={objectMenuItemVariant}
|
||||
dragIndex={dragIndex}
|
||||
disableDrag={disableDrag}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconSettings } from 'twenty-ui/display';
|
||||
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
|
||||
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
|
||||
import { SidePanelObjectPickerItem } from '@/side-panel/pages/navigation-menu-item/components/SidePanelObjectPickerItem';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
|
||||
type SidePanelObjectPickerSubViewProps = {
|
||||
objects: ObjectMetadataItem[];
|
||||
searchValue: string;
|
||||
onSearchChange: (value: string) => void;
|
||||
onOpenSystemPicker: () => void;
|
||||
isViewItem: boolean;
|
||||
onSelectObjectForViewEdit?: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
onChangeObject: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
objectMenuItemVariant?: 'add' | 'edit';
|
||||
emptyNoResultsText?: string;
|
||||
disableDrag?: boolean;
|
||||
};
|
||||
|
||||
export const SidePanelObjectPickerSubView = ({
|
||||
objects,
|
||||
searchValue,
|
||||
onSearchChange,
|
||||
onOpenSystemPicker,
|
||||
isViewItem,
|
||||
onSelectObjectForViewEdit,
|
||||
onChangeObject,
|
||||
objectMenuItemVariant = 'edit',
|
||||
emptyNoResultsText,
|
||||
disableDrag = false,
|
||||
}: SidePanelObjectPickerSubViewProps) => {
|
||||
const { t } = useLingui();
|
||||
const { filteredItems, selectableItemIds, isEmpty, hasSearchQuery } =
|
||||
useSidePanelFilteredPickerItems({
|
||||
items: objects,
|
||||
searchQuery: searchValue,
|
||||
getSearchableValues: (item) => [item.labelPlural],
|
||||
appendSelectableIds: ['system'],
|
||||
});
|
||||
|
||||
const noResultsText = hasSearchQuery
|
||||
? t`No results found`
|
||||
: (emptyNoResultsText ?? t`All objects are already in the sidebar`);
|
||||
|
||||
const isAddVariant = objectMenuItemVariant === 'add';
|
||||
|
||||
const listContent = (
|
||||
<SidePanelGroup heading={t`Objects`}>
|
||||
{filteredItems.map((objectMetadataItem, index) => (
|
||||
<SidePanelObjectPickerItem
|
||||
key={objectMetadataItem.id}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
isViewItem={isViewItem}
|
||||
onSelectObjectForViewEdit={onSelectObjectForViewEdit}
|
||||
onChangeObject={onChangeObject}
|
||||
objectMenuItemVariant={objectMenuItemVariant}
|
||||
dragIndex={isAddVariant && !disableDrag ? index : undefined}
|
||||
disableDrag={disableDrag}
|
||||
/>
|
||||
))}
|
||||
<SelectableListItem itemId="system" onEnter={onOpenSystemPicker}>
|
||||
<CommandMenuItem
|
||||
Icon={() => <NavigationMenuItemStyleIcon Icon={IconSettings} />}
|
||||
label={t`System objects`}
|
||||
id="system"
|
||||
hasSubMenu
|
||||
onClick={onOpenSystemPicker}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SidePanelGroup>
|
||||
);
|
||||
|
||||
return (
|
||||
<SidePanelSubViewWithSearch
|
||||
searchPlaceholder={t`Search an object...`}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={onSearchChange}
|
||||
>
|
||||
{isAddVariant ? (
|
||||
<SidePanelAddToNavigationDroppable>
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
{/* oxlint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<div ref={innerRef} {...droppableProps}>
|
||||
{listContent}
|
||||
{placeholder}
|
||||
</div>
|
||||
</SidePanelList>
|
||||
)}
|
||||
</SidePanelAddToNavigationDroppable>
|
||||
) : (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
{listContent}
|
||||
</SidePanelList>
|
||||
)}
|
||||
</SidePanelSubViewWithSearch>
|
||||
);
|
||||
};
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
|
||||
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
|
||||
import { SidePanelObjectPickerItem } from '@/side-panel/pages/navigation-menu-item/components/SidePanelObjectPickerItem';
|
||||
|
||||
type SidePanelSystemObjectPickerSubViewProps = {
|
||||
systemObjects: ObjectMetadataItem[];
|
||||
searchValue: string;
|
||||
onSearchChange: (value: string) => void;
|
||||
isViewItem: boolean;
|
||||
onSelectObjectForViewEdit?: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
onChangeObject: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
objectMenuItemVariant?: 'add' | 'edit';
|
||||
emptyNoResultsText?: string;
|
||||
disableDrag?: boolean;
|
||||
};
|
||||
|
||||
export const SidePanelSystemObjectPickerSubView = ({
|
||||
systemObjects,
|
||||
searchValue,
|
||||
onSearchChange,
|
||||
isViewItem,
|
||||
onSelectObjectForViewEdit,
|
||||
onChangeObject,
|
||||
objectMenuItemVariant = 'edit',
|
||||
emptyNoResultsText,
|
||||
disableDrag = false,
|
||||
}: SidePanelSystemObjectPickerSubViewProps) => {
|
||||
const { t } = useLingui();
|
||||
const { filteredItems, selectableItemIds, isEmpty, hasSearchQuery } =
|
||||
useSidePanelFilteredPickerItems({
|
||||
items: systemObjects,
|
||||
searchQuery: searchValue,
|
||||
getSearchableValues: (item) => [item.labelPlural],
|
||||
});
|
||||
|
||||
const noResultsText = hasSearchQuery
|
||||
? t`No results found`
|
||||
: (emptyNoResultsText ?? t`No system objects available`);
|
||||
|
||||
const isAddVariant = objectMenuItemVariant === 'add';
|
||||
|
||||
const listContent = (
|
||||
<SidePanelGroup heading={t`System objects`}>
|
||||
{filteredItems.map((objectMetadataItem, index) => (
|
||||
<SidePanelObjectPickerItem
|
||||
key={objectMetadataItem.id}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
isViewItem={isViewItem}
|
||||
onSelectObjectForViewEdit={onSelectObjectForViewEdit}
|
||||
onChangeObject={onChangeObject}
|
||||
objectMenuItemVariant={objectMenuItemVariant}
|
||||
dragIndex={isAddVariant && !disableDrag ? index : undefined}
|
||||
disableDrag={disableDrag}
|
||||
/>
|
||||
))}
|
||||
</SidePanelGroup>
|
||||
);
|
||||
|
||||
return (
|
||||
<SidePanelSubViewWithSearch
|
||||
searchPlaceholder={t`Search a system object...`}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={onSearchChange}
|
||||
>
|
||||
{isAddVariant ? (
|
||||
<SidePanelAddToNavigationDroppable>
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<div ref={innerRef} {...droppableProps}>
|
||||
{listContent}
|
||||
{placeholder}
|
||||
</div>
|
||||
</SidePanelList>
|
||||
)}
|
||||
</SidePanelAddToNavigationDroppable>
|
||||
) : (
|
||||
<SidePanelList
|
||||
commandGroups={[]}
|
||||
selectableItemIds={selectableItemIds}
|
||||
noResults={isEmpty}
|
||||
noResultsText={noResultsText}
|
||||
>
|
||||
{listContent}
|
||||
</SidePanelList>
|
||||
)}
|
||||
</SidePanelSubViewWithSearch>
|
||||
);
|
||||
};
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconFolder } from 'twenty-ui/display';
|
||||
|
||||
import { useAddFolderToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddFolderToNavigationMenuDraft';
|
||||
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/hooks/useNavigationMenuItemsDraftState';
|
||||
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
export const useAddFolderToNavigationMenu = () => {
|
||||
const { t } = useLingui();
|
||||
const { addFolderToDraft } = useAddFolderToNavigationMenuDraft();
|
||||
const { workspaceNavigationMenuItems } = useNavigationMenuItemsDraftState();
|
||||
const navigationMenuItemsDraft = useAtomStateValue(
|
||||
navigationMenuItemsDraftState,
|
||||
);
|
||||
const { openNavigationMenuItemInSidePanel } =
|
||||
useOpenNavigationMenuItemInSidePanel();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
|
||||
const currentDraft = isDefined(navigationMenuItemsDraft)
|
||||
? navigationMenuItemsDraft
|
||||
: workspaceNavigationMenuItems;
|
||||
|
||||
const handleAddFolder = () => {
|
||||
const targetFolderId = addMenuItemInsertionContext?.targetFolderId ?? null;
|
||||
const targetIndex = addMenuItemInsertionContext?.targetIndex;
|
||||
|
||||
const itemId = addFolderToDraft(
|
||||
t`New folder`,
|
||||
currentDraft,
|
||||
targetFolderId,
|
||||
targetIndex,
|
||||
);
|
||||
|
||||
setAddMenuItemInsertionContext(null);
|
||||
openNavigationMenuItemInSidePanel({
|
||||
itemId,
|
||||
pageTitle: t`Edit folder`,
|
||||
pageIcon: IconFolder,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
};
|
||||
|
||||
return { handleAddFolder };
|
||||
};
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconLink } from 'twenty-ui/display';
|
||||
|
||||
import { useAddLinkToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddLinkToNavigationMenuDraft';
|
||||
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/hooks/useNavigationMenuItemsDraftState';
|
||||
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
export const useAddLinkToNavigationMenu = () => {
|
||||
const { t } = useLingui();
|
||||
const { addLinkToDraft } = useAddLinkToNavigationMenuDraft();
|
||||
const { workspaceNavigationMenuItems } = useNavigationMenuItemsDraftState();
|
||||
const navigationMenuItemsDraft = useAtomStateValue(
|
||||
navigationMenuItemsDraftState,
|
||||
);
|
||||
const { openNavigationMenuItemInSidePanel } =
|
||||
useOpenNavigationMenuItemInSidePanel();
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
|
||||
const currentDraft = isDefined(navigationMenuItemsDraft)
|
||||
? navigationMenuItemsDraft
|
||||
: workspaceNavigationMenuItems;
|
||||
|
||||
const handleAddLink = () => {
|
||||
const targetFolderId = addMenuItemInsertionContext?.targetFolderId ?? null;
|
||||
const targetIndex = addMenuItemInsertionContext?.targetIndex;
|
||||
|
||||
const itemId = addLinkToDraft(
|
||||
t`Link label`,
|
||||
'www.example.com',
|
||||
currentDraft,
|
||||
targetFolderId,
|
||||
targetIndex,
|
||||
);
|
||||
|
||||
setAddMenuItemInsertionContext(null);
|
||||
openNavigationMenuItemInSidePanel({
|
||||
itemId,
|
||||
pageTitle: t`Edit link`,
|
||||
pageIcon: IconLink,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
};
|
||||
|
||||
return { handleAddLink };
|
||||
};
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useDraftNavigationMenuItemsAllFolders } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItemsAllFolders';
|
||||
import { useDraftNavigationMenuItemsWorkspaceFolders } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItemsWorkspaceFolders';
|
||||
import { useNavigationMenuItemMoveRemove } from '@/navigation-menu-item/hooks/useNavigationMenuItemMoveRemove';
|
||||
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
type FolderOption = {
|
||||
id: string;
|
||||
name: string;
|
||||
folderId?: string | null;
|
||||
};
|
||||
|
||||
const getDescendantFolderIds = (
|
||||
folderId: string,
|
||||
allFolders: FolderOption[],
|
||||
): Set<string> => {
|
||||
const result = new Set<string>();
|
||||
for (const folder of allFolders) {
|
||||
if (folder.folderId !== folderId) continue;
|
||||
result.add(folder.id);
|
||||
getDescendantFolderIds(folder.id, allFolders).forEach((id) =>
|
||||
result.add(id),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const excludeCurrentFolder = <T extends { id: string }>(
|
||||
folders: T[],
|
||||
currentFolderId: string | null,
|
||||
): T[] =>
|
||||
!isDefined(currentFolderId)
|
||||
? folders
|
||||
: folders.filter((folder) => folder.id !== currentFolderId);
|
||||
|
||||
export const useFolderPickerSelectionData = () => {
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
const { moveToFolder } = useNavigationMenuItemMoveRemove();
|
||||
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const { selectedItem } = useSelectedNavigationMenuItemEditItem();
|
||||
const selectedItemType = selectedItem?.type ?? null;
|
||||
const { allFolders } = useDraftNavigationMenuItemsAllFolders();
|
||||
const { workspaceFolders } = useDraftNavigationMenuItemsWorkspaceFolders();
|
||||
|
||||
const selectedFolderId =
|
||||
selectedItemType === NavigationMenuItemType.FOLDER
|
||||
? selectedNavigationMenuItemInEditMode
|
||||
: null;
|
||||
const currentFolderId =
|
||||
selectedItemType === NavigationMenuItemType.FOLDER
|
||||
? (selectedItem?.id ?? null)
|
||||
: (selectedItem?.folderId ?? null);
|
||||
|
||||
const descendantFolderIds =
|
||||
selectedItemType === NavigationMenuItemType.FOLDER &&
|
||||
isDefined(selectedFolderId)
|
||||
? getDescendantFolderIds(selectedFolderId, allFolders)
|
||||
: new Set<string>();
|
||||
|
||||
const includeNoFolderOption =
|
||||
(selectedItemType === NavigationMenuItemType.FOLDER &&
|
||||
isDefined(selectedFolderId)) ||
|
||||
(selectedItemType === NavigationMenuItemType.LINK &&
|
||||
isDefined(currentFolderId));
|
||||
|
||||
const folders =
|
||||
includeNoFolderOption &&
|
||||
selectedItemType === NavigationMenuItemType.FOLDER &&
|
||||
isDefined(selectedFolderId)
|
||||
? allFolders.filter(
|
||||
(folder) =>
|
||||
folder.id !== selectedFolderId &&
|
||||
!descendantFolderIds.has(folder.id),
|
||||
)
|
||||
: excludeCurrentFolder(allFolders, currentFolderId);
|
||||
|
||||
const foldersToShow = includeNoFolderOption
|
||||
? folders
|
||||
: excludeCurrentFolder(workspaceFolders, currentFolderId);
|
||||
|
||||
const handleSelectFolder = (folderId: string | null) => {
|
||||
if (isDefined(selectedNavigationMenuItemInEditMode)) {
|
||||
moveToFolder(selectedNavigationMenuItemInEditMode, folderId);
|
||||
closeSidePanelMenu();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
foldersToShow,
|
||||
includeNoFolderOption,
|
||||
handleSelectFolder,
|
||||
};
|
||||
};
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconColumnInsertRight } from 'twenty-ui/display';
|
||||
|
||||
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
|
||||
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
import { type OrganizeActionsProps } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOrganizeActions';
|
||||
import { useNavigationMenuItemMoveRemove } from '@/navigation-menu-item/hooks/useNavigationMenuItemMoveRemove';
|
||||
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/hooks/useNavigationMenuItemsDraftState';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { type AddMenuItemInsertionContext } from '@/navigation-menu-item/types/AddMenuItemInsertionContext';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
|
||||
const getAddMenuItemInsertionContext = (
|
||||
selectedItem: { id: string; folderId?: string | null },
|
||||
workspaceNavigationMenuItems: Array<{
|
||||
id: string;
|
||||
folderId?: string | null;
|
||||
userWorkspaceId?: string | null;
|
||||
}>,
|
||||
offset: 0 | 1,
|
||||
): AddMenuItemInsertionContext | null => {
|
||||
const targetFolderId = selectedItem.folderId ?? null;
|
||||
const itemsInFolder = workspaceNavigationMenuItems.filter(
|
||||
(item) =>
|
||||
(item.folderId ?? null) === targetFolderId &&
|
||||
!isDefined(item.userWorkspaceId),
|
||||
);
|
||||
const selectedIndexInFolder = itemsInFolder.findIndex(
|
||||
(item) => item.id === selectedItem.id,
|
||||
);
|
||||
|
||||
if (selectedIndexInFolder < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
targetFolderId,
|
||||
targetIndex: selectedIndexInFolder + offset,
|
||||
disableDrag: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const useNavigationMenuItemEditOrganizeActions =
|
||||
(): OrganizeActionsProps => {
|
||||
const { t } = useLingui();
|
||||
const { closeSidePanelMenu } = useSidePanelMenu();
|
||||
const { navigateSidePanel } = useNavigateSidePanel();
|
||||
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const setSelectedNavigationMenuItemInEditMode = useSetAtomState(
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const { workspaceNavigationMenuItems } = useNavigationMenuItemsDraftState();
|
||||
const items = useWorkspaceSectionItems();
|
||||
const { moveUp, moveDown, remove } = useNavigationMenuItemMoveRemove();
|
||||
|
||||
const selectedItem = selectedNavigationMenuItemInEditMode
|
||||
? items.find((item) => item.id === selectedNavigationMenuItemInEditMode)
|
||||
: undefined;
|
||||
|
||||
const folderId = selectedItem?.folderId ?? null;
|
||||
const siblings = items
|
||||
.filter(
|
||||
(item) =>
|
||||
('folderId' in item ? (item.folderId ?? null) : null) === folderId,
|
||||
)
|
||||
.sort((a, b) => a.position - b.position);
|
||||
const selectedIndexInSiblings = selectedItem
|
||||
? siblings.findIndex((item) => item.id === selectedItem.id)
|
||||
: -1;
|
||||
|
||||
const canMoveUp =
|
||||
selectedIndexInSiblings > 0 &&
|
||||
selectedItem != null &&
|
||||
isDefined(selectedNavigationMenuItemInEditMode);
|
||||
const canMoveDown =
|
||||
selectedIndexInSiblings >= 0 &&
|
||||
selectedIndexInSiblings < siblings.length - 1 &&
|
||||
selectedItem != null &&
|
||||
isDefined(selectedNavigationMenuItemInEditMode);
|
||||
|
||||
const handleMoveUp = () => {
|
||||
if (canMoveUp && isDefined(selectedNavigationMenuItemInEditMode)) {
|
||||
moveUp(selectedNavigationMenuItemInEditMode);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMoveDown = () => {
|
||||
if (canMoveDown && isDefined(selectedNavigationMenuItemInEditMode)) {
|
||||
moveDown(selectedNavigationMenuItemInEditMode);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = () => {
|
||||
if (isDefined(selectedNavigationMenuItemInEditMode)) {
|
||||
remove(selectedNavigationMenuItemInEditMode);
|
||||
setSelectedNavigationMenuItemInEditMode(null);
|
||||
closeSidePanelMenu();
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddAtOffset = (offset: 0 | 1) => {
|
||||
if (!isDefined(selectedItem)) return;
|
||||
const context = getAddMenuItemInsertionContext(
|
||||
selectedItem,
|
||||
workspaceNavigationMenuItems,
|
||||
offset,
|
||||
);
|
||||
if (!context) return;
|
||||
setAddMenuItemInsertionContext(context);
|
||||
navigateSidePanel({
|
||||
page: SidePanelPages.NavigationMenuAddItem,
|
||||
pageTitle: t`New sidebar item`,
|
||||
pageIcon: IconColumnInsertRight,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddBefore = () => handleAddAtOffset(0);
|
||||
const handleAddAfter = () => handleAddAtOffset(1);
|
||||
|
||||
return {
|
||||
canMoveUp,
|
||||
canMoveDown,
|
||||
onMoveUp: handleMoveUp,
|
||||
onMoveDown: handleMoveDown,
|
||||
onRemove: handleRemove,
|
||||
onAddBefore: handleAddBefore,
|
||||
onAddAfter: handleAddAfter,
|
||||
};
|
||||
};
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
export enum SidePanelNavigationItemActions {
|
||||
MOVE_UP = 'move-up',
|
||||
MOVE_DOWN = 'move-down',
|
||||
MOVE_TO_FOLDER = 'move-to-folder',
|
||||
ADD_BEFORE = 'add-before',
|
||||
ADD_AFTER = 'add-after',
|
||||
REMOVE = 'remove',
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
type GetAvailableObjectMetadataForNewSidebarItemProps = {
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
activeNonSystemObjectMetadataItems: ObjectMetadataItem[];
|
||||
objectMetadataIdsWithIndexView: Set<string>;
|
||||
objectMetadataIdsWithDisplayableViews: Set<string>;
|
||||
};
|
||||
|
||||
type GetAvailableObjectMetadataForNewSidebarItemResult = {
|
||||
availableObjectMetadataItems: ObjectMetadataItem[];
|
||||
availableSystemObjectMetadataItems: ObjectMetadataItem[];
|
||||
objectMetadataItemsWithViews: ObjectMetadataItem[];
|
||||
availableSystemObjectMetadataItemsForView: ObjectMetadataItem[];
|
||||
};
|
||||
|
||||
export const getAvailableObjectMetadataForNewSidebarItem = ({
|
||||
objectMetadataItems,
|
||||
activeNonSystemObjectMetadataItems,
|
||||
objectMetadataIdsWithIndexView,
|
||||
objectMetadataIdsWithDisplayableViews,
|
||||
}: GetAvailableObjectMetadataForNewSidebarItemProps): GetAvailableObjectMetadataForNewSidebarItemResult => {
|
||||
const availableObjectMetadataItems = activeNonSystemObjectMetadataItems
|
||||
.filter((item) => objectMetadataIdsWithIndexView.has(item.id))
|
||||
.sort((a, b) => a.labelPlural.localeCompare(b.labelPlural));
|
||||
|
||||
const activeSystemObjectMetadataItems = objectMetadataItems
|
||||
.filter((item) => item.isActive && item.isSystem)
|
||||
.sort((a, b) => a.labelPlural.localeCompare(b.labelPlural));
|
||||
|
||||
const availableSystemObjectMetadataItems =
|
||||
activeSystemObjectMetadataItems.filter((item) =>
|
||||
objectMetadataIdsWithIndexView.has(item.id),
|
||||
);
|
||||
|
||||
const objectMetadataItemsWithViews = objectMetadataItems
|
||||
.filter(
|
||||
(item) =>
|
||||
item.isActive && objectMetadataIdsWithDisplayableViews.has(item.id),
|
||||
)
|
||||
.filter((item) => !item.isSystem)
|
||||
.sort((a, b) => a.labelPlural.localeCompare(b.labelPlural));
|
||||
|
||||
const availableSystemObjectMetadataItemsForView =
|
||||
activeSystemObjectMetadataItems.filter((item) =>
|
||||
objectMetadataIdsWithDisplayableViews.has(item.id),
|
||||
);
|
||||
|
||||
return {
|
||||
availableObjectMetadataItems,
|
||||
availableSystemObjectMetadataItems,
|
||||
objectMetadataItemsWithViews,
|
||||
availableSystemObjectMetadataItemsForView,
|
||||
};
|
||||
};
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
import { SidePanelNavigationItemActions } from '@/side-panel/pages/navigation-menu-item/side-panel-navigation-item-actions';
|
||||
|
||||
const ORGANIZE_ACTIONS_WITHOUT_MOVE_TO_FOLDER: SidePanelNavigationItemActions[] =
|
||||
[
|
||||
SidePanelNavigationItemActions.MOVE_UP,
|
||||
SidePanelNavigationItemActions.MOVE_DOWN,
|
||||
SidePanelNavigationItemActions.ADD_BEFORE,
|
||||
SidePanelNavigationItemActions.ADD_AFTER,
|
||||
SidePanelNavigationItemActions.REMOVE,
|
||||
];
|
||||
|
||||
const ORGANIZE_ACTIONS_WITH_MOVE_TO_FOLDER: SidePanelNavigationItemActions[] = [
|
||||
SidePanelNavigationItemActions.MOVE_UP,
|
||||
SidePanelNavigationItemActions.MOVE_DOWN,
|
||||
SidePanelNavigationItemActions.MOVE_TO_FOLDER,
|
||||
SidePanelNavigationItemActions.ADD_BEFORE,
|
||||
SidePanelNavigationItemActions.ADD_AFTER,
|
||||
SidePanelNavigationItemActions.REMOVE,
|
||||
];
|
||||
|
||||
export const getOrganizeActionsSelectableItemIds = (
|
||||
includeMoveToFolder: boolean,
|
||||
): SidePanelNavigationItemActions[] =>
|
||||
includeMoveToFolder
|
||||
? ORGANIZE_ACTIONS_WITH_MOVE_TO_FOLDER
|
||||
: ORGANIZE_ACTIONS_WITHOUT_MOVE_TO_FOLDER;
|
||||
Reference in New Issue
Block a user