diff --git a/apps/nextjs/src/app/[locale]/manage/layout.tsx b/apps/nextjs/src/app/[locale]/manage/layout.tsx
index 32013221c..ec9b6c316 100644
--- a/apps/nextjs/src/app/[locale]/manage/layout.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/layout.tsx
@@ -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 (
-
+ }>
{children}
);
diff --git a/apps/nextjs/src/components/language/current-language-combobox.tsx b/apps/nextjs/src/components/language/current-language-combobox.tsx
index e27f34c39..1272880a4 100644
--- a/apps/nextjs/src/components/language/current-language-combobox.tsx
+++ b/apps/nextjs/src/components/language/current-language-combobox.tsx
@@ -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 ;
+ return (
+
+ );
};
diff --git a/apps/nextjs/src/components/language/language-combobox.tsx b/apps/nextjs/src/components/language/language-combobox.tsx
index 6f4b39b33..7cd94b6d5 100644
--- a/apps/nextjs/src/components/language/language-combobox.tsx
+++ b/apps/nextjs/src/components/language/language-combobox.tsx
@@ -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}
>
-
+
@@ -75,10 +83,12 @@ const OptionItem = ({
currentLocale,
localeKey,
showCheck,
+ showTranslatedName = true,
}: {
currentLocale: SupportedLanguage;
localeKey: SupportedLanguage;
showCheck?: boolean;
+ showTranslatedName?: boolean;
}) => {
return (
@@ -86,9 +96,11 @@ const OptionItem = ({
{localeConfigurations[localeKey].name}
-
- ({localeConfigurations[localeKey].translatedName})
-
+ {showTranslatedName && (
+
+ ({localeConfigurations[localeKey].translatedName})
+
+ )}
{showCheck && localeKey === currentLocale && }
diff --git a/apps/nextjs/src/components/manage/manage-navigation-footer.tsx b/apps/nextjs/src/components/manage/manage-navigation-footer.tsx
new file mode 100644
index 000000000..eb483c206
--- /dev/null
+++ b/apps/nextjs/src/components/manage/manage-navigation-footer.tsx
@@ -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 }) => (
+
+ {t("description")}
+
+
+ );
+}).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 (
+
+ }
+ onClick={toggleColorScheme}
+ justify="flex-start"
+ fullWidth
+ >
+ {colorSchemeText}
+
+
+ {session.status === "authenticated" && (
+ }
+ onClick={handleSignout}
+ color="red"
+ justify="flex-start"
+ fullWidth
+ >
+ {t("logout")}
+
+ )}
+
+ );
+};