Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
639cbbd032 |
No files matched your search
@@ -36,6 +36,7 @@ import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
|
||||
import type { NavigationLink } from "~/components/layout/navigation";
|
||||
import { MainNavigation } from "~/components/layout/navigation";
|
||||
import { ClientShell } from "~/components/layout/shell";
|
||||
import { ManageNavigationFooter } from "~/components/manage/manage-navigation-footer";
|
||||
|
||||
export default async function ManageLayout({ children }: PropsWithChildren) {
|
||||
const t = await getScopedI18n("management.navbar");
|
||||
@@ -194,7 +195,7 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<ClientShell hasNavigation>
|
||||
<MainHeader></MainHeader>
|
||||
<MainNavigation links={navigationLinks}></MainNavigation>
|
||||
<MainNavigation links={navigationLinks} footerSection={<ManageNavigationFooter />}></MainNavigation>
|
||||
<AppShellMain>{children}</AppShellMain>
|
||||
</ClientShell>
|
||||
);
|
||||
|
||||
@@ -6,11 +6,20 @@ import { LanguageCombobox } from "./language-combobox";
|
||||
|
||||
interface CurrentLanguageComboboxProps {
|
||||
width?: string;
|
||||
showTranslatedName?: boolean;
|
||||
}
|
||||
|
||||
export const CurrentLanguageCombobox = ({ width }: CurrentLanguageComboboxProps) => {
|
||||
export const CurrentLanguageCombobox = ({ width, showTranslatedName }: CurrentLanguageComboboxProps) => {
|
||||
const currentLocale = useCurrentLocale();
|
||||
const { changeLocale, isPending } = useChangeLocale();
|
||||
|
||||
return <LanguageCombobox value={currentLocale} onChange={changeLocale} isPending={isPending} width={width} />;
|
||||
return (
|
||||
<LanguageCombobox
|
||||
showTranslatedName={showTranslatedName}
|
||||
value={currentLocale}
|
||||
onChange={changeLocale}
|
||||
isPending={isPending}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -16,9 +16,17 @@ interface LanguageComboboxProps {
|
||||
onChange: (value: SupportedLanguage) => void;
|
||||
isPending?: boolean;
|
||||
width?: string;
|
||||
showTranslatedName?: boolean;
|
||||
}
|
||||
|
||||
export const LanguageCombobox = ({ label, value, onChange, isPending, width }: LanguageComboboxProps) => {
|
||||
export const LanguageCombobox = ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
isPending,
|
||||
width,
|
||||
showTranslatedName,
|
||||
}: LanguageComboboxProps) => {
|
||||
const combobox = useCombobox({
|
||||
onDropdownClose: () => combobox.resetSelectedOption(),
|
||||
});
|
||||
@@ -53,7 +61,7 @@ export const LanguageCombobox = ({ label, value, onChange, isPending, width }: L
|
||||
variant="filled"
|
||||
w={width}
|
||||
>
|
||||
<OptionItem currentLocale={value} localeKey={value} />
|
||||
<OptionItem currentLocale={value} localeKey={value} showTranslatedName={showTranslatedName} />
|
||||
</InputBase>
|
||||
</Combobox.Target>
|
||||
<Combobox.Dropdown>
|
||||
@@ -75,10 +83,12 @@ const OptionItem = ({
|
||||
currentLocale,
|
||||
localeKey,
|
||||
showCheck,
|
||||
showTranslatedName = true,
|
||||
}: {
|
||||
currentLocale: SupportedLanguage;
|
||||
localeKey: SupportedLanguage;
|
||||
showCheck?: boolean;
|
||||
showTranslatedName?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Group wrap="nowrap" justify="space-between">
|
||||
@@ -86,9 +96,11 @@ const OptionItem = ({
|
||||
<LanguageIcon icon={localeConfigurations[localeKey].icon} />
|
||||
<Group wrap="nowrap" gap="xs">
|
||||
<Text>{localeConfigurations[localeKey].name}</Text>
|
||||
<Text size="xs" c="dimmed" inherit>
|
||||
({localeConfigurations[localeKey].translatedName})
|
||||
</Text>
|
||||
{showTranslatedName && (
|
||||
<Text size="xs" c="dimmed" inherit>
|
||||
({localeConfigurations[localeKey].translatedName})
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
{showCheck && localeKey === currentLocale && <IconCheck color="currentColor" size={16} />}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Button, Stack, useMantineColorScheme } from "@mantine/core";
|
||||
import { IconLogout, IconMoon, IconSun } from "@tabler/icons-react";
|
||||
|
||||
|
||||
|
||||
import { signOut, useSession } from "@homarr/auth/client";
|
||||
import { createModal, useModalAction } from "@homarr/modals";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
|
||||
|
||||
import { useAuthContext } from "~/app/[locale]/_client-providers/session";
|
||||
import { CurrentLanguageCombobox } from "../language/current-language-combobox";
|
||||
|
||||
|
||||
const LogoutModal = createModal(() => {
|
||||
const t = useScopedI18n("common.userAvatar.menu.logoutModal");
|
||||
|
||||
return ({ onTimeout }: { onTimeout: () => void }) => (
|
||||
<Stack>
|
||||
<div>{t("description")}</div>
|
||||
<Button onClick={onTimeout} fullWidth>
|
||||
{t("action")}
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
}).withOptions({
|
||||
centered: true,
|
||||
defaultTitle: (t) => t("common.userAvatar.menu.logoutModal.title"),
|
||||
});
|
||||
|
||||
export const ManageNavigationFooter = () => {
|
||||
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 { logoutUrl } = useAuthContext();
|
||||
const { openModal } = useModalAction(LogoutModal);
|
||||
|
||||
const handleSignout = useCallback(async () => {
|
||||
await signOut({
|
||||
redirect: false,
|
||||
});
|
||||
openModal({
|
||||
onTimeout: () => {
|
||||
if (logoutUrl) {
|
||||
window.location.assign(logoutUrl);
|
||||
return;
|
||||
}
|
||||
router.refresh();
|
||||
},
|
||||
});
|
||||
}, [logoutUrl, openModal, router]);
|
||||
|
||||
return (
|
||||
<Stack gap="xs">
|
||||
<Button
|
||||
variant="subtle"
|
||||
leftSection={<ColorSchemeIcon size="1rem" />}
|
||||
onClick={toggleColorScheme}
|
||||
justify="flex-start"
|
||||
fullWidth
|
||||
>
|
||||
{colorSchemeText}
|
||||
</Button>
|
||||
<CurrentLanguageCombobox width="100%" showTranslatedName={false} />
|
||||
{session.status === "authenticated" && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
leftSection={<IconLogout size="1rem" />}
|
||||
onClick={handleSignout}
|
||||
color="red"
|
||||
justify="flex-start"
|
||||
fullWidth
|
||||
>
|
||||
{t("logout")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user