Files
homarr/apps/nextjs/src/components/user-avatar-menu.tsx
Meier Lukas db01301845 feat: user preferences (#470)
* wip: improve user preferences

* wip: fix translations and add user danger zone

* feat: add user delete button to danger zone

* fix: test not working

* refactor: add access checks for user edit page, improve not found behaviour, change user preference link in avatar menu to correct link

* fix: remove invalid bg for container

* chore: address pull request feedback
2024-05-12 16:27:56 +02:00

147 lines
3.5 KiB
TypeScript

"use client";
import type { ReactNode } from "react";
import { useCallback, useEffect } from "react";
import Link from "next/link";
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,
IconSettings,
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 {
children: ReactNode;
}
export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => {
const t = useScopedI18n("common.userAvatar.menu");
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const ColorSchemeIcon = colorScheme === "dark" ? IconSun : IconMoon;
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 (
<Menu width={200} withArrow withinPortal>
<Menu.Dropdown>
<Menu.Item
onClick={toggleColorScheme}
leftSection={<ColorSchemeIcon size="1rem" />}
>
{colorSchemeText}
</Menu.Item>
<Menu.Item
component={Link}
href="/boards"
leftSection={<IconDashboard size="1rem" />}
>
{t("navigateDefaultBoard")}
</Menu.Item>
{Boolean(session.data) && (
<Menu.Item
component={Link}
href={`/manage/users/${session.data?.user.id}`}
leftSection={<IconSettings size="1rem" />}
>
{t("preferences")}
</Menu.Item>
)}
<Menu.Item
component={Link}
href="/manage"
leftSection={<IconTool size="1rem" />}
>
{t("management")}
</Menu.Item>
<Menu.Divider />
{session.status === "authenticated" ? (
<Menu.Item
onClick={handleSignout}
leftSection={<IconLogout size="1rem" />}
color="red"
>
{t("logout")}
</Menu.Item>
) : (
<Menu.Item
onClick={() => router.push("/auth/login")}
leftSection={<IconLogin size="1rem" />}
>
{t("login")}
</Menu.Item>
)}
</Menu.Dropdown>
<Menu.Target>{children}</Menu.Target>
</Menu>
);
};
const LogoutModal = createModal<{ onTimeout: () => void }>(
({ actions, innerProps }) => {
const t = useScopedI18n("common.userAvatar.menu");
const { start } = useTimeout(() => {
actions.closeModal();
innerProps.onTimeout();
}, 1500);
useEffect(() => {
start();
}, [start]);
return (
<Center h={200 - 2 * 16}>
<Stack align="center" c="green">
<IconCheck size={50} />
<Text ta="center" fw="bold">
{t("loggedOut")}
</Text>
</Stack>
</Center>
);
},
).withOptions({
centered: true,
withCloseButton: false,
transitionProps: {
transition: "pop",
},
size: 200,
closeOnClickOutside: false,
closeOnEscape: false,
});