diff --git a/packages/twenty-front/src/modules/ai/components/AIChatThreadGroup.tsx b/packages/twenty-front/src/modules/ai/components/AIChatThreadGroup.tsx
index 5854aa5494..83a5549f63 100644
--- a/packages/twenty-front/src/modules/ai/components/AIChatThreadGroup.tsx
+++ b/packages/twenty-front/src/modules/ai/components/AIChatThreadGroup.tsx
@@ -1,13 +1,7 @@
-import { agentChatUsageState } from '@/ai/states/agentChatUsageState';
-import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState';
-import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
-import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
-import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
-import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
+import { useAIChatThreadClick } from '@/ai/hooks/useAIChatThreadClick';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
-import { isDefined } from 'twenty-shared/utils';
import { IconSparkles } from 'twenty-ui/display';
import { type AgentChatThread } from '~/generated-metadata/graphql';
@@ -24,7 +18,7 @@ const StyledDateGroup = styled.div`
const StyledDateHeader = styled.div`
color: ${({ theme }) => theme.font.color.light};
font-size: ${({ theme }) => theme.font.size.xs};
- font-weight: 600;
+ font-weight: ${({ theme }) => theme.font.weight.medium};
margin-bottom: ${({ theme }) => theme.spacing(1)};
`;
@@ -79,39 +73,7 @@ export const AIChatThreadGroup = ({
}) => {
const { t } = useLingui();
const theme = useTheme();
- const [, setCurrentAIChatThread] = useAtomState(currentAIChatThreadState);
- const setCurrentAIChatThreadTitle = useSetAtomState(
- currentAIChatThreadTitleState,
- );
- const setAgentChatUsage = useSetAtomState(agentChatUsageState);
- const { openAskAIPage } = useOpenAskAIPageInCommandMenu();
-
- const handleThreadClick = (thread: AgentChatThread) => {
- setCurrentAIChatThread(thread.id);
- setCurrentAIChatThreadTitle(thread.title ?? null);
-
- const hasUsageData =
- (thread.conversationSize ?? 0) > 0 &&
- isDefined(thread.contextWindowTokens);
-
- setAgentChatUsage(
- hasUsageData
- ? {
- lastMessage: null,
- conversationSize: thread.conversationSize ?? 0,
- contextWindowTokens: thread.contextWindowTokens ?? 0,
- inputTokens: thread.totalInputTokens,
- outputTokens: thread.totalOutputTokens,
- inputCredits: thread.totalInputCredits,
- outputCredits: thread.totalOutputCredits,
- }
- : null,
- );
-
- openAskAIPage({
- resetNavigationStack: false,
- });
- };
+ const { handleThreadClick } = useAIChatThreadClick();
if (threads.length === 0) {
return null;
diff --git a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAIChatThreadDateSection.tsx b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAIChatThreadDateSection.tsx
new file mode 100644
index 0000000000..e2f22de51d
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAIChatThreadDateSection.tsx
@@ -0,0 +1,75 @@
+import styled from '@emotion/styled';
+import { useLingui } from '@lingui/react/macro';
+import { IconComment } from 'twenty-ui/display';
+
+import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
+import { type AgentChatThread } from '~/generated-metadata/graphql';
+import { beautifyPastDateRelativeToNowShort } from '~/utils/date-utils';
+
+const StyledDateSection = styled.section`
+ margin-bottom: ${({ theme }) => theme.spacing(4)};
+`;
+
+const StyledThreadList = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(0.5)};
+`;
+
+const StyledDateHeader = styled.div`
+ color: ${({ theme }) => theme.font.color.light};
+ font-size: ${({ theme }) => theme.font.size.xs};
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+ margin-bottom: ${({ theme }) => theme.spacing(1)};
+ padding: ${({ theme }) => theme.spacing(0, 2)};
+`;
+
+const StyledThreadTimestamp = styled.span`
+ color: ${({ theme }) => theme.font.color.light};
+ font-size: ${({ theme }) => theme.font.size.xs};
+ font-weight: ${({ theme }) => theme.font.weight.regular};
+ padding-right: ${({ theme }) => theme.spacing(0.5)};
+`;
+
+export type NavigationDrawerAIChatThreadDateSectionProps = {
+ title: string;
+ threads: AgentChatThread[];
+ currentThreadId: string | null;
+ onThreadClick: (thread: AgentChatThread) => void;
+};
+
+export const NavigationDrawerAIChatThreadDateSection = ({
+ title,
+ threads,
+ currentThreadId,
+ onThreadClick,
+}: NavigationDrawerAIChatThreadDateSectionProps) => {
+ const { t } = useLingui();
+
+ return (
+
+ {title}
+
+ {threads.map((thread) => {
+ const isActive = currentThreadId === thread.id;
+ const timestamp = beautifyPastDateRelativeToNowShort(
+ thread.updatedAt ?? thread.createdAt,
+ );
+ return (
+ onThreadClick(thread)}
+ alwaysShowRightOptions
+ rightOptions={
+ {timestamp}
+ }
+ />
+ );
+ })}
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/ai/components/NavigationDrawerAIChatThreadsList.tsx b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAIChatThreadsList.tsx
new file mode 100644
index 0000000000..7ab9b59c27
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/components/NavigationDrawerAIChatThreadsList.tsx
@@ -0,0 +1,55 @@
+import styled from '@emotion/styled';
+
+import { NavigationDrawerAIChatThreadDateSection } from '@/ai/components/NavigationDrawerAIChatThreadDateSection';
+import { AIChatSkeletonLoader } from '@/ai/components/internal/AIChatSkeletonLoader';
+import { useAIChatThreadClick } from '@/ai/hooks/useAIChatThreadClick';
+import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState';
+import { groupThreadsByDate } from '@/ai/utils/groupThreadsByDate';
+import { type DateGroupKey } from '@/ai/utils/dateGroupKey';
+import { DATE_GROUP_KEYS } from '@/ai/utils/dateGroupKeys';
+import { getDateGroupTitle } from '@/ai/utils/getDateGroupTitle';
+import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
+import { useGetChatThreadsQuery } from '~/generated-metadata/graphql';
+
+const StyledScrollableList = styled.div`
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+ overflow-y: auto;
+ padding: ${({ theme }) => theme.spacing(2, 0)};
+ width: ${({ theme }) => `calc(100% - ${theme.spacing(2)})`};
+`;
+
+export const NavigationDrawerAIChatThreadsList = () => {
+ const currentAIChatThread = useAtomStateValue(currentAIChatThreadState);
+ const { handleThreadClick } = useAIChatThreadClick({
+ resetNavigationStack: true,
+ });
+
+ const { data: { chatThreads = [] } = {}, loading } = useGetChatThreadsQuery();
+ const groupedThreads = groupThreadsByDate(chatThreads);
+
+ if (loading === true) {
+ return ;
+ }
+
+ return (
+
+ {DATE_GROUP_KEYS.map((key: DateGroupKey) => {
+ const threads = groupedThreads[key];
+ if (threads.length === 0) return null;
+
+ return (
+
+ );
+ })}
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/ai/hooks/useAIChatThreadClick.ts b/packages/twenty-front/src/modules/ai/hooks/useAIChatThreadClick.ts
new file mode 100644
index 0000000000..2bf1814923
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/hooks/useAIChatThreadClick.ts
@@ -0,0 +1,53 @@
+import { agentChatUsageState } from '@/ai/states/agentChatUsageState';
+import { currentAIChatThreadState } from '@/ai/states/currentAIChatThreadState';
+import { currentAIChatThreadTitleState } from '@/ai/states/currentAIChatThreadTitleState';
+import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
+import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
+import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
+import { isDefined } from 'twenty-shared/utils';
+import { type AgentChatThread } from '~/generated-metadata/graphql';
+
+export type UseAIChatThreadClickOptions = {
+ resetNavigationStack?: boolean;
+};
+
+export const useAIChatThreadClick = (
+ options: UseAIChatThreadClickOptions = {},
+) => {
+ const { resetNavigationStack = false } = options;
+ const [, setCurrentAIChatThread] = useAtomState(currentAIChatThreadState);
+ const setCurrentAIChatThreadTitle = useSetAtomState(
+ currentAIChatThreadTitleState,
+ );
+ const setAgentChatUsage = useSetAtomState(agentChatUsageState);
+ const { openAskAIPage } = useOpenAskAIPageInCommandMenu();
+
+ const handleThreadClick = (thread: AgentChatThread) => {
+ setCurrentAIChatThread(thread.id);
+ setCurrentAIChatThreadTitle(thread.title ?? null);
+
+ const hasUsageData =
+ (thread.conversationSize ?? 0) > 0 &&
+ isDefined(thread.contextWindowTokens);
+
+ setAgentChatUsage(
+ hasUsageData
+ ? {
+ lastMessage: null,
+ conversationSize: thread.conversationSize ?? 0,
+ contextWindowTokens: thread.contextWindowTokens ?? 0,
+ inputTokens: thread.totalInputTokens,
+ outputTokens: thread.totalOutputTokens,
+ inputCredits: thread.totalInputCredits,
+ outputCredits: thread.totalOutputCredits,
+ }
+ : null,
+ );
+
+ openAskAIPage({
+ resetNavigationStack,
+ });
+ };
+
+ return { handleThreadClick };
+};
diff --git a/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts b/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts
index c845f9bae1..f56546dff3 100644
--- a/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts
+++ b/packages/twenty-front/src/modules/ai/utils/__tests__/groupThreadsByDate.test.ts
@@ -2,9 +2,15 @@ import { type AgentChatThread } from '~/generated-metadata/graphql';
import { groupThreadsByDate } from '@/ai/utils/groupThreadsByDate';
describe('groupThreadsByDate', () => {
- const baseThread: Omit = {
+ const today = new Date();
+ const yesterday = new Date(today);
+ yesterday.setDate(today.getDate() - 1);
+ const twoDaysAgo = new Date(today);
+ twoDaysAgo.setDate(today.getDate() - 2);
+
+ const baseThread: Omit = {
title: 'Test Thread',
- updatedAt: new Date().toISOString(),
+ createdAt: twoDaysAgo.toISOString(),
totalInputTokens: 0,
totalOutputTokens: 0,
contextWindowTokens: null,
@@ -13,16 +19,10 @@ describe('groupThreadsByDate', () => {
totalOutputCredits: 0,
};
- const today = new Date();
- const yesterday = new Date(today);
- yesterday.setDate(today.getDate() - 1);
- const twoDaysAgo = new Date(today);
- twoDaysAgo.setDate(today.getDate() - 2);
-
const threads: AgentChatThread[] = [
- { ...baseThread, id: '1', createdAt: today.toISOString() },
- { ...baseThread, id: '2', createdAt: yesterday.toISOString() },
- { ...baseThread, id: '3', createdAt: twoDaysAgo.toISOString() },
+ { ...baseThread, id: '1', updatedAt: today.toISOString() },
+ { ...baseThread, id: '2', updatedAt: yesterday.toISOString() },
+ { ...baseThread, id: '3', updatedAt: twoDaysAgo.toISOString() },
];
it('groups threads into today, yesterday, and older', () => {
diff --git a/packages/twenty-front/src/modules/ai/utils/dateGroupKey.ts b/packages/twenty-front/src/modules/ai/utils/dateGroupKey.ts
new file mode 100644
index 0000000000..c12765471e
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/utils/dateGroupKey.ts
@@ -0,0 +1 @@
+export type DateGroupKey = 'today' | 'yesterday' | 'older';
diff --git a/packages/twenty-front/src/modules/ai/utils/dateGroupKeys.ts b/packages/twenty-front/src/modules/ai/utils/dateGroupKeys.ts
new file mode 100644
index 0000000000..ede6d99754
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/utils/dateGroupKeys.ts
@@ -0,0 +1,7 @@
+import { type DateGroupKey } from '@/ai/utils/dateGroupKey';
+
+export const DATE_GROUP_KEYS: readonly DateGroupKey[] = [
+ 'today',
+ 'yesterday',
+ 'older',
+];
diff --git a/packages/twenty-front/src/modules/ai/utils/getDateGroupTitle.ts b/packages/twenty-front/src/modules/ai/utils/getDateGroupTitle.ts
new file mode 100644
index 0000000000..58ac9a796a
--- /dev/null
+++ b/packages/twenty-front/src/modules/ai/utils/getDateGroupTitle.ts
@@ -0,0 +1,14 @@
+import { t } from '@lingui/core/macro';
+
+import { type DateGroupKey } from '@/ai/utils/dateGroupKey';
+
+export const getDateGroupTitle = (key: DateGroupKey): string => {
+ switch (key) {
+ case 'today':
+ return t`Today`;
+ case 'yesterday':
+ return t`Yesterday`;
+ case 'older':
+ return t`Older`;
+ }
+};
diff --git a/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts b/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts
index 2cacf2fa66..e1ffd59ff8 100644
--- a/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts
+++ b/packages/twenty-front/src/modules/ai/utils/groupThreadsByDate.ts
@@ -1,17 +1,17 @@
import { type AgentChatThread } from '~/generated-metadata/graphql';
-export const groupThreadsByDate = (threads: AgentChatThread[]) => {
+import { type DateGroupKey } from '@/ai/utils/dateGroupKey';
+
+export const groupThreadsByDate = (
+ threads: AgentChatThread[],
+): Record => {
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
- return threads.reduce<{
- today: AgentChatThread[];
- yesterday: AgentChatThread[];
- older: AgentChatThread[];
- }>(
+ return threads.reduce>(
(acc, thread) => {
- const threadDate = new Date(thread.createdAt);
+ const threadDate = new Date(thread.updatedAt);
const threadDateString = threadDate.toDateString();
if (threadDateString === today.toDateString()) {
diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx
index 94400b5ece..50421f05c4 100644
--- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx
+++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuOpenContainer.tsx
@@ -8,6 +8,7 @@ import { RECORD_CHIP_CLICK_OUTSIDE_ID } from '@/object-record/record-table/const
import { MENTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/ui/input/constants/MentionMenuDropdownClickOutsideId';
import { SLASH_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/ui/input/constants/SlashMenuDropdownClickOutsideId';
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
+import { NAVIGATION_DRAWER_CLICK_OUTSIDE_ID } from '@/ui/navigation/navigation-drawer/constants/NavigationDrawerClickOutsideId';
import { PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID } from '@/ui/layout/page-header/constants/PageHeaderCommandMenuButtonClickOutsideId';
import { currentFocusIdSelector } from '@/ui/utilities/focus/states/currentFocusIdSelector';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
@@ -76,6 +77,7 @@ export const CommandMenuOpenContainer = ({
callback: handleClickOutside,
listenerId: 'COMMAND_MENU_LISTENER_ID',
excludedClickOutsideIds: [
+ NAVIGATION_DRAWER_CLICK_OUTSIDE_ID,
PAGE_HEADER_COMMAND_MENU_BUTTON_CLICK_OUTSIDE_ID,
LINK_CHIP_CLICK_OUTSIDE_ID,
RECORD_CHIP_CLICK_OUTSIDE_ID,
diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx
index 26e242640a..dc5989efb4 100644
--- a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx
+++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx
@@ -1,51 +1,19 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
-import styled from '@emotion/styled';
-import { useFavoritesByFolder } from '@/favorites/hooks/useFavoritesByFolder';
-import { NavigationMenuItemFolderContentDispatcherEffect } from '@/navigation-menu-item/components/NavigationMenuItemFolderContentDispatcher';
-import { useNavigationMenuItemsByFolder } from '@/navigation-menu-item/hooks/useNavigationMenuItemsByFolder';
-import { MainNavigationDrawerFixedItems } from '@/navigation/components/MainNavigationDrawerFixedItems';
-import { MainNavigationDrawerScrollableItems } from '@/navigation/components/MainNavigationDrawerScrollableItems';
+import { MainNavigationDrawerAIChatContent } from '@/navigation/components/MainNavigationDrawerAIChatContent';
+import { MainNavigationDrawerNavigationContent } from '@/navigation/components/MainNavigationDrawerNavigationContent';
+import { MainNavigationDrawerTabsRow } from '@/navigation/components/MainNavigationDrawerTabsRow';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent';
import { NavigationDrawerScrollableContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerScrollableContent';
-import { currentFavoriteFolderIdState } from '@/ui/navigation/navigation-drawer/states/currentFavoriteFolderIdState';
-import { currentNavigationMenuItemFolderIdState } from '@/ui/navigation/navigation-drawer/states/currentNavigationMenuItemFolderIdState';
+import { navigationDrawerActiveTabState } from '@/ui/navigation/states/navigationDrawerActiveTabState';
+import { NAVIGATION_DRAWER_TABS } from '@/ui/navigation/states/navigationDrawerTabs';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
-import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
-import { FeatureFlagKey } from '~/generated-metadata/graphql';
-
-const StyledScrollableContent = styled.div`
- height: 100%;
- min-height: 0;
-`;
export const MainNavigationDrawer = ({ className }: { className?: string }) => {
+ const navigationDrawerActiveTab = useAtomStateValue(
+ navigationDrawerActiveTabState,
+ );
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
- const currentFavoriteFolderId = useAtomStateValue(
- currentFavoriteFolderIdState,
- );
- const currentNavigationMenuItemFolderId = useAtomStateValue(
- currentNavigationMenuItemFolderIdState,
- );
- const { favoritesByFolder } = useFavoritesByFolder();
- const { navigationMenuItemsByFolder } = useNavigationMenuItemsByFolder();
- const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
- FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
- );
-
- const openedFavoriteFolder = favoritesByFolder.find(
- (f) => f.folderId === currentFavoriteFolderId,
- );
-
- const openedNavigationMenuItemFolder = navigationMenuItemsByFolder.find(
- (f) => f.id === currentNavigationMenuItemFolderId,
- );
-
- const openedFolder = isNavigationMenuItemEditingEnabled
- ? openedNavigationMenuItemFolder
- : openedFavoriteFolder;
-
- const openedFolderId = openedNavigationMenuItemFolder?.id ?? '';
return (
{
title={currentWorkspace?.displayName ?? ''}
>
-
+
- {isNavigationMenuItemEditingEnabled ? (
-
- {openedFolder ? (
-
- ) : (
-
- )}
-
- ) : openedFolder ? (
-
+ {navigationDrawerActiveTab ===
+ NAVIGATION_DRAWER_TABS.AI_CHAT_HISTORY ? (
+
) : (
-
+
)}
diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerAIChatContent.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerAIChatContent.tsx
new file mode 100644
index 0000000000..313a9bea46
--- /dev/null
+++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerAIChatContent.tsx
@@ -0,0 +1,18 @@
+import styled from '@emotion/styled';
+
+import { NavigationDrawerAIChatThreadsList } from '@/ai/components/NavigationDrawerAIChatThreadsList';
+
+const StyledAIChatThreadsListWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ min-height: 0;
+`;
+
+export const MainNavigationDrawerAIChatContent = () => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerFixedItems.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerFixedItems.tsx
deleted file mode 100644
index 2502f1d0fb..0000000000
--- a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerFixedItems.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu';
-import { useOpenRecordsSearchPageInCommandMenu } from '@/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu';
-import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
-import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
-import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
-import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
-import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
-import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
-import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
-import { useLingui } from '@lingui/react/macro';
-import { useLocation, useNavigate } from 'react-router-dom';
-import { SettingsPath } from 'twenty-shared/types';
-import { getSettingsPath } from 'twenty-shared/utils';
-import { IconSearch, IconSettings, IconSparkles } from 'twenty-ui/display';
-import { useIsMobile } from 'twenty-ui/utilities';
-import { FeatureFlagKey } from '~/generated-metadata/graphql';
-
-export const MainNavigationDrawerFixedItems = () => {
- const isMobile = useIsMobile();
- const location = useLocation();
- const setNavigationMemorizedUrl = useSetAtomState(
- navigationMemorizedUrlState,
- );
-
- const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
- useAtomState(isNavigationDrawerExpandedState);
- const setNavigationDrawerExpandedMemorized = useSetAtomState(
- navigationDrawerExpandedMemorizedState,
- );
-
- const navigate = useNavigate();
-
- const { t } = useLingui();
-
- const { openRecordsSearchPage } = useOpenRecordsSearchPageInCommandMenu();
- const { openAskAIPage } = useOpenAskAIPageInCommandMenu();
- const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
-
- return (
- !isMobile && (
- <>
-
- {isAiEnabled && (
- openAskAIPage({ resetNavigationStack: true })}
- keyboard={['@']}
- mouseUpNavigation={true}
- />
- )}
- {
- setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
- setIsNavigationDrawerExpanded(true);
- setNavigationMemorizedUrl(location.pathname + location.search);
- navigate(getSettingsPath(SettingsPath.ProfilePage));
- }}
- Icon={IconSettings}
- />
- >
- )
- );
-};
diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerNavigationContent.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerNavigationContent.tsx
new file mode 100644
index 0000000000..f284731abf
--- /dev/null
+++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerNavigationContent.tsx
@@ -0,0 +1,74 @@
+import styled from '@emotion/styled';
+
+import { useFavoritesByFolder } from '@/favorites/hooks/useFavoritesByFolder';
+import { NavigationMenuItemFolderContentDispatcherEffect } from '@/navigation-menu-item/components/NavigationMenuItemFolderContentDispatcher';
+import { useNavigationMenuItemsByFolder } from '@/navigation-menu-item/hooks/useNavigationMenuItemsByFolder';
+import { MainNavigationDrawerScrollableItems } from '@/navigation/components/MainNavigationDrawerScrollableItems';
+import { currentFavoriteFolderIdState } from '@/ui/navigation/navigation-drawer/states/currentFavoriteFolderIdState';
+import { currentNavigationMenuItemFolderIdState } from '@/ui/navigation/navigation-drawer/states/currentNavigationMenuItemFolderIdState';
+import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
+import { FeatureFlagKey } from '~/generated-metadata/graphql';
+
+const StyledScrollableContent = styled.div`
+ height: 100%;
+ min-height: 0;
+`;
+
+export const MainNavigationDrawerNavigationContent = () => {
+ const currentFavoriteFolderId = useAtomStateValue(
+ currentFavoriteFolderIdState,
+ );
+ const currentNavigationMenuItemFolderId = useAtomStateValue(
+ currentNavigationMenuItemFolderIdState,
+ );
+ const { favoritesByFolder } = useFavoritesByFolder();
+ const { navigationMenuItemsByFolder } = useNavigationMenuItemsByFolder();
+ const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
+ FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
+ );
+
+ const openedFavoriteFolder = favoritesByFolder.find(
+ (folder) => folder.folderId === currentFavoriteFolderId,
+ );
+
+ const openedNavigationMenuItemFolder = navigationMenuItemsByFolder.find(
+ (folder) => folder.id === currentNavigationMenuItemFolderId,
+ );
+
+ const openedFolder = isNavigationMenuItemEditingEnabled
+ ? openedNavigationMenuItemFolder
+ : openedFavoriteFolder;
+
+ const openedFolderId = openedNavigationMenuItemFolder?.id ?? '';
+
+ if (isNavigationMenuItemEditingEnabled) {
+ return (
+
+ {openedFolder ? (
+
+ ) : (
+
+ )}
+
+ );
+ }
+
+ return openedFolder ? (
+
+ ) : (
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerScrollableItems.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerScrollableItems.tsx
index 98084c3794..c4b5bb7f92 100644
--- a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerScrollableItems.tsx
+++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerScrollableItems.tsx
@@ -3,6 +3,8 @@ import { RemoteNavigationDrawerSection } from '@/object-metadata/components/Remo
import styled from '@emotion/styled';
import { lazy, Suspense } from 'react';
+import { NavigationDrawerOtherSection } from '@/navigation/components/NavigationDrawerOtherSection';
+
const CurrentWorkspaceMemberNavigationMenuItemFoldersDispatcher = lazy(() =>
import(
'@/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItemFoldersDispatcher'
@@ -36,6 +38,7 @@ export const MainNavigationDrawerScrollableItems = () => {
+
);
};
diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerTabsRow.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerTabsRow.tsx
new file mode 100644
index 0000000000..16c63477cb
--- /dev/null
+++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawerTabsRow.tsx
@@ -0,0 +1,247 @@
+import { useTheme } from '@emotion/react';
+import styled from '@emotion/styled';
+import { t } from '@lingui/core/macro';
+import {
+ IconComment,
+ IconHome,
+ IconMessageCirclePlus,
+} from 'twenty-ui/display';
+import { useIsMobile } from 'twenty-ui/utilities';
+
+import { useCreateNewAIChatThread } from '@/ai/hooks/useCreateNewAIChatThread';
+import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
+import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
+import { navigationDrawerActiveTabState } from '@/ui/navigation/states/navigationDrawerActiveTabState';
+import {
+ type NavigationDrawerActiveTab,
+ NAVIGATION_DRAWER_TABS,
+} from '@/ui/navigation/states/navigationDrawerTabs';
+import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
+import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
+import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
+import { FeatureFlagKey } from '~/generated-metadata/graphql';
+
+const StyledRow = styled.div<{ isExpanded: boolean }>`
+ align-items: center;
+ display: flex;
+ justify-content: ${({ isExpanded }) =>
+ isExpanded ? 'space-between' : 'center'};
+ gap: ${({ theme, isExpanded }) => (isExpanded ? theme.spacing(2) : 0)};
+ width: 100%;
+ transition: gap ${({ theme }) => theme.animation.duration.normal}s ease;
+`;
+
+const StyledTabsPill = styled.div`
+ align-items: center;
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
+ background: ${({ theme }) => theme.background.secondary};
+ border-radius: ${({ theme }) => theme.border.radius.pill};
+ padding: ${({ theme }) => theme.spacing(0.75)};
+ height: ${({ theme }) => theme.spacing(7)};
+ display: flex;
+ width: ${({ theme }) => theme.spacing(18)};
+ gap: ${({ theme }) => theme.spacing(0.5)};
+ box-sizing: border-box;
+`;
+
+const StyledTabWrapper = styled.div<{ isActive: boolean }>`
+ border-radius: ${({ theme }) => theme.border.radius.pill};
+ align-items: center;
+ background: ${({ theme, isActive }) =>
+ isActive ? theme.background.transparent.light : 'transparent'};
+ color: ${({ theme, isActive }) =>
+ isActive ? theme.font.color.primary : theme.font.color.tertiary};
+ cursor: pointer;
+ display: flex;
+ justify-content: center;
+ height: 100%;
+ flex: 1;
+
+ &:hover {
+ background: ${({ theme, isActive }) =>
+ isActive
+ ? theme.background.transparent.light
+ : theme.background.transparent.lighter};
+ }
+`;
+
+const StyledTabIcon = styled.div`
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ height: ${({ theme }) => theme.spacing(5)};
+ width: ${({ theme }) => theme.spacing(5)};
+`;
+
+const StyledNewChatButtonWrapper = styled.div<{ isExpanded: boolean }>`
+ align-items: center;
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
+ background: ${({ theme }) => theme.background.secondary};
+ border-radius: ${({ theme }) => theme.border.radius.pill};
+ box-sizing: border-box;
+ display: flex;
+ height: ${({ theme, isExpanded }) =>
+ isExpanded ? theme.spacing(7) : theme.spacing(6)};
+ justify-content: center;
+ padding: ${({ theme, isExpanded }) =>
+ isExpanded ? theme.spacing(0.75) : theme.spacing(0.5)};
+ width: ${({ theme, isExpanded }) =>
+ isExpanded ? theme.spacing(25.75) : theme.spacing(6)};
+ transition:
+ height ${({ theme }) => theme.animation.duration.normal}s ease,
+ padding ${({ theme }) => theme.animation.duration.normal}s ease;
+`;
+
+const StyledNewChatButton = styled.div`
+ align-items: center;
+ justify-content: center;
+ display: flex;
+ cursor: pointer;
+ font-size: ${({ theme }) => theme.font.size.sm};
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+ gap: ${({ theme }) => theme.spacing(1)};
+ height: 100%;
+ width: 100%;
+ border-radius: inherit;
+ color: ${({ theme }) => theme.font.color.secondary};
+ transition:
+ background ${({ theme }) => theme.animation.duration.fast}s ease,
+ color ${({ theme }) => theme.animation.duration.fast}s ease;
+
+ &:hover {
+ background: ${({ theme }) => theme.background.transparent.light};
+ color: ${({ theme }) => theme.font.color.primary};
+ }
+`;
+
+export const MainNavigationDrawerTabsRow = () => {
+ const theme = useTheme();
+ const isMobile = useIsMobile();
+ const isNavigationDrawerExpanded = useAtomStateValue(
+ isNavigationDrawerExpandedState,
+ );
+ const [navigationDrawerActiveTab, setNavigationDrawerActiveTab] =
+ useAtomState(navigationDrawerActiveTabState);
+ const { createChatThread } = useCreateNewAIChatThread();
+ const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
+ const setIsNavigationDrawerExpanded = useSetAtomState(
+ isNavigationDrawerExpandedState,
+ );
+
+ if (!isAiEnabled) {
+ return null;
+ }
+
+ const isExpanded = isNavigationDrawerExpanded || isMobile;
+
+ const handleTabClick = (tab: NavigationDrawerActiveTab) => () => {
+ setNavigationDrawerActiveTab(tab);
+ };
+
+ const handleTabKeyDown =
+ (tab: NavigationDrawerActiveTab) => (event: React.KeyboardEvent) => {
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault();
+ setNavigationDrawerActiveTab(tab);
+ }
+ };
+
+ const handleNewChatClick = () => {
+ if (isMobile) {
+ setIsNavigationDrawerExpanded(false);
+ }
+ createChatThread();
+ };
+
+ const handleNewChatKeyDown = (event: React.KeyboardEvent) => {
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault();
+ handleNewChatClick();
+ }
+ };
+
+ const getTabIconColor = (isActive: boolean) =>
+ isActive ? theme.font.color.primary : theme.font.color.tertiary;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {isExpanded && t`New chat`}
+
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx b/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx
index 110db7087e..8c9ccc6699 100644
--- a/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx
+++ b/packages/twenty-front/src/modules/navigation/components/MobileNavigationBar.tsx
@@ -1,27 +1,29 @@
+import { useCreateNewAIChatThread } from '@/ai/hooks/useCreateNewAIChatThread';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { useOpenRecordsSearchPageInCommandMenu } from '@/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath';
-import { useOpenSettingsMenu } from '@/navigation/hooks/useOpenSettings';
+import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage';
+import { currentMobileNavigationDrawerState } from '@/navigation/states/currentMobileNavigationDrawerState';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
+import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
-import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
+import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useNavigate } from 'react-router-dom';
import {
type IconComponent,
IconList,
+ IconMessageCirclePlus,
IconSearch,
- IconSettings,
} from 'twenty-ui/display';
import { NavigationBar } from 'twenty-ui/navigation';
-import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage';
-import { currentMobileNavigationDrawerState } from '@/navigation/states/currentMobileNavigationDrawerState';
+import { FeatureFlagKey } from '~/generated-metadata/graphql';
-type NavigationBarItemName = 'main' | 'search' | 'tasks' | 'settings';
+type NavigationBarItemName = 'main' | 'search' | 'newAIChat';
export const MobileNavigationBar = () => {
const navigate = useNavigate();
@@ -34,7 +36,8 @@ export const MobileNavigationBar = () => {
useAtomState(isNavigationDrawerExpandedState);
const [currentMobileNavigationDrawer, setCurrentMobileNavigationDrawer] =
useAtomState(currentMobileNavigationDrawerState);
- const { openSettingsMenu } = useOpenSettingsMenu();
+ const { createChatThread } = useCreateNewAIChatThread();
+ const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
const { alphaSortedActiveNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
@@ -47,9 +50,7 @@ export const MobileNavigationBar = () => {
? currentMobileNavigationDrawer
: isCommandMenuOpened
? 'search'
- : isSettingsPage
- ? 'settings'
- : 'main';
+ : 'main';
const items: {
name: NavigationBarItemName;
@@ -91,14 +92,19 @@ export const MobileNavigationBar = () => {
openRecordsSearchPage();
},
},
- {
- name: 'settings',
- Icon: IconSettings,
- onClick: () => {
- closeCommandMenu();
- openSettingsMenu();
- },
- },
+ ...(isAiEnabled
+ ? [
+ {
+ name: 'newAIChat' as const,
+ Icon: IconMessageCirclePlus,
+ onClick: () => {
+ setIsNavigationDrawerExpanded(false);
+ closeCommandMenu();
+ createChatThread();
+ },
+ },
+ ]
+ : []),
];
return ;
diff --git a/packages/twenty-front/src/modules/navigation/components/NavigationDrawerOtherSection.tsx b/packages/twenty-front/src/modules/navigation/components/NavigationDrawerOtherSection.tsx
new file mode 100644
index 0000000000..a28626b668
--- /dev/null
+++ b/packages/twenty-front/src/modules/navigation/components/NavigationDrawerOtherSection.tsx
@@ -0,0 +1,77 @@
+import { useLingui } from '@lingui/react/macro';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { SettingsPath } from 'twenty-shared/types';
+import { getSettingsPath } from 'twenty-shared/utils';
+import { IconHelpCircle, IconSettings } from 'twenty-ui/display';
+
+import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
+import { getDocumentationUrl } from '@/support/utils/getDocumentationUrl';
+import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
+import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
+import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
+import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
+import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
+import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
+
+import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
+import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
+import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
+import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
+import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
+import { isNavigationSectionOpenFamilyState } from '@/ui/navigation/navigation-drawer/states/isNavigationSectionOpenFamilyState';
+import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
+
+export const NavigationDrawerOtherSection = () => {
+ const { t } = useLingui();
+ const location = useLocation();
+ const navigate = useNavigate();
+ const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
+ const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
+ useAtomState(isNavigationDrawerExpandedState);
+ const setNavigationDrawerExpandedMemorized = useSetAtomState(
+ navigationDrawerExpandedMemorizedState,
+ );
+ const setNavigationMemorizedUrl = useSetAtomState(
+ navigationMemorizedUrlState,
+ );
+
+ const { toggleNavigationSection } = useNavigationSection('Other');
+ const isNavigationSectionOpen = useAtomFamilyStateValue(
+ isNavigationSectionOpenFamilyState,
+ 'Other',
+ );
+
+ const handleSettingsClick = () => {
+ setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
+ setIsNavigationDrawerExpanded(true);
+ setNavigationMemorizedUrl(location.pathname + location.search);
+ navigate(getSettingsPath(SettingsPath.ProfilePage));
+ };
+
+ return (
+
+
+
+
+ {isNavigationSectionOpen && (
+ <>
+
+
+ >
+ )}
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx
index e287ca5610..1db4f45d4c 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspaceDropdownDefaultComponents.tsx
@@ -2,13 +2,11 @@ import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/consta
import { useAuth } from '@/auth/hooks/useAuth';
import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState';
-import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { countAvailableWorkspaces } from '@/auth/utils/availableWorkspacesUtils';
+import { supportChatState } from '@/client-config/states/supportChatState';
import { useBuildWorkspaceUrl } from '@/domain-manager/hooks/useBuildWorkspaceUrl';
import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain';
-import { supportChatState } from '@/client-config/states/supportChatState';
-import { getDocumentationUrl } from '@/support/utils/getDocumentationUrl';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
@@ -19,22 +17,27 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MultiWorkspaceDropdownId';
import { multiWorkspaceDropdownState } from '@/ui/navigation/navigation-drawer/states/multiWorkspaceDropdownState';
+import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
+import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
+import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useColorScheme } from '@/ui/theme/hooks/useColorScheme';
+import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
+import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { type ApolloError } from '@apollo/client';
import styled from '@emotion/styled';
-import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
-import { isNonEmptyString } from '@sniptt/guards';
import { useLingui } from '@lingui/react/macro';
+import { isNonEmptyString } from '@sniptt/guards';
+import { useLocation } from 'react-router-dom';
import { AppPath, SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import {
Avatar,
IconDotsVertical,
- IconHelpCircle,
IconLogout,
IconMessage,
IconPlus,
+ IconSettings,
IconSwitchHorizontal,
IconUserPlus,
} from 'twenty-ui/display';
@@ -57,7 +60,6 @@ const StyledDescription = styled.div`
export const MultiWorkspaceDropdownDefaultComponents = () => {
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
- const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
const { t } = useLingui();
const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain();
const availableWorkspaces = useAtomStateValue(availableWorkspacesState);
@@ -79,19 +81,21 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
multiWorkspaceDropdownState,
);
+ const location = useLocation();
+ const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
+ useAtomState(isNavigationDrawerExpandedState);
+ const setNavigationDrawerExpandedMemorized = useSetAtomState(
+ navigationDrawerExpandedMemorizedState,
+ );
+ const setNavigationMemorizedUrl = useSetAtomState(
+ navigationMemorizedUrlState,
+ );
+
const handleSupport = () => {
window.FrontChat?.('show');
closeDropdown(MULTI_WORKSPACE_DROPDOWN_ID);
};
- const handleDocumentation = () => {
- window.open(
- getDocumentationUrl({ locale: currentWorkspaceMember?.locale }),
- '_blank',
- );
- closeDropdown(MULTI_WORKSPACE_DROPDOWN_ID);
- };
-
const handleChange = async (availableWorkspace: AvailableWorkspace) => {
redirectToWorkspaceDomain(
getWorkspaceUrl(availableWorkspace.workspaceUrls),
@@ -149,6 +153,11 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
text={t`Create Workspace`}
onClick={createWorkspace}
/>
+
}
@@ -223,6 +232,17 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
>
+ {
+ setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
+ setIsNavigationDrawerExpanded(true);
+ setNavigationMemorizedUrl(location.pathname + location.search);
+ closeDropdown(MULTI_WORKSPACE_DROPDOWN_ID);
+ }}
+ >
+
+
{isSupportChatConfigured && (