diff --git a/app/[lang]/admin/page.tsx b/app/[lang]/admin/page.tsx index f6db0d9..cf77222 100644 --- a/app/[lang]/admin/page.tsx +++ b/app/[lang]/admin/page.tsx @@ -12,6 +12,7 @@ import { ShieldCheck, } from "lucide-react" import { useCallback, useEffect, useMemo, useRef, useState } from "react" +import { ProviderLogo } from "@/components/provider-logo" import { Button } from "@/components/ui/button" import { Collapsible, @@ -33,8 +34,10 @@ import { SETTING_GROUPS, SETTINGS_REGISTRY, type SettingDef, + SUBGROUP_PROVIDERS, } from "@/lib/admin/settings-registry" import { getApiEndpoint } from "@/lib/base-path" +import type { ProviderName } from "@/lib/types/model-config" import { cn } from "@/lib/utils" const SESSION_PASSWORD_KEY = "next-ai-draw-io-admin-password" @@ -327,6 +330,11 @@ function ProviderSubgroup({ )} aria-hidden="true" /> + {SUBGROUP_PROVIDERS[name] && ( + + )} {name} {configured && ( @@ -365,6 +373,8 @@ export default function AdminPage() { const [errors, setErrors] = useState>({}) const [saving, setSaving] = useState(false) const [saveMessage, setSaveMessage] = useState("") + const [justSaved, setJustSaved] = useState(false) + const [activeGroup, setActiveGroup] = useState(SETTING_GROUPS[0].id) const mainRef = useRef(null) const dirtyCount = Object.keys(pending).length @@ -429,6 +439,29 @@ export default function AdminPage() { return () => window.removeEventListener("beforeunload", handler) }, [dirtyCount]) + // Highlight the section currently in view in the sidebar + useEffect(() => { + if (!authedPassword) return + const observer = new IntersectionObserver( + (entries) => { + const visible = entries + .filter((e) => e.isIntersecting) + .sort( + (a, b) => + a.boundingClientRect.top - b.boundingClientRect.top, + ) + if (visible[0]) setActiveGroup(visible[0].target.id) + }, + // Track which section heading is in the top half of the viewport + { rootMargin: "-10% 0px -50% 0px" }, + ) + for (const group of SETTING_GROUPS) { + const el = document.getElementById(group.id) + if (el) observer.observe(el) + } + return () => observer.disconnect() + }, [authedPassword]) + const handleChange = useCallback( (key: string, value: string | null) => { setSaveMessage("") @@ -494,6 +527,11 @@ export default function AdminPage() { applyResponse(data) setPending({}) setSaveMessage("Settings saved. Changes apply immediately.") + setJustSaved(true) + setTimeout(() => { + setJustSaved(false) + setSaveMessage("") + }, 4000) } catch { setSaveMessage( "Save failed: network error. Check your connection and try again.", @@ -585,7 +623,7 @@ export default function AdminPage() { return (
-
+
-
+