diff --git a/apps/nextjs/src/components/user-avatar-menu.tsx b/apps/nextjs/src/components/user-avatar-menu.tsx index a71efb16c..a8bb39d27 100644 --- a/apps/nextjs/src/components/user-avatar-menu.tsx +++ b/apps/nextjs/src/components/user-avatar-menu.tsx @@ -1,16 +1,29 @@ "use client"; import type { ReactNode } from "react"; +import { useCallback, useEffect } from "react"; import Link from "next/link"; -import { Menu, useMantineColorScheme } from "@mantine/core"; +import { useRouter } from "next/navigation"; import { + Center, + Menu, + Stack, + Text, + useMantineColorScheme, +} from "@mantine/core"; +import { useTimeout } from "@mantine/hooks"; +import { + IconCheck, IconDashboard, + IconLogin, IconLogout, IconMoon, IconSun, IconTool, } from "@tabler/icons-react"; +import { signOut, useSession } from "@homarr/auth/client"; +import { createModal, useModalAction } from "@homarr/modals"; import { useScopedI18n } from "@homarr/translation/client"; interface UserAvatarMenuProps { @@ -26,6 +39,22 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { const colorSchemeText = colorScheme === "dark" ? t("switchToLightMode") : t("switchToDarkMode"); + const session = useSession(); + const router = useRouter(); + + const { openModal } = useModalAction(LogoutModal); + + const handleSignout = useCallback(async () => { + await signOut({ + redirect: false, + }); + openModal({ + onTimeout: () => { + router.refresh(); + }, + }); + }, [openModal, router]); + return ( @@ -50,11 +79,58 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { {t("management")} - } color="red"> - {t("logout")} - + {session.status === "authenticated" ? ( + } + color="red" + > + {t("logout")} + + ) : ( + router.push("/auth/login")} + leftSection={} + > + {t("login")} + + )} {children} ); }; + +const LogoutModal = createModal<{ onTimeout: () => void }>( + ({ actions, innerProps }) => { + const t = useScopedI18n("common.userAvatar.menu"); + const { start } = useTimeout(() => { + actions.closeModal(); + innerProps.onTimeout(); + }, 1500); + + useEffect(() => { + start(); + }, []); + + return ( +
+ + + + {t("loggedOut")} + + +
+ ); + }, +).withOptions({ + centered: true, + withCloseButton: false, + transitionProps: { + transition: "pop", + }, + size: 200, + closeOnClickOutside: false, + closeOnEscape: false, +}); diff --git a/packages/modals/src/type.ts b/packages/modals/src/type.ts index a5f2040e8..19feaa550 100644 --- a/packages/modals/src/type.ts +++ b/packages/modals/src/type.ts @@ -18,6 +18,9 @@ export type CreateModalOptions = Pick< | "zIndex" | "scrollAreaComponent" | "yOffset" + | "transitionProps" + | "closeOnClickOutside" + | "closeOnEscape" > & { defaultTitle: stringOrTranslation; }; diff --git a/packages/translation/src/lang/en.ts b/packages/translation/src/lang/en.ts index b16b91d3e..06f442dc1 100644 --- a/packages/translation/src/lang/en.ts +++ b/packages/translation/src/lang/en.ts @@ -403,7 +403,9 @@ export default { switchToLightMode: "Switch to light mode", management: "Management", logout: "Logout", + login: "Login", navigateDefaultBoard: "Navigate to default board", + loggedOut: "Logged out", }, }, menu: {