Merge branch 'dev' into feat/add-integration-from-dashboard

This commit is contained in:
Thomas authored and GitHub committed 2026-05-07 19:10:03 +02:00
commit 6520dedf1c
79 files changed
+9400 -695

No files matched your search

@@ -0,0 +1,94 @@
"use client";
import React from "react";
import { Combobox, Group, InputBase, Text, useCombobox } from "@mantine/core";
import { IconCheck, IconDeviceDesktop, IconMoon, IconSun } from "@tabler/icons-react";
import type { ColorScheme } from "@homarr/definitions";
import { colorSchemes } from "@homarr/definitions";
import { useScopedI18n } from "@homarr/translation/client";
const schemeIcons: Record<ColorScheme, React.ElementType> = {
auto: IconDeviceDesktop,
light: IconSun,
dark: IconMoon,
};
interface ColorSchemeComboboxProps {
value: ColorScheme;
onChange: (value: ColorScheme) => void;
width?: string;
}
export const ColorSchemeCombobox = ({ value, onChange, width }: ColorSchemeComboboxProps) => {
const tOptions = useScopedI18n("common.colorScheme.options");
const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
});
const handleOnOptionSubmit = React.useCallback(
(val: string) => {
if (!val) return;
onChange(val as ColorScheme);
combobox.closeDropdown();
},
[onChange, combobox],
);
const handleOnClick = React.useCallback(() => {
combobox.toggleDropdown();
}, [combobox]);
return (
<Combobox store={combobox} onOptionSubmit={handleOnOptionSubmit}>
<Combobox.Target>
<InputBase
component="button"
type="button"
pointer
rightSection={<Combobox.Chevron />}
rightSectionPointerEvents="none"
onClick={handleOnClick}
variant="filled"
w={width}
>
<OptionItem scheme={value} currentScheme={value} tOptions={tOptions} />
</InputBase>
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Options>
{colorSchemes.map((scheme) => (
<Combobox.Option value={scheme} key={scheme}>
<OptionItem scheme={scheme} currentScheme={value} tOptions={tOptions} showCheck />
</Combobox.Option>
))}
</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
);
};
const OptionItem = ({
scheme,
currentScheme,
tOptions,
showCheck,
}: {
scheme: ColorScheme;
currentScheme: ColorScheme;
tOptions: (key: ColorScheme) => string;
showCheck?: boolean;
}) => {
const Icon = schemeIcons[scheme];
return (
<Group wrap="nowrap" justify="space-between">
<Group wrap="nowrap">
<Icon size={16} stroke={1.5} />
<Text>{tOptions(scheme)}</Text>
</Group>
{showCheck && scheme === currentScheme && <IconCheck color="currentColor" size={16} />}
</Group>
);
};
@@ -1,49 +1,17 @@
"use client";
import { Group, Text, useMantineColorScheme } from "@mantine/core";
import { IconMoon, IconSun } from "@tabler/icons-react";
import { useMantineColorScheme } from "@mantine/core";
import type { ColorScheme } from "@homarr/definitions";
import { colorSchemes } from "@homarr/definitions";
import { useScopedI18n } from "@homarr/translation/client";
import { SelectWithCustomItems } from "@homarr/ui";
import { ColorSchemeCombobox } from "./color-scheme-combobox";
interface CurrentColorSchemeComboboxProps {
w?: string;
}
export const CurrentColorSchemeCombobox = ({ w }: CurrentColorSchemeComboboxProps) => {
const tOptions = useScopedI18n("common.colorScheme.options");
const { colorScheme, setColorScheme } = useMantineColorScheme();
return (
<SelectWithCustomItems
value={colorScheme}
onChange={(value) => setColorScheme((value as ColorScheme | null) ?? "light")}
data={colorSchemes.map((scheme) => ({
value: scheme,
label: tOptions(scheme),
}))}
SelectOption={ColorSchemeCustomOption}
w={w}
/>
);
};
const appearanceIcons = {
light: IconSun,
dark: IconMoon,
};
const ColorSchemeCustomOption = ({ value, label }: { value: ColorScheme; label: string }) => {
const Icon = appearanceIcons[value];
return (
<Group>
<Icon size={16} stroke={1.5} />
<Text fz="sm" fw={500}>
{label}
</Text>
</Group>
);
return <ColorSchemeCombobox value={colorScheme as ColorScheme} onChange={setColorScheme} width={w} />;
};
@@ -5,16 +5,7 @@ import { useCallback, useEffect } from "react";
import { useRouter } from "next/navigation";
import { Center, Menu, Stack, Text, useMantineColorScheme } from "@mantine/core";
import { useHotkeys, useTimeout } from "@mantine/hooks";
import {
IconCheck,
IconHome,
IconLogin,
IconLogout,
IconMoon,
IconSettings,
IconSun,
IconTool,
} from "@tabler/icons-react";
import { IconCheck, IconHome, IconLogin, IconLogout, IconSettings, IconTool } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { signOut, useSession } from "@homarr/auth/client";
@@ -24,6 +15,7 @@ import { useScopedI18n } from "@homarr/translation/client";
import { Link } from "@homarr/ui";
import { useAuthContext } from "~/app/[locale]/_client-providers/session";
import { CurrentColorSchemeCombobox } from "./color-scheme/current-color-scheme-combobox";
import { CurrentLanguageCombobox } from "./language/current-language-combobox";
import { AvailableUpdatesMenuItem } from "./layout/header/update";
@@ -34,13 +26,9 @@ interface UserAvatarMenuProps {
export const UserAvatarMenu = ({ children, availableUpdatesPromise }: UserAvatarMenuProps) => {
const t = useScopedI18n("common.userAvatar.menu");
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const { toggleColorScheme } = useMantineColorScheme();
useHotkeys([[hotkeys.toggleColorScheme, toggleColorScheme]]);
const ColorSchemeIcon = colorScheme === "dark" ? IconSun : IconMoon;
const colorSchemeText = colorScheme === "dark" ? t("switchToLightMode") : t("switchToDarkMode");
const session = useSession();
const router = useRouter();
@@ -67,14 +55,14 @@ export const UserAvatarMenu = ({ children, availableUpdatesPromise }: UserAvatar
<Menu width={300} withArrow withinPortal keepMounted>
<Menu.Dropdown>
<AvailableUpdatesMenuItem availableUpdatesPromise={availableUpdatesPromise} />
<Menu.Item onClick={toggleColorScheme} leftSection={<ColorSchemeIcon size="1rem" />}>
{colorSchemeText}
</Menu.Item>
<Menu.Item component={Link} href="/boards" leftSection={<IconHome size="1rem" />}>
{t("homeBoard")}
</Menu.Item>
<Menu.Divider />
<Menu.Item p={0} closeMenuOnClick={false} component="div">
<CurrentColorSchemeCombobox />
</Menu.Item>
<Menu.Item p={0} closeMenuOnClick={false} component="div">
<CurrentLanguageCombobox />
</Menu.Item>