feat: promote assistant configuration
This commit is contained in:
1 parent
c0445cdca2
commit
cba2df72e2
13 files changed
+219
-23
No files matched your search
+18
-9
@@ -10,6 +10,7 @@ import {
|
||||
Card,
|
||||
Divider,
|
||||
Group,
|
||||
Image,
|
||||
LoadingOverlay,
|
||||
PasswordInput,
|
||||
Select,
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import {
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
IconDatabaseSearch,
|
||||
IconKey,
|
||||
IconPlus,
|
||||
IconRobot,
|
||||
IconShieldLock,
|
||||
IconTool,
|
||||
IconTrash,
|
||||
@@ -41,7 +40,16 @@ import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
type HeaderEntry = { id: number; name: string; value: string };
|
||||
|
||||
export const AssistantSettings = () => {
|
||||
const ProviderIcon = ({ providerId, size = 20 }: { providerId: AssistantProvider; size?: number }) => {
|
||||
const iconUrl = assistantProviderPresets[providerId].iconUrl;
|
||||
return iconUrl ? (
|
||||
<Image src={iconUrl} alt="" aria-hidden w={size} h={size} fit="contain" />
|
||||
) : (
|
||||
<IconWorld size={size} aria-hidden />
|
||||
);
|
||||
};
|
||||
|
||||
export const AssistantConfiguration = () => {
|
||||
const t = useScopedI18n("management.page.settings.section.assistant");
|
||||
const utils = clientApi.useUtils();
|
||||
const { data: configuration, isLoading } = clientApi.assistant.getAdminConfiguration.useQuery();
|
||||
@@ -200,12 +208,6 @@ export const AssistantSettings = () => {
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Group gap="sm">
|
||||
<IconRobot size={26} />
|
||||
<Title order={2}>{t("title")}</Title>
|
||||
</Group>
|
||||
<Text c="dimmed">{t("description")}</Text>
|
||||
|
||||
<Card pos="relative" withBorder>
|
||||
<LoadingOverlay visible={pending} />
|
||||
<Stack gap="lg">
|
||||
@@ -234,6 +236,13 @@ export const AssistantSettings = () => {
|
||||
data={providerOptions}
|
||||
searchable
|
||||
allowDeselect={false}
|
||||
leftSection={<ProviderIcon providerId={provider} />}
|
||||
renderOption={({ option }) => (
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<ProviderIcon providerId={option.value as AssistantProvider} />
|
||||
<Text size="sm">{option.label}</Text>
|
||||
</Group>
|
||||
)}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("baseUrl.title")}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { Stack, Text, Title } from "@mantine/core";
|
||||
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import { getScopedI18n } from "@homarr/translation/server";
|
||||
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
import { AssistantConfiguration } from "./_components/assistant-configuration";
|
||||
|
||||
export async function generateMetadata() {
|
||||
const t = await getScopedI18n("management.page.settings.section.assistant");
|
||||
return {
|
||||
title: `${t("title")} • Homarr`,
|
||||
};
|
||||
}
|
||||
|
||||
export default async function AssistantPage() {
|
||||
const session = await auth();
|
||||
|
||||
if (!session?.user.permissions.includes("admin")) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const t = await getScopedI18n("management.page.settings.section.assistant");
|
||||
return (
|
||||
<>
|
||||
<DynamicBreadcrumb />
|
||||
<Stack>
|
||||
<div>
|
||||
<Title order={1}>{t("title")}</Title>
|
||||
<Text c="dimmed" maw="70ch">
|
||||
{t("description")}
|
||||
</Text>
|
||||
</div>
|
||||
<AssistantConfiguration />
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
IconMailForward,
|
||||
IconPhotoFilled,
|
||||
IconPointerFilled,
|
||||
IconRobot,
|
||||
IconSearch,
|
||||
IconSettingsFilled,
|
||||
IconUserFilled,
|
||||
@@ -198,6 +199,12 @@ export default async function ManageLayout({ children }: PropsWithChildren) {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t("items.assistant"),
|
||||
href: "/manage/assistant",
|
||||
icon: IconRobot,
|
||||
hidden: !session?.user.permissions.includes("admin"),
|
||||
},
|
||||
{
|
||||
label: t("items.settings"),
|
||||
href: "/manage/settings",
|
||||
|
||||
@@ -259,7 +259,7 @@ export const AssistantProvider = ({ children }: PropsWithChildren) => {
|
||||
const t = useScopedI18n("common.assistant");
|
||||
const session = useSession();
|
||||
const [opened, setOpened] = useState(false);
|
||||
const { data } = clientApi.assistant.getAvailability.useQuery(undefined, {
|
||||
const { data, isLoading: isAvailabilityLoading } = clientApi.assistant.getAvailability.useQuery(undefined, {
|
||||
enabled: session.status === "authenticated",
|
||||
staleTime: 60_000,
|
||||
});
|
||||
@@ -268,19 +268,32 @@ export const AssistantProvider = ({ children }: PropsWithChildren) => {
|
||||
const open = useCallback(() => setOpened(true), []);
|
||||
const close = useCallback(() => setOpened(false), []);
|
||||
const toggle = useCallback(() => setOpened((current) => !current), []);
|
||||
useHotkeys([[hotkeys.openAssistant, open]]);
|
||||
const openIfEnabled = useCallback(() => {
|
||||
if (enabled) {
|
||||
open();
|
||||
}
|
||||
}, [enabled, open]);
|
||||
useHotkeys([[hotkeys.openAssistant, openIfEnabled]]);
|
||||
|
||||
const spotlightItem = useMemo(
|
||||
() => ({
|
||||
id: "homarr-assistant",
|
||||
name: t("spotlight"),
|
||||
icon: "/logo/logo.png",
|
||||
interaction: () => ({ type: "javaScript" as const, onSelect: open }),
|
||||
description: enabled
|
||||
? t("spotlightDescription")
|
||||
: session.status !== "authenticated"
|
||||
? t("unavailable.signIn")
|
||||
: isAvailabilityLoading
|
||||
? t("unavailable.checking")
|
||||
: t("unavailable.notConfigured"),
|
||||
unavailable: !enabled,
|
||||
interaction: () => (enabled ? { type: "javaScript" as const, onSelect: open } : { type: "none" as const }),
|
||||
}),
|
||||
[open, t],
|
||||
[enabled, isAvailabilityLoading, open, session.status, t],
|
||||
);
|
||||
useRegisterSpotlightContextResults("homarr-assistant", enabled ? [spotlightItem] : [], [enabled, spotlightItem]);
|
||||
useRegisterSpotlightContextActions("homarr-assistant", enabled ? [spotlightItem] : [], [enabled, spotlightItem]);
|
||||
useRegisterSpotlightContextResults("homarr-assistant", [spotlightItem], [spotlightItem]);
|
||||
useRegisterSpotlightContextActions("homarr-assistant", [spotlightItem], [spotlightItem]);
|
||||
|
||||
const value = useMemo(() => ({ enabled, opened, open, close, toggle }), [close, enabled, open, opened, toggle]);
|
||||
|
||||
|
||||
Reference in new issue
Block a user