This commit is contained in:
Eduard Gert
2026-05-13 10:11:38 +02:00
parent c8e18585c6
commit 1c8a6e3798
24 changed files with 959 additions and 96 deletions

View File

@@ -7,6 +7,7 @@ import { VerticalTabs } from "@/components/VerticalTabs.tsx";
import { SettingsNavigationTriggers } from "@/modules/settings/SettingsNavigationTriggers.tsx";
import { SettingsProvider } from "@/modules/settings/SettingsContext.tsx";
import { SettingsGeneral } from "@/modules/settings/SettingsGeneral.tsx";
import { SettingsAppearance } from "@/modules/settings/SettingsAppearance.tsx";
import { SettingsNetwork } from "@/modules/settings/SettingsNetwork.tsx";
import { SettingsSecurity } from "@/modules/settings/SettingsSecurity.tsx";
import { SettingsSSH } from "@/modules/settings/SettingsSSH.tsx";
@@ -14,15 +15,35 @@ import { SettingsAdvanced } from "@/modules/settings/SettingsAdvanced.tsx";
import { SettingsTroubleshooting } from "@/modules/settings/SettingsTroubleshooting.tsx";
import { SettingsAbout } from "@/modules/settings/SettingsAbout.tsx";
const LAST_TAB_KEY = "netbird:settings:lastTab";
const readLastTab = () => {
try {
return localStorage.getItem(LAST_TAB_KEY);
} catch {
return null;
}
};
export const Settings = () => {
const location = useLocation();
const navState = location.state as { tab?: string } | null;
const [active, setActive] = useState(navState?.tab ?? "general");
const [active, setActive] = useState(
() => navState?.tab ?? readLastTab() ?? "general",
);
useEffect(() => {
if (navState?.tab) setActive(navState.tab);
}, [navState?.tab, location.key]);
useEffect(() => {
try {
localStorage.setItem(LAST_TAB_KEY, active);
} catch {
// ignore quota / unavailable storage
}
}, [active]);
return (
<VerticalTabs value={active} onValueChange={setActive} className={"p-4"}>
<SettingsNavigationTriggers />
@@ -37,6 +58,9 @@ export const Settings = () => {
<VerticalTabs.Content value={"general"}>
<SettingsGeneral />
</VerticalTabs.Content>
<VerticalTabs.Content value={"appearance"}>
<SettingsAppearance />
</VerticalTabs.Content>
<VerticalTabs.Content value={"network"}>
<SettingsNetwork />
</VerticalTabs.Content>

View File

@@ -0,0 +1,88 @@
import FancyToggleSwitch from "@/components/FancyToggleSwitch";
import { CardSelect } from "@/components/CardSelect.tsx";
import { SectionGroup } from "@/modules/settings/SettingsSection.tsx";
import {
useAppearance,
type AppearanceView,
} from "@/modules/appearance/AppearanceContext.tsx";
import simpleScreen from "@/assets/screens/simple.png";
import advancedScreen from "@/assets/screens/advanced.png";
const ScreenPreview = ({ src, alt }: { src: string; alt: string }) => (
<img
src={src}
alt={alt}
draggable={false}
className={"h-full w-full object-contain select-none"}
/>
);
export function SettingsAppearance() {
const {
view,
setView,
showPeersNav,
showResourcesNav,
showExitNodeNav,
showProfileSelector,
showSettingsButton,
setField,
} = useAppearance();
return (
<>
<SectionGroup title={"View"}>
<CardSelect
value={view}
onChange={(v) => setView(v as AppearanceView)}
>
<CardSelect.Option
value={"default"}
title={"Simple"}
description={"Streamlined view with essential controls."}
preview={<ScreenPreview src={simpleScreen} alt={"Simple view"} />}
/>
<CardSelect.Option
value={"advanced"}
title={"Advanced"}
description={"All details and power-user options visible."}
preview={<ScreenPreview src={advancedScreen} alt={"Advanced view"} />}
/>
</CardSelect>
</SectionGroup>
<SectionGroup title={"Interface"}>
<FancyToggleSwitch
value={showPeersNav}
onChange={(v) => setField("showPeersNav", v)}
label={"Peers"}
helpText={"Show the Peers item in the side navigation."}
/>
<FancyToggleSwitch
value={showResourcesNav}
onChange={(v) => setField("showResourcesNav", v)}
label={"Resources"}
helpText={"Show the Resources item in the side navigation."}
/>
<FancyToggleSwitch
value={showExitNodeNav}
onChange={(v) => setField("showExitNodeNav", v)}
label={"Exit Node"}
helpText={"Show the active exit node in the side navigation."}
/>
<FancyToggleSwitch
value={showProfileSelector}
onChange={(v) => setField("showProfileSelector", v)}
label={"Profile Selector"}
helpText={"Show the profile selector in the header."}
/>
<FancyToggleSwitch
value={showSettingsButton}
onChange={(v) => setField("showSettingsButton", v)}
label={"Settings Button"}
helpText={"Show the settings button in the header."}
/>
</SectionGroup>
</>
);
}

View File

@@ -10,6 +10,7 @@ import {
ShieldIcon,
SlidersHorizontalIcon,
SquareTerminalIcon,
SwatchBookIcon,
} from "lucide-react";
export const SettingsNavigationTriggers = () => {
@@ -29,6 +30,11 @@ export const SettingsNavigationTriggers = () => {
icon={SlidersHorizontalIcon}
title={"General"}
/>
<VerticalTabs.Trigger
value={"appearance"}
icon={SwatchBookIcon}
title={"Appearance"}
/>
<VerticalTabs.Trigger
value={"network"}
icon={NetworkIcon}