diff --git a/front/src/modules/favorites/components/Favorites.tsx b/front/src/modules/favorites/components/Favorites.tsx index 1b5c1b32d..ad16361ef 100644 --- a/front/src/modules/favorites/components/Favorites.tsx +++ b/front/src/modules/favorites/components/Favorites.tsx @@ -2,8 +2,8 @@ import styled from '@emotion/styled'; import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList'; -import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; -import NavTitle from '@/ui/navigation/navigation-drawer/components/NavTitle'; +import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; +import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; import { Avatar } from '@/users/components/Avatar'; import { useFavorites } from '../hooks/useFavorites'; @@ -24,7 +24,7 @@ export const Favorites = () => { return ( - + { draggableId={id} index={index} itemComponent={ - ( diff --git a/front/src/modules/navigation/components/AppNavigationDrawer.tsx b/front/src/modules/navigation/components/AppNavigationDrawer.tsx new file mode 100644 index 000000000..0a8a50cdd --- /dev/null +++ b/front/src/modules/navigation/components/AppNavigationDrawer.tsx @@ -0,0 +1,74 @@ +import { useEffect } from 'react'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; + +import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems'; +import { SupportChat } from '@/support/components/SupportChat'; +import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink'; +import { + NavigationDrawer, + NavigationDrawerProps, +} from '@/ui/navigation/navigation-drawer/components/NavigationDrawer'; +import { isNavigationDrawerOpenState } from '@/ui/navigation/states/isNavigationDrawerOpenState'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; +import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI'; + +import { useIsSettingsPage } from '../hooks/useIsSettingsPage'; +import { currentMobileNavigationDrawerState } from '../states/currentMobileNavigationDrawerState'; + +import { MainNavigationDrawerItems } from './MainNavigationDrawerItems'; + +export type AppNavigationDrawerProps = { + className?: string; +}; + +export const AppNavigationDrawer = ({ + className, +}: AppNavigationDrawerProps) => { + const isMobile = useIsMobile(); + const isSettingsPage = useIsSettingsPage(); + const currentMobileNavigationDrawer = useRecoilValue( + currentMobileNavigationDrawerState, + ); + const setIsNavigationDrawerOpen = useSetRecoilState( + isNavigationDrawerOpenState, + ); + const currentWorkspace = useRecoilValue(currentWorkspaceState); + + const isSettingsDrawer = isMobile + ? currentMobileNavigationDrawer === 'settings' + : isSettingsPage; + + const drawerProps: NavigationDrawerProps = isSettingsDrawer + ? { + isSubMenu: true, + title: 'Settings', + children: , + footer: , + } + : { + logo: + (currentWorkspace?.logo && + getImageAbsoluteURIOrBase64(currentWorkspace.logo)) ?? + undefined, + title: currentWorkspace?.displayName ?? undefined, + children: , + footer: , + }; + + useEffect(() => { + setIsNavigationDrawerOpen(!isMobile); + }, [isMobile, setIsNavigationDrawerOpen]); + + return ( + + {drawerProps.children} + + ); +}; diff --git a/front/src/modules/navigation/components/DesktopNavigationDrawer.tsx b/front/src/modules/navigation/components/DesktopNavigationDrawer.tsx deleted file mode 100644 index 1d4339d56..000000000 --- a/front/src/modules/navigation/components/DesktopNavigationDrawer.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useLocation, useNavigate } from 'react-router-dom'; -import { useSetRecoilState } from 'recoil'; - -import { useCurrentUserTaskCount } from '@/activities/tasks/hooks/useCurrentUserDueTaskCount'; -import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; -import { Favorites } from '@/favorites/components/Favorites'; -import { useIsTasksPage } from '@/navigation/hooks/useIsTasksPage'; -import { SettingsNavbar } from '@/settings/components/SettingsNavbar'; -import { - IconBell, - IconCheckbox, - IconSearch, - IconSettings, -} from '@/ui/display/icon/index'; -import MainNavbar from '@/ui/navigation/navigation-drawer/components/MainNavbar'; -import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; -import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; - -import { useIsSettingsPage } from '../hooks/useIsSettingsPage'; - -import { WorkspaceNavItems } from './WorkspaceNavItems'; - -export const DesktopNavigationDrawer = () => { - const { toggleCommandMenu } = useCommandMenu(); - const isSettingsPage = useIsSettingsPage(); - const isTasksPage = useIsTasksPage(); - const { currentUserDueTaskCount } = useCurrentUserTaskCount(); - const navigate = useNavigate(); - const location = useLocation(); - const setNavigationMemorizedUrl = useSetRecoilState( - navigationMemorizedUrlState, - ); - - return isSettingsPage ? ( - - ) : ( - - - - { - setNavigationMemorizedUrl(location.pathname + location.search); - navigate('/settings/profile'); - }} - Icon={IconSettings} - /> - - - - - ); -}; diff --git a/front/src/modules/navigation/components/MainNavigationDrawerItems.tsx b/front/src/modules/navigation/components/MainNavigationDrawerItems.tsx new file mode 100644 index 000000000..df3688048 --- /dev/null +++ b/front/src/modules/navigation/components/MainNavigationDrawerItems.tsx @@ -0,0 +1,79 @@ +import { useLocation, useNavigate } from 'react-router-dom'; +import { useSetRecoilState } from 'recoil'; + +import { useCurrentUserTaskCount } from '@/activities/tasks/hooks/useCurrentUserDueTaskCount'; +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { Favorites } from '@/favorites/components/Favorites'; +import { ObjectMetadataNavItems } from '@/object-metadata/components/ObjectMetadataNavItems'; +import { + IconBell, + IconCheckbox, + IconSearch, + IconSettings, + IconTargetArrow, +} from '@/ui/display/icon'; +import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; +import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; +import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; + +import { useIsTasksPage } from '../hooks/useIsTasksPage'; + +export const MainNavigationDrawerItems = () => { + const isMobile = useIsMobile(); + const { toggleCommandMenu } = useCommandMenu(); + const isTasksPage = useIsTasksPage(); + const { currentUserDueTaskCount } = useCurrentUserTaskCount(); + const navigate = useNavigate(); + const location = useLocation(); + const setNavigationMemorizedUrl = useSetRecoilState( + navigationMemorizedUrlState, + ); + + return ( + <> + {!isMobile && ( + <> + + + { + setNavigationMemorizedUrl(location.pathname + location.search); + navigate('/settings/profile'); + }} + Icon={IconSettings} + /> + + + )} + + + + + + + + ); +}; diff --git a/front/src/modules/navigation/components/MobileNavigationBar.tsx b/front/src/modules/navigation/components/MobileNavigationBar.tsx index 0a34f66e4..f256a35fc 100644 --- a/front/src/modules/navigation/components/MobileNavigationBar.tsx +++ b/front/src/modules/navigation/components/MobileNavigationBar.tsx @@ -11,10 +11,11 @@ import { } from '@/ui/display/icon'; import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { NavigationBar } from '@/ui/navigation/navigation-bar/components/NavigationBar'; -import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState'; +import { isNavigationDrawerOpenState } from '@/ui/navigation/states/isNavigationDrawerOpenState'; import { useIsSettingsPage } from '../hooks/useIsSettingsPage'; import { useIsTasksPage } from '../hooks/useIsTasksPage'; +import { currentMobileNavigationDrawerState } from '../states/currentMobileNavigationDrawerState'; type NavigationBarItemName = 'main' | 'search' | 'tasks' | 'settings'; @@ -24,11 +25,15 @@ export const MobileNavigationBar = () => { const isTasksPage = useIsTasksPage(); const isSettingsPage = useIsSettingsPage(); const navigate = useNavigate(); - const [navigationDrawer, setNavigationDrawer] = useRecoilState( - navigationDrawerState, + const [isNavigationDrawerOpen, setIsNavigationDrawerOpen] = useRecoilState( + isNavigationDrawerOpenState, ); + const [currentMobileNavigationDrawer, setCurrentMobileNavigationDrawer] = + useRecoilState(currentMobileNavigationDrawerState); - const initialActiveItemName: NavigationBarItemName = isCommandMenuOpened + const activeItemName = isNavigationDrawerOpen + ? currentMobileNavigationDrawer + : isCommandMenuOpened ? 'search' : isTasksPage ? 'tasks' @@ -46,14 +51,17 @@ export const MobileNavigationBar = () => { Icon: IconList, onClick: () => { closeCommandMenu(); - setNavigationDrawer(navigationDrawer === 'main' ? '' : 'main'); + setIsNavigationDrawerOpen( + (previousIsOpen) => activeItemName !== 'main' || !previousIsOpen, + ); + setCurrentMobileNavigationDrawer('main'); }, }, { name: 'search', Icon: IconSearch, onClick: () => { - setNavigationDrawer(''); + setIsNavigationDrawerOpen(false); toggleCommandMenu(); }, }, @@ -62,7 +70,7 @@ export const MobileNavigationBar = () => { Icon: IconCheckbox, onClick: () => { closeCommandMenu(); - setNavigationDrawer(''); + setIsNavigationDrawerOpen(false); navigate('/tasks'); }, }, @@ -71,15 +79,13 @@ export const MobileNavigationBar = () => { Icon: IconSettings, onClick: () => { closeCommandMenu(); - setNavigationDrawer(navigationDrawer === 'settings' ? '' : 'settings'); + setIsNavigationDrawerOpen( + (previousIsOpen) => activeItemName !== 'settings' || !previousIsOpen, + ); + setCurrentMobileNavigationDrawer('settings'); }, }, ]; - return ( - - ); + return ; }; diff --git a/front/src/modules/navigation/components/MobileNavigationDrawer.tsx b/front/src/modules/navigation/components/MobileNavigationDrawer.tsx deleted file mode 100644 index eb3264237..000000000 --- a/front/src/modules/navigation/components/MobileNavigationDrawer.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useRecoilState } from 'recoil'; - -import { Favorites } from '@/favorites/components/Favorites'; -import { SettingsNavbar } from '@/settings/components/SettingsNavbar'; -import MainNavbar from '@/ui/navigation/navigation-drawer/components/MainNavbar'; -import { navigationDrawerState } from '@/ui/navigation/states/navigationDrawerState'; - -import { WorkspaceNavItems } from './WorkspaceNavItems'; - -export const MobileNavigationDrawer = () => { - const [navigationDrawer] = useRecoilState(navigationDrawerState); - - return navigationDrawer === 'settings' ? ( - - ) : ( - - - - - ); -}; diff --git a/front/src/modules/navigation/components/WorkspaceNavItems.tsx b/front/src/modules/navigation/components/WorkspaceNavItems.tsx deleted file mode 100644 index 4e8913588..000000000 --- a/front/src/modules/navigation/components/WorkspaceNavItems.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useLocation } from 'react-router-dom'; - -import { ObjectMetadataNavItems } from '@/object-metadata/components/ObjectMetadataNavItems'; -import { IconTargetArrow } from '@/ui/display/icon/index'; -import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; -import NavTitle from '@/ui/navigation/navigation-drawer/components/NavTitle'; - -export const WorkspaceNavItems = () => { - const { pathname } = useLocation(); - - return ( - <> - - - - - ); -}; diff --git a/front/src/modules/navigation/components/__stories__/AppNavigationDrawer.stories.tsx b/front/src/modules/navigation/components/__stories__/AppNavigationDrawer.stories.tsx new file mode 100644 index 000000000..e267c7e24 --- /dev/null +++ b/front/src/modules/navigation/components/__stories__/AppNavigationDrawer.stories.tsx @@ -0,0 +1,77 @@ +import { useEffect } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { Meta, StoryObj } from '@storybook/react'; +import { useSetRecoilState } from 'recoil'; + +import { currentMobileNavigationDrawerState } from '@/navigation/states/currentMobileNavigationDrawerState'; +import { AppPath } from '@/types/AppPath'; +import { isNavigationDrawerOpenState } from '@/ui/navigation/states/isNavigationDrawerOpenState'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; +import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; + +import { + AppNavigationDrawer, + AppNavigationDrawerProps, +} from '../AppNavigationDrawer'; + +const MobileNavigationDrawerStateSetterEffect = ({ + mobileNavigationDrawer = 'main', +}: { + mobileNavigationDrawer?: 'main' | 'settings'; +}) => { + const isMobile = useIsMobile(); + const setIsNavigationDrawerOpen = useSetRecoilState( + isNavigationDrawerOpenState, + ); + const setCurrentMobileNavigationDrawer = useSetRecoilState( + currentMobileNavigationDrawerState, + ); + + useEffect(() => { + if (!isMobile) return; + + setIsNavigationDrawerOpen(true); + setCurrentMobileNavigationDrawer(mobileNavigationDrawer); + }, [ + isMobile, + mobileNavigationDrawer, + setCurrentMobileNavigationDrawer, + setIsNavigationDrawerOpen, + ]); + + return null; +}; + +type StoryArgs = AppNavigationDrawerProps & { + mobileNavigationDrawer?: 'main' | 'settings'; + routePath: string; +}; + +const meta: Meta = { + title: 'Modules/Navigation/AppNavigationDrawer', + decorators: [ + (Story, { args }) => ( + + + + + ), + SnackBarDecorator, + ], + component: AppNavigationDrawer, + args: { routePath: AppPath.Index }, +}; + +export default meta; +type Story = StoryObj; + +export const Main: Story = {}; + +export const Settings: Story = { + args: { + mobileNavigationDrawer: 'settings', + routePath: '/settings/appearance', + }, +}; diff --git a/front/src/modules/navigation/components/__stories__/DesktopNavigationDrawer.stories.tsx b/front/src/modules/navigation/components/__stories__/DesktopNavigationDrawer.stories.tsx deleted file mode 100644 index f56cea7a6..000000000 --- a/front/src/modules/navigation/components/__stories__/DesktopNavigationDrawer.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; - -import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; - -import { DesktopNavigationDrawer } from '../DesktopNavigationDrawer'; -import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; -import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; - -const meta: Meta = { - title: 'Modules/Navigation/DesktopNavigationDrawer', - component: DesktopNavigationDrawer, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - decorators: [ - ComponentDecorator, - ComponentWithRouterDecorator, - SnackBarDecorator, - ], -}; diff --git a/front/src/modules/navigation/components/__stories__/MobileNavigationDrawer.stories.tsx b/front/src/modules/navigation/components/__stories__/MobileNavigationDrawer.stories.tsx deleted file mode 100644 index dea61479b..000000000 --- a/front/src/modules/navigation/components/__stories__/MobileNavigationDrawer.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; - -import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; - -import { MobileNavigationDrawer } from '../MobileNavigationDrawer'; -import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; -import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; - -const meta: Meta = { - title: 'Modules/Navigation/MobileNavigationDrawer', - component: MobileNavigationDrawer, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - decorators: [ - ComponentDecorator, - ComponentWithRouterDecorator, - SnackBarDecorator, - ], -}; diff --git a/front/src/modules/navigation/states/currentMobileNavigationDrawerState.ts b/front/src/modules/navigation/states/currentMobileNavigationDrawerState.ts new file mode 100644 index 000000000..acd302d44 --- /dev/null +++ b/front/src/modules/navigation/states/currentMobileNavigationDrawerState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const currentMobileNavigationDrawerState = atom<'main' | 'settings'>({ + key: 'currentMobileNavigationDrawerState', + default: 'main', +}); diff --git a/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx b/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx index 99d3382e1..b6663117a 100644 --- a/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx +++ b/front/src/modules/object-metadata/components/ObjectMetadataNavItems.tsx @@ -3,7 +3,7 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings'; import { Icon123 } from '@/ui/input/constants/icons'; import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons'; -import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; +import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; export const ObjectMetadataNavItems = () => { const { activeObjectMetadataItems } = useObjectMetadataItemForSettings(); @@ -13,10 +13,9 @@ export const ObjectMetadataNavItems = () => { return ( <> - {activeObjectMetadataItems.map((objectMetadataItem) => { - if (objectMetadataItem.nameSingular === 'opportunity') return null; - return ( - + objectMetadataItem.nameSingular === 'opportunity' ? null : ( + { navigate(`/objects/${objectMetadataItem.namePlural}`); }} /> - ); - })} + ), + )} ); }; diff --git a/front/src/modules/settings/components/SettingsNavbar.tsx b/front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx similarity index 76% rename from front/src/modules/settings/components/SettingsNavbar.tsx rename to front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx index afb2aa1af..cc8d8c433 100644 --- a/front/src/modules/settings/components/SettingsNavbar.tsx +++ b/front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx @@ -13,14 +13,12 @@ import { IconUserCircle, IconUsers, } from '@/ui/display/icon'; -import NavItem from '@/ui/navigation/navigation-drawer/components/NavItem'; -import NavTitle from '@/ui/navigation/navigation-drawer/components/NavTitle'; -import SubMenuNavbar from '@/ui/navigation/navigation-drawer/components/SubMenuNavbar'; +import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; +import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -export const SettingsNavbar = () => { +export const SettingsNavigationDrawerItems = () => { const navigate = useNavigate(); - const { signOut } = useAuth(); const handleLogout = useCallback(() => { @@ -35,9 +33,9 @@ export const SettingsNavbar = () => { }); return ( - - - + + { }) } /> - { }) } /> - {isMessagingEnabled && ( - { /> )} - - + { }) } /> - { }) } /> - { }) } /> - { } /> - - - + + + ); }; diff --git a/front/src/modules/ui/navigation/navigation-drawer/components/SupportChat.tsx b/front/src/modules/support/components/SupportChat.tsx similarity index 96% rename from front/src/modules/ui/navigation/navigation-drawer/components/SupportChat.tsx rename to front/src/modules/support/components/SupportChat.tsx index eaf966ffa..a9993ab11 100644 --- a/front/src/modules/ui/navigation/navigation-drawer/components/SupportChat.tsx +++ b/front/src/modules/support/components/SupportChat.tsx @@ -30,7 +30,7 @@ const insertScript = ({ document.body.appendChild(script); }; -const SupportChat = () => { +export const SupportChat = () => { const currentUser = useRecoilValue(currentUserState); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const supportChat = useRecoilValue(supportChatState); @@ -93,8 +93,8 @@ const SupportChat = () => { return isFrontChatLoaded ? (