diff --git a/apps/client/src/components/layouts/global/app-header.module.css b/apps/client/src/components/layouts/global/app-header.module.css index 7cdec643..6abbdad4 100644 --- a/apps/client/src/components/layouts/global/app-header.module.css +++ b/apps/client/src/components/layouts/global/app-header.module.css @@ -13,6 +13,7 @@ text-decoration: none; color: inherit; cursor: pointer; + user-select: none; } .brandIcon { @@ -33,21 +34,3 @@ that is ~9.3px, minus the font descent (~2px) ≈ 7px. */ margin-bottom: rem(7px); } - -.link { - display: block; - line-height: 1; - padding: rem(8px) rem(12px); - border-radius: var(--mantine-radius-sm); - text-decoration: none; - color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); - font-size: var(--mantine-font-size-sm); - font-weight: 500; - user-select: none; - white-space: nowrap; - flex-shrink: 0; - - @mixin hover { - background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); - } -} diff --git a/apps/client/src/components/layouts/global/app-header.tsx b/apps/client/src/components/layouts/global/app-header.tsx index fc8e769a..8eb7c303 100644 --- a/apps/client/src/components/layouts/global/app-header.tsx +++ b/apps/client/src/components/layouts/global/app-header.tsx @@ -10,7 +10,6 @@ import classes from "./app-header.module.css"; import { BrandLogo } from "@/components/ui/brand-logo"; import TopMenu from "@/components/layouts/global/top-menu.tsx"; import { Link } from "react-router-dom"; -import APP_ROUTE from "@/lib/app-route.ts"; import { useAtom, useSetAtom } from "jotai"; import { desktopSidebarAtom, @@ -30,10 +29,6 @@ import { } from "@/features/search/constants.ts"; import { NotificationPopover } from "@/features/notification/components/notification-popover.tsx"; -const links = [ - { link: APP_ROUTE.HOME, label: "Home" }, -]; - export function AppHeader() { const { t } = useTranslation(); const [mobileOpened] = useAtom(mobileSidebarAtom); @@ -47,12 +42,6 @@ export function AppHeader() { // AI chat entry point: only shown when the workspace enables it (A7 gate). const aiChatEnabled = workspace?.settings?.ai?.chat === true; - const items = links.map((link) => ( - - {t(link.label)} - - )); - return ( <> @@ -97,10 +86,6 @@ export function AppHeader() { - - - {items} -
diff --git a/apps/client/src/components/layouts/global/top-menu.tsx b/apps/client/src/components/layouts/global/top-menu.tsx index 84925080..f2872ea0 100644 --- a/apps/client/src/components/layouts/global/top-menu.tsx +++ b/apps/client/src/components/layouts/global/top-menu.tsx @@ -20,18 +20,29 @@ import { } from "@tabler/icons-react"; import { useAtom } from "jotai"; import { currentUserAtom } from "@/features/user/atoms/current-user-atom.ts"; -import { Link } from "react-router-dom"; +import { Link, useMatch } from "react-router-dom"; import APP_ROUTE from "@/lib/app-route.ts"; import useAuth from "@/features/auth/hooks/use-auth.ts"; import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; import { useTranslation } from "react-i18next"; import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts"; +import { useDisclosure } from "@mantine/hooks"; +import SpaceSettingsModal from "@/features/space/components/settings-modal.tsx"; export default function TopMenu() { const { t } = useTranslation(); const [currentUser] = useAtom(currentUserAtom); const { logout } = useAuth(); const { colorScheme, setColorScheme } = useMantineColorScheme(); + // Detect the currently viewed space so the "Space settings" item is only + // offered while the user is inside a space. The "/*" splat also matches the + // bare "/s/:spaceSlug" route (the splat matches an empty segment). + const spaceMatch = useMatch("/s/:spaceSlug/*"); + const spaceSlug = spaceMatch?.params?.spaceSlug; + const [ + spaceSettingsOpened, + { open: openSpaceSettings, close: closeSpaceSettings }, + ] = useDisclosure(false); const user = currentUser?.user; const workspace = currentUser?.workspace; @@ -41,124 +52,143 @@ export default function TopMenu() { } return ( - - - - - - - {workspace?.name} - - - - - - - {t("Workspace")} - - } - > - {t("Workspace settings")} - - - } - > - {t("Manage members")} - - - - - {t("Account")} - - - - -
- - {user.name} + <> + + + + + + + {workspace?.name} - - {user.email} - -
-
-
- } - > - {t("My profile")} - + + + + + + {t("Workspace")} - } - > - {t("My preferences")} - + } + > + {t("Workspace settings")} + - - - }> - {t("Theme")} - - - - + {spaceSlug && ( setColorScheme("light")} - leftSection={} - rightSection={ - colorScheme === "light" ? : null - } + onClick={openSpaceSettings} + leftSection={} > - {t("Light")} + {t("Space settings")} - setColorScheme("dark")} - leftSection={} - rightSection={ - colorScheme === "dark" ? : null - } - > - {t("Dark")} - - setColorScheme("auto")} - leftSection={} - rightSection={ - colorScheme === "auto" ? : null - } - > - {t("System settings")} - - - + )} - + } + > + {t("Manage members")} + - }> - {t("Logout")} - - -
+ + + {t("Account")} + + + + +
+ + {user.name} + + + {user.email} + +
+
+
+ } + > + {t("My profile")} + + + } + > + {t("My preferences")} + + + + + }> + {t("Theme")} + + + + + setColorScheme("light")} + leftSection={} + rightSection={ + colorScheme === "light" ? : null + } + > + {t("Light")} + + setColorScheme("dark")} + leftSection={} + rightSection={ + colorScheme === "dark" ? : null + } + > + {t("Dark")} + + setColorScheme("auto")} + leftSection={} + rightSection={ + colorScheme === "auto" ? : null + } + > + {t("System settings")} + + + + + + + }> + {t("Logout")} + + + + + {spaceSlug && ( + + )} + ); } diff --git a/apps/client/src/components/settings/settings-sidebar.tsx b/apps/client/src/components/settings/settings-sidebar.tsx index 47ecece6..a3f6c0ed 100644 --- a/apps/client/src/components/settings/settings-sidebar.tsx +++ b/apps/client/src/components/settings/settings-sidebar.tsx @@ -20,7 +20,6 @@ import { prefetchSpaces, prefetchWorkspaceMembers, } from "@/components/settings/settings-queries.tsx"; -import AppVersion from "@/components/settings/app-version.tsx"; import { mobileSidebarAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts"; import { useToggleSidebar } from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar.ts"; import { useSettingsNavigation } from "@/hooks/use-settings-navigation"; @@ -141,8 +140,6 @@ export default function SettingsSidebar() { {menuItems} - -
); } diff --git a/apps/client/src/components/ui/brand-logo.tsx b/apps/client/src/components/ui/brand-logo.tsx index cc41ce5e..3ec5706f 100644 --- a/apps/client/src/components/ui/brand-logo.tsx +++ b/apps/client/src/components/ui/brand-logo.tsx @@ -27,6 +27,7 @@ export function BrandLogo({ src={src} alt="Gitmost" className={className} + draggable={false} style={{ height, width: "auto", display: "block", userSelect: "none" }} /> ); diff --git a/apps/client/src/features/space/components/sidebar/space-sidebar.tsx b/apps/client/src/features/space/components/sidebar/space-sidebar.tsx index b5b9a9c8..b6ccfefc 100644 --- a/apps/client/src/features/space/components/sidebar/space-sidebar.tsx +++ b/apps/client/src/features/space/components/sidebar/space-sidebar.tsx @@ -87,7 +87,6 @@ export function SpaceSidebar() { spaceName={space?.name} spaceSlug={space?.slug} spaceIcon={space?.logo} - onSettings={openSettings} /> diff --git a/apps/client/src/features/space/components/sidebar/switch-space.module.css b/apps/client/src/features/space/components/sidebar/switch-space.module.css index 480c40bb..39493607 100644 --- a/apps/client/src/features/space/components/sidebar/switch-space.module.css +++ b/apps/client/src/features/space/components/sidebar/switch-space.module.css @@ -2,16 +2,6 @@ width: 100%; } -.header { - padding: rem(4px) var(--mantine-spacing-sm); -} - -.spaceName { - flex: 1; - min-width: 0; - color: light-dark(var(--mantine-color-dark-4), var(--mantine-color-dark-0)); -} - .grid { display: flex; flex-wrap: wrap; diff --git a/apps/client/src/features/space/components/sidebar/switch-space.tsx b/apps/client/src/features/space/components/sidebar/switch-space.tsx index 23b19679..5fc94daf 100644 --- a/apps/client/src/features/space/components/sidebar/switch-space.tsx +++ b/apps/client/src/features/space/components/sidebar/switch-space.tsx @@ -1,8 +1,7 @@ import classes from "./switch-space.module.css"; import { useNavigate } from "react-router-dom"; import { getSpaceUrl } from "@/lib/config"; -import { ActionIcon, Group, Text, Tooltip, UnstyledButton } from "@mantine/core"; -import { IconSettings } from "@tabler/icons-react"; +import { Text, UnstyledButton } from "@mantine/core"; import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; import { AvatarIconType } from "@/features/attachments/types/attachment.types.ts"; import { @@ -10,7 +9,6 @@ import { useGetSpacesQuery, } from "@/features/space/queries/space-query.ts"; import { ISpace } from "../../types/space.types"; -import { useTranslation } from "react-i18next"; import clsx from "clsx"; import React, { useMemo } from "react"; @@ -19,7 +17,6 @@ interface SwitchSpaceProps { spaceName: string; spaceSlug: string; spaceIcon?: string; - onSettings: () => void; } export function SwitchSpace({ @@ -27,9 +24,7 @@ export function SwitchSpace({ spaceName, spaceSlug, spaceIcon, - onSettings, }: SwitchSpaceProps) { - const { t } = useTranslation(); const navigate = useNavigate(); // Load every space the user belongs to (API caps limit at 100) and render // them as an always-visible grid instead of the previous searchable popover. @@ -59,31 +54,6 @@ export function SwitchSpace({ return (
- - - - {spaceName} - - - - - - - -
{spaces.map((space: ISpace) => (