From 0abc192c3b3a834a285a31cfe3cb3766a5ca8a0c Mon Sep 17 00:00:00 2001 From: Ronen Mars Date: Wed, 31 Dec 2025 22:08:42 +0200 Subject: [PATCH 1/4] fix: remove stats from main menu and optimize the design of the mobile menu --- src/components/organisms/sidebar/sidebar.tsx | 77 +++++++------------- 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/src/components/organisms/sidebar/sidebar.tsx b/src/components/organisms/sidebar/sidebar.tsx index 347a532347..05654deef7 100644 --- a/src/components/organisms/sidebar/sidebar.tsx +++ b/src/components/organisms/sidebar/sidebar.tsx @@ -21,7 +21,7 @@ import { UserMenu } from "@components/organisms/sidebar"; import { IconLogo, IconLogoName } from "@assets/image"; import { EventsFlag } from "@assets/image/icons"; -import { CircleQuestionIcon, FileIcon, StatsBlackIcon } from "@assets/image/icons/sidebar"; +import { CircleQuestionIcon, FileIcon } from "@assets/image/icons/sidebar"; export const Sidebar = () => { const [isOpen, setIsOpen] = useState(false); @@ -79,17 +79,21 @@ export const Sidebar = () => { if (isMobile) { return ( }> -
-
@@ -105,47 +109,39 @@ export const Sidebar = () => { /> -
-
- +
+
+ {featureFlags.hideOrgConnections ? null : ( )}
-
- +
{descopeProjectId ? ( -
- - {user?.name} +
+ + {user?.name}
) : null}
@@ -265,27 +261,6 @@ export const Sidebar = () => {
- - - @@ -100,8 +86,15 @@ export const ProjectsMenu = ({ className, isOpen = false }: MenuProps) => {
  • -
    -
    +
    +
    { />
    - - {isOpen ? ( - - {t("myProjects")} - - ) : null} - + {isOpen ? {t("myProjects")} : null}
  • @@ -135,8 +116,8 @@ export const ProjectsMenu = ({ className, isOpen = false }: MenuProps) => { )} emptyListMessage={t("noProjectsFound")} itemClassName={cn( - "hover:text-current flex cursor-pointer items-center gap-2.5 rounded-3xl p-2 transition", - "whitespace-nowrap px-4 text-center text-gray-1100 duration-300 hover:bg-green-200", + "hover:text-current flex cursor-pointer gap-2.5 rounded-3xl p-2 transition", + "whitespace-nowrap px-4 text-gray-1100 duration-300 hover:bg-green-200", "overflow-hidden" )} items={sortedProjectsList.map(({ id, name }) => ({ id, label: name, value: id }))} diff --git a/src/components/organisms/sidebar/sidebar.tsx b/src/components/organisms/sidebar/sidebar.tsx index 05654deef7..a002fbcf88 100644 --- a/src/components/organisms/sidebar/sidebar.tsx +++ b/src/components/organisms/sidebar/sidebar.tsx @@ -3,7 +3,7 @@ import React, { Suspense, useEffect, useMemo, useState } from "react"; import { AnimatePresence, motion } from "motion/react"; import Avatar from "react-avatar"; import { useTranslation } from "react-i18next"; -import { LuUnplug } from "react-icons/lu"; +import { LuUnplug, LuX } from "react-icons/lu"; import { useLocation, useNavigate } from "react-router-dom"; import { descopeProjectId, featureFlags } from "@constants"; @@ -76,105 +76,113 @@ export const Sidebar = () => { [isFeedbackOpen] ); + const mobileMenuItemClass = + "w-full !justify-start gap-4 rounded-xl px-4 py-3.5 text-lg text-left hover:bg-green-200 active:bg-green-400 min-h-[52px]"; + if (isMobile) { return ( }> -
    -
    {isOpen ? ( - <> - setIsOpen(false)} - /> - -
    -
    - + +
    + + setIsOpen(false)} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + +
    - {featureFlags.hideOrgConnections ? null : ( - - )} -
    +
    +
    + -
    - + {featureFlags.hideOrgConnections ? null : ( - + + - {descopeProjectId ? ( -
    - - {user?.name} -
    - ) : null} -
    + + + {t("systemLog")} + + +
    - - + + {descopeProjectId ? ( +
    +
    + + + {user?.name} + +
    +
    + ) : null} +
    + ) : null} @@ -184,7 +192,12 @@ export const Sidebar = () => { return ( }>
    -
    +
    - - - - - -
  • - -
    -
    - -
    - - {isOpen ? {t("myProjects")} : null} -
    -
    -
  • -
    - ({ id, label: name, value: id }))} - onItemSelect={({ id: selectedProjectId }: { id: string }) => { - const isSettingsDrawerOpen = drawers[selectedProjectId]?.settings === true; - const storedSettingsPath = settingsPath[selectedProjectId]; - const basePath = `/${SidebarHrefMenu.projects}/${selectedProjectId}/explorer`; - const fullPath = - isSettingsDrawerOpen && storedSettingsPath - ? `${basePath}/${storedSettingsPath}` - : basePath; - navigate(fullPath); - }} - /> -
    - - - ); -}; diff --git a/src/components/organisms/sidebar/components/desktopMenuSidebar.tsx b/src/components/organisms/sidebar/components/desktopMenuSidebar.tsx new file mode 100644 index 0000000000..3385e6d328 --- /dev/null +++ b/src/components/organisms/sidebar/components/desktopMenuSidebar.tsx @@ -0,0 +1,65 @@ +import { Suspense, useMemo } from "react"; + +import { SidebarLogo } from "./logo"; +import { ConnectionsMenuItem } from "./menuItems/connectionsMenuItem"; +import { EventsMenuItem } from "./menuItems/eventsMenuItem"; +import { IntroMenuItem } from "./menuItems/introMenuItem"; +import { SystemLogMenuItem } from "./menuItems/systemLogMenuItem"; +import { SidebarMenuToggle } from "./menuToggle"; +import { ProjectsMenu } from "./projectsMenu"; +import { SidebarMenuItemProps } from "./sidebar.types"; +import { SidebarUserSection } from "./userSection"; +import { cn } from "@src/utilities"; + +import { Loader } from "@components/atoms"; +import { UserFeedbackForm } from "@components/organisms"; + +interface DesktopSidebarProps { + isFeedbackOpen: boolean; + isOpen: boolean; + menuItemProps: SidebarMenuItemProps; + onCloseFeedbackForm: () => void; + onOpenFeedbackForm: () => void; + onToggle: () => void; +} + +export const DesktopSidebar = ({ + isFeedbackOpen, + isOpen, + menuItemProps, + onCloseFeedbackForm, + onOpenFeedbackForm, + onToggle, +}: DesktopSidebarProps) => { + const rootClassName = useMemo( + () => cn("relative z-30 flex h-full items-start", { "z-50": isFeedbackOpen }), + [isFeedbackOpen] + ); + + return ( + }> +
    +
    +
    + + + + +
    + +
    + + + + +
    + +
    +
    +
    + ); +}; diff --git a/src/components/organisms/sidebar/components/index.ts b/src/components/organisms/sidebar/components/index.ts new file mode 100644 index 0000000000..cd7fcaa7a1 --- /dev/null +++ b/src/components/organisms/sidebar/components/index.ts @@ -0,0 +1,13 @@ +export { ConnectionsMenuItem } from "./menuItems/connectionsMenuItem"; +export { EventsMenuItem } from "./menuItems/eventsMenuItem"; +export { IntroMenuItem } from "./menuItems/introMenuItem"; +export { SystemLogMenuItem } from "./menuItems/systemLogMenuItem"; + +export { DesktopSidebar } from "./desktopMenuSidebar"; +export { MobileSidebar } from "./mobileTopMenuBar"; +export { ProjectsMenu } from "./projectsMenu/projectsMenu"; +export { SidebarLogo } from "./logo"; +export { SidebarMenuItem } from "./menuItem"; +export { SidebarMenuToggle } from "./menuToggle"; +export { SidebarUserSection } from "./userSection"; +export { sidebarAnimateVariant, type SidebarMenuItemProps } from "./sidebar.types"; diff --git a/src/components/organisms/sidebar/components/logo.tsx b/src/components/organisms/sidebar/components/logo.tsx new file mode 100644 index 0000000000..0c4d946ed4 --- /dev/null +++ b/src/components/organisms/sidebar/components/logo.tsx @@ -0,0 +1,58 @@ +import { AnimatePresence, motion } from "motion/react"; +import { useNavigate } from "react-router-dom"; + +import { sidebarAnimateVariant } from "./sidebar.types"; + +import { Button } from "@components/atoms"; + +import { IconLogo, IconLogoName } from "@assets/image"; + +interface SidebarLogoProps { + isMobile: boolean; + isOpen: boolean; + showName?: boolean; +} + +export const SidebarLogo = ({ isMobile, isOpen, showName = true }: SidebarLogoProps) => { + const navigate = useNavigate(); + + const handleLogoClick = () => { + navigate("/"); + }; + + if (isMobile && !isOpen) { + return ( + + ); + } + + if (isMobile && isOpen) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItem.tsx b/src/components/organisms/sidebar/components/menuItem.tsx new file mode 100644 index 0000000000..e11c25b367 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItem.tsx @@ -0,0 +1,69 @@ +import { ReactNode } from "react"; + +import { AnimatePresence, motion } from "motion/react"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "./sidebar.types"; +import { SvgIconType } from "@interfaces/components/icon.interface"; +import { cn } from "@src/utilities"; + +import { Button, IconSvg, Tooltip } from "@components/atoms"; + +interface MenuItemProps extends SidebarMenuItemProps { + ariaLabel: string; + children?: ReactNode; + href?: string; + icon: SvgIconType; + iconClassName?: string; + label: string; + onClick?: () => void; + className?: string; +} + +export const SidebarMenuItem = ({ + ariaLabel, + children, + href, + icon, + iconClassName = "fill-gray-1100", + className, + isMobile, + isOpen, + label, + mobileMenuItemClass, + onClick, +}: MenuItemProps) => { + if (isMobile) { + return ( + + ); + } + + const itemClassName = cn("w-full gap-1.5 p-0.5 hover:bg-green-200", className); + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/connectionsMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/connectionsMenuItem.tsx new file mode 100644 index 0000000000..3870e70d19 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/connectionsMenuItem.tsx @@ -0,0 +1,49 @@ +import { AnimatePresence, motion } from "motion/react"; +import { useTranslation } from "react-i18next"; +import { LuUnplug } from "react-icons/lu"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "../sidebar.types"; +import { featureFlags } from "@constants"; + +import { Button, Tooltip } from "@components/atoms"; + +export const ConnectionsMenuItem = ({ isMobile, isOpen, mobileMenuItemClass }: SidebarMenuItemProps) => { + const { t } = useTranslation("sidebar"); + + if (featureFlags.hideOrgConnections) { + return null; + } + + if (isMobile) { + return ( + + ); + } + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/eventsMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/eventsMenuItem.tsx new file mode 100644 index 0000000000..c4bba8f073 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/eventsMenuItem.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from "react-i18next"; + +import { SidebarMenuItem } from "../menuItem"; +import { SidebarMenuItemProps } from "../sidebar.types"; + +import { IconSvg } from "@components/atoms"; + +import { EventsFlag } from "@assets/image/icons"; + +export const EventsMenuItem = (props: SidebarMenuItemProps) => { + const { t } = useTranslation("sidebar"); + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/index.ts b/src/components/organisms/sidebar/components/menuItems/index.ts new file mode 100644 index 0000000000..cebd8a88de --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/index.ts @@ -0,0 +1,4 @@ +export { ConnectionsMenuItem } from "./connectionsMenuItem"; +export { EventsMenuItem } from "./eventsMenuItem"; +export { IntroMenuItem } from "./introMenuItem"; +export { SystemLogMenuItem } from "./systemLogMenuItem"; diff --git a/src/components/organisms/sidebar/components/menuItems/introMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/introMenuItem.tsx new file mode 100644 index 0000000000..a877d3c5d8 --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/introMenuItem.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from "react-i18next"; + +import { SidebarMenuItem } from "../menuItem"; +import { SidebarMenuItemProps } from "../sidebar.types"; + +import { IconSvg } from "@components/atoms"; + +import { CircleQuestionIcon } from "@assets/image/icons/sidebar"; + +export const IntroMenuItem = (props: SidebarMenuItemProps) => { + const { t } = useTranslation("sidebar"); + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuItems/systemLogMenuItem.tsx b/src/components/organisms/sidebar/components/menuItems/systemLogMenuItem.tsx new file mode 100644 index 0000000000..8077b6f63d --- /dev/null +++ b/src/components/organisms/sidebar/components/menuItems/systemLogMenuItem.tsx @@ -0,0 +1,83 @@ +import { AnimatePresence, motion } from "motion/react"; +import { useTranslation } from "react-i18next"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "../sidebar.types"; + +import { useLoggerStore } from "@store"; + +import { Badge, Button, IconSvg, Tooltip } from "@components/atoms"; + +import { FileIcon } from "@assets/image/icons/sidebar"; + +interface SystemLogMenuItemProps extends SidebarMenuItemProps { + onClose?: () => void; +} + +export const SystemLogMenuItem = ({ isMobile, isOpen, mobileMenuItemClass, onClose }: SystemLogMenuItemProps) => { + const { t } = useTranslation("sidebar"); + const { isNewLogs, setSystemLogHeight, setNewLogs, lastLogType, systemLogHeight } = useLoggerStore(); + + const toggleSystemLogHeight = () => { + setNewLogs(false); + setSystemLogHeight(systemLogHeight < 1 ? 20 : 0); + }; + + const handleClick = () => { + toggleSystemLogHeight(); + onClose?.(); + }; + + if (isMobile) { + return ( + + ); + } + + return ( + + + + ); +}; diff --git a/src/components/organisms/sidebar/components/menuToggle.tsx b/src/components/organisms/sidebar/components/menuToggle.tsx new file mode 100644 index 0000000000..e390664bef --- /dev/null +++ b/src/components/organisms/sidebar/components/menuToggle.tsx @@ -0,0 +1,76 @@ +import { useMemo } from "react"; + +import { AnimatePresence, motion } from "motion/react"; +import { useTranslation } from "react-i18next"; +import { LuX } from "react-icons/lu"; + +import { sidebarAnimateVariant } from "./sidebar.types"; +import { cn } from "@src/utilities"; + +import { Button } from "@components/atoms"; +import { MenuToggle } from "@components/atoms/menuToggle"; + +interface SidebarMenuToggleProps { + isMobile: boolean; + isOpen: boolean; + onToggle: () => void; +} + +export const SidebarMenuToggle = ({ isMobile, isOpen, onToggle }: SidebarMenuToggleProps) => { + const { t } = useTranslation("sidebar"); + const btnClassName = useMemo(() => cn("mt-7 w-full p-0 hover:bg-green-200", { "pr-2": isOpen }), [isOpen]); + + if (isMobile && !isOpen) { + return ( + + ); + } + + if (isMobile && isOpen) { + return ( + + + + ); + } + + return ( + + ); +}; diff --git a/src/components/organisms/sidebar/components/mobileTopMenuBar.tsx b/src/components/organisms/sidebar/components/mobileTopMenuBar.tsx new file mode 100644 index 0000000000..fef8c00abc --- /dev/null +++ b/src/components/organisms/sidebar/components/mobileTopMenuBar.tsx @@ -0,0 +1,64 @@ +import { Suspense } from "react"; + +import { SidebarLogo } from "./logo"; +import { ConnectionsMenuItem } from "./menuItems/connectionsMenuItem"; +import { EventsMenuItem } from "./menuItems/eventsMenuItem"; +import { IntroMenuItem } from "./menuItems/introMenuItem"; +import { SystemLogMenuItem } from "./menuItems/systemLogMenuItem"; +import { SidebarMenuToggle } from "./menuToggle"; +import { ProjectsMenu } from "./projectsMenu"; +import { SidebarMenuItemProps } from "./sidebar.types"; +import { SidebarUserSection } from "./userSection"; + +import { Loader } from "@components/atoms"; + +interface MobileSidebarProps { + isOpen: boolean; + menuItemProps: SidebarMenuItemProps; + onClose: () => void; + onOpenFeedbackForm: () => void; + onToggle: () => void; +} + +export const MobileSidebar = ({ isOpen, menuItemProps, onClose, onOpenFeedbackForm, onToggle }: MobileSidebarProps) => { + return ( + }> +
    + + +
    + + {isOpen ? ( + <> +
    e.key === "Escape" && onClose()} + role="button" + tabIndex={0} + /> +
    +
    +
    + + +
    + +
    + + + + +
    +
    +
    + + ) : null} + + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/index.ts b/src/components/organisms/sidebar/components/projectsMenu/index.ts new file mode 100644 index 0000000000..722b8649da --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/index.ts @@ -0,0 +1,4 @@ +export { MyProjectsPopover } from "./myProjectsPopover"; +export { NewProjectButton } from "./newProjectButton"; +export { ProjectsMenu } from "./projectsMenu"; +export { useProjectsMenu } from "./useProjectsMenu"; diff --git a/src/components/organisms/sidebar/components/projectsMenu/myProjectsPopover.tsx b/src/components/organisms/sidebar/components/projectsMenu/myProjectsPopover.tsx new file mode 100644 index 0000000000..6644e309a7 --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/myProjectsPopover.tsx @@ -0,0 +1,73 @@ +import { AnimatePresence, motion } from "motion/react"; + +import { sidebarAnimateVariant } from "../sidebar.types"; +import { Project } from "@type/models"; +import { cn } from "@utilities"; + +import { IconSvg, Tooltip } from "@components/atoms"; +import { PopoverListContent, PopoverListTrigger, PopoverListWrapper } from "@components/molecules/popover"; + +import { ProjectsIcon } from "@assets/image"; + +interface MyProjectsPopoverProps { + activeProjectId?: string; + emptyListMessage: string; + isOpen: boolean; + label: string; + onProjectSelect: (project: { id: string }) => void; + projects: Project[]; +} + +export const MyProjectsPopover = ({ + activeProjectId, + emptyListMessage, + isOpen, + label, + onProjectSelect, + projects, +}: MyProjectsPopoverProps) => { + return ( + + +
  • + +
    +
    + +
    + + + {isOpen ? ( + + {label} + + ) : null} + +
    +
    +
  • +
    + ({ id, label: name, value: id }))} + onItemSelect={onProjectSelect} + /> +
    + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/newProjectButton.tsx b/src/components/organisms/sidebar/components/projectsMenu/newProjectButton.tsx new file mode 100644 index 0000000000..2ccd6058f9 --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/newProjectButton.tsx @@ -0,0 +1,45 @@ +import { AnimatePresence, motion } from "motion/react"; + +import { sidebarAnimateVariant } from "../sidebar.types"; + +import { Button, IconSvg, Tooltip } from "@components/atoms"; + +import { NewProject } from "@assets/image"; + +interface NewProjectButtonProps { + isOpen: boolean; + label: string; + onClick: () => void; +} + +export const NewProjectButton = ({ isOpen, label, onClick }: NewProjectButtonProps) => { + return ( +
  • + + + +
  • + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/projectsMenu.tsx b/src/components/organisms/sidebar/components/projectsMenu/projectsMenu.tsx new file mode 100644 index 0000000000..3d31a4a88c --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/projectsMenu.tsx @@ -0,0 +1,29 @@ +import { MyProjectsPopover } from "./myProjectsPopover"; +import { NewProjectButton } from "./newProjectButton"; +import { useProjectsMenu } from "./useProjectsMenu"; +import { cn } from "@utilities"; + +interface ProjectsMenuProps { + className?: string; + isOpen?: boolean; +} + +export const ProjectsMenu = ({ className, isOpen = false }: ProjectsMenuProps) => { + const { handleNewProject, handleProjectSelect, projectId, sortedProjectsList, t } = useProjectsMenu(); + + return ( + + ); +}; diff --git a/src/components/organisms/sidebar/components/projectsMenu/useProjectsMenu.ts b/src/components/organisms/sidebar/components/projectsMenu/useProjectsMenu.ts new file mode 100644 index 0000000000..bf292e4b76 --- /dev/null +++ b/src/components/organisms/sidebar/components/projectsMenu/useProjectsMenu.ts @@ -0,0 +1,73 @@ +import { useEffect, useState } from "react"; + +import { useTranslation } from "react-i18next"; +import { useNavigate, useParams } from "react-router-dom"; + +import { SidebarHrefMenu } from "@enums/components"; +import { LoggerService } from "@services/logger.service"; +import { descopeProjectId, namespaces } from "@src/constants"; +import { Project } from "@type/models"; + +import { useOrganizationStore, useProjectStore, useSharedBetweenProjectsStore, useToastStore } from "@store"; + +export const useProjectsMenu = () => { + const { t } = useTranslation(["menu", "errors"]); + const { getProjectsList, projectsList } = useProjectStore(); + const navigate = useNavigate(); + const { projectId } = useParams(); + const addToast = useToastStore((state) => state.addToast); + const [sortedProjectsList, setSortedProjectsList] = useState([]); + const { user } = useOrganizationStore(); + const { drawers, settingsPath } = useSharedBetweenProjectsStore(); + + useEffect(() => { + const sortedProjects = projectsList.slice().sort((a, b) => a.name.localeCompare(b.name)); + setSortedProjectsList(sortedProjects); + }, [projectsList]); + + const fetchProjects = async () => { + const { error } = await getProjectsList(); + if (error) { + addToast({ + message: t("projectsListFetchFailed"), + type: "error", + }); + + LoggerService.error( + namespaces.ui.menu, + t("projectsListFetchFailedExtended", { + error, + }) + ); + } + }; + + useEffect(() => { + if (!descopeProjectId) { + fetchProjects(); + return; + } + if (user) { + fetchProjects(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleProjectSelect = ({ id: selectedProjectId }: { id: string }) => { + const isSettingsDrawerOpen = drawers[selectedProjectId]?.settings === true; + const storedSettingsPath = settingsPath[selectedProjectId]; + const basePath = `/${SidebarHrefMenu.projects}/${selectedProjectId}/explorer`; + const fullPath = isSettingsDrawerOpen && storedSettingsPath ? `${basePath}/${storedSettingsPath}` : basePath; + navigate(fullPath); + }; + + const handleNewProject = () => navigate("/ai"); + + return { + handleNewProject, + handleProjectSelect, + projectId, + sortedProjectsList, + t, + }; +}; diff --git a/src/components/organisms/sidebar/components/sidebar.types.ts b/src/components/organisms/sidebar/components/sidebar.types.ts new file mode 100644 index 0000000000..b6605f3827 --- /dev/null +++ b/src/components/organisms/sidebar/components/sidebar.types.ts @@ -0,0 +1,10 @@ +export interface SidebarMenuItemProps { + isMobile: boolean; + isOpen: boolean; + mobileMenuItemClass: string; +} + +export const sidebarAnimateVariant = { + hidden: { opacity: 0, width: 0 }, + visible: { opacity: 1, transition: { duration: 0.35, ease: "easeOut" as const }, width: "auto" }, +}; diff --git a/src/components/organisms/sidebar/components/userSection.tsx b/src/components/organisms/sidebar/components/userSection.tsx new file mode 100644 index 0000000000..049c709870 --- /dev/null +++ b/src/components/organisms/sidebar/components/userSection.tsx @@ -0,0 +1,59 @@ +import { AnimatePresence, motion } from "motion/react"; +import Avatar from "react-avatar"; + +import { sidebarAnimateVariant, SidebarMenuItemProps } from "./sidebar.types"; +import { descopeProjectId } from "@constants"; + +import { useOrganizationStore } from "@store"; + +import { PopoverWrapper, PopoverContent, PopoverTrigger } from "@components/molecules/popover"; +import { UserMenu } from "@components/organisms/sidebar"; + +interface SidebarUserSectionProps extends Omit { + onOpenFeedbackForm: () => void; +} + +export const SidebarUserSection = ({ isMobile, isOpen, onOpenFeedbackForm }: SidebarUserSectionProps) => { + const { user } = useOrganizationStore(); + + if (!descopeProjectId) { + return null; + } + + if (isMobile) { + return ( +
    + + {user?.name} +
    + ); + } + + return ( + + +
    +
    + +
    + + {isOpen ? ( + + {user?.name} + + ) : null} + +
    +
    + + + +
    + ); +}; diff --git a/src/components/organisms/sidebar/sidebar.tsx b/src/components/organisms/sidebar/sidebar.tsx index a002fbcf88..e2b83e39a2 100644 --- a/src/components/organisms/sidebar/sidebar.tsx +++ b/src/components/organisms/sidebar/sidebar.tsx @@ -1,38 +1,22 @@ -import React, { Suspense, useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; -import { AnimatePresence, motion } from "motion/react"; -import Avatar from "react-avatar"; -import { useTranslation } from "react-i18next"; -import { LuUnplug, LuX } from "react-icons/lu"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useLocation } from "react-router-dom"; -import { descopeProjectId, featureFlags } from "@constants"; -import { cn } from "@src/utilities"; +import { DesktopSidebar, MobileSidebar } from "./components"; +import { descopeProjectId } from "@constants"; import { useWindowDimensions } from "@hooks"; -import { useLoggerStore, useOrganizationStore, useToastStore } from "@store"; +import { useOrganizationStore, useToastStore } from "@store"; -import { Badge, Button, IconSvg, Loader, Tooltip } from "@components/atoms"; -import { MenuToggle } from "@components/atoms/menuToggle"; -import { PopoverWrapper, PopoverContent, PopoverTrigger } from "@components/molecules/popover"; -import { ProjectsMenu } from "@components/molecules/projectsMenu"; -import { UserFeedbackForm } from "@components/organisms"; -import { UserMenu } from "@components/organisms/sidebar"; - -import { IconLogo, IconLogoName } from "@assets/image"; -import { EventsFlag } from "@assets/image/icons"; -import { CircleQuestionIcon, FileIcon } from "@assets/image/icons/sidebar"; +const mobileMenuItemClass = "w-full justify-start gap-3 p-2 hover:bg-green-200"; export const Sidebar = () => { const [isOpen, setIsOpen] = useState(false); const [isFeedbackOpen, setIsFeedbackOpen] = useState(false); const { isMobile } = useWindowDimensions(); const { user, getEnrichedOrganizations, currentOrganization } = useOrganizationStore(); - const { isNewLogs, setSystemLogHeight, setNewLogs, lastLogType, systemLogHeight } = useLoggerStore(); const location = useLocation(); - const { t } = useTranslation("sidebar"); const addToast = useToastStore((state) => state.addToast); - const navigate = useNavigate(); useEffect(() => { setIsOpen(false); @@ -42,7 +26,7 @@ export const Sidebar = () => { const { data, error } = await getEnrichedOrganizations(); if (error || !data) { addToast({ - message: t("organizationFetchingFailed"), + message: "Failed to fetch organizations", type: "error", }); return; @@ -57,334 +41,37 @@ export const Sidebar = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [user, currentOrganization]); - const handleLogoClick = () => { - navigate("/"); - }; - - const animateVariant = { - hidden: { opacity: 0, width: 0 }, - visible: { opacity: 1, transition: { duration: 0.35, ease: "easeOut" as const }, width: "auto" }, - }; + const handleToggle = () => setIsOpen(!isOpen); + const handleClose = () => setIsOpen(false); + const handleOpenFeedbackForm = () => setIsFeedbackOpen(true); + const handleCloseFeedbackForm = () => setIsFeedbackOpen(false); - const toggleSystemLogHeight = () => { - setNewLogs(false); - setSystemLogHeight(systemLogHeight < 1 ? 20 : 0); + const menuItemProps = { + isMobile, + isOpen, + mobileMenuItemClass, }; - const rootClassName = useMemo( - () => cn("relative z-30 flex h-full items-start", { "z-50": isFeedbackOpen }), - [isFeedbackOpen] - ); - - const mobileMenuItemClass = - "w-full !justify-start gap-4 rounded-xl px-4 py-3.5 text-lg text-left hover:bg-green-200 active:bg-green-400 min-h-[52px]"; - if (isMobile) { return ( - }> -
    - - -
    - - - {isOpen ? ( - -
    - - setIsOpen(false)} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - - -
    - -
    -
    - - - {featureFlags.hideOrgConnections ? null : ( - - )} - - - - - - -
    - - {descopeProjectId ? ( -
    -
    - - - {user?.name} - -
    -
    - ) : null} -
    -
    - ) : null} -
    -
    + ); } return ( - }> -
    -
    -
    - - - - - - {featureFlags.hideOrgConnections ? null : ( - - - - )} -
    - -
    - - - - - - - - - - {descopeProjectId ? ( - - -
    - - - {isOpen ? ( - - {user?.name} - - ) : null} - -
    -
    - - setIsFeedbackOpen(true)} /> - -
    - ) : null} -
    - setIsFeedbackOpen(false)} - /> -
    -
    -
    + ); }; diff --git a/src/components/pages/aiLandingPage.tsx b/src/components/pages/aiLandingPage.tsx index 166ecdc6dc..37ecaf2309 100644 --- a/src/components/pages/aiLandingPage.tsx +++ b/src/components/pages/aiLandingPage.tsx @@ -124,7 +124,7 @@ export const AiLandingPage = () => { const showQuickstart = !projectsList.some((project) => project.name.toLowerCase() === "quickstart"); return ( -
    +
    diff --git a/src/components/pages/dashboard.tsx b/src/components/pages/dashboard.tsx index 007bba07b2..09c04a7e36 100644 --- a/src/components/pages/dashboard.tsx +++ b/src/components/pages/dashboard.tsx @@ -28,7 +28,7 @@ export const Dashboard = () => { } return ( -
    +
    { <> -
    +
    { return (
    -
    +
    diff --git a/src/components/templates/systemLogLayout.tsx b/src/components/templates/systemLogLayout.tsx index 575582c5d9..125e73973b 100644 --- a/src/components/templates/systemLogLayout.tsx +++ b/src/components/templates/systemLogLayout.tsx @@ -28,7 +28,7 @@ export const SystemLogLayout = ({ sidebar?: ReactNode; topbar?: ReactNode; }) => { - const layoutClasses = cn("flex h-screen flex-1 overflow-hidden", className); + const layoutClasses = cn("mr-1 mt-1 flex h-screen flex-1 overflow-hidden", className); const location = useLocation(); const { pathname } = location; const { projectId } = useParams(); @@ -82,7 +82,7 @@ export const SystemLogLayout = ({ }); const buttonResizeClasses = cn("my-0.5", { "my-0": systemLogHeight === 100 }); - const innerLayoutClasses = cn("mr-2 flex min-w-0 flex-1 flex-col md:mb-2", { + const innerLayoutClasses = cn("mr-1 flex min-w-0 flex-1 flex-col md:mb-2", { "md:mb-0.5": systemLogHeight === 0, "w-0": ["/", "/intro"].includes(pathname), "mr-0": isMobile,