mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
feat(settings): add debug toggle and sidebar visibility toggle
feat(ui): replace hide-sidebar toggle with dynamic visibility toggle
This commit is contained in:
@@ -221,9 +221,7 @@ export default function AppearanceTab() {
|
||||
|
||||
<div className="pt-4 border-t border-border">
|
||||
<div className="mb-3">
|
||||
<p className="font-medium">
|
||||
{getSettingsLabel("sidebarVisibility", "Hide sidebar items")}
|
||||
</p>
|
||||
<p className="font-medium">{t("sidebarVisibilityToggle")}</p>
|
||||
<p className="text-sm text-text-muted">
|
||||
{getSettingsLabel(
|
||||
"sidebarVisibilityDesc",
|
||||
@@ -249,7 +247,7 @@ export default function AppearanceTab() {
|
||||
>
|
||||
<p className="font-medium">{item.label}</p>
|
||||
<Toggle
|
||||
checked={hiddenSidebarSet.has(item.id)}
|
||||
checked={!hiddenSidebarSet.has(item.id)}
|
||||
onChange={() => toggleSidebarItem(item.id)}
|
||||
disabled={loading}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { Card, Button, ProxyConfigModal } from "@/shared/components";
|
||||
import { Card, Button, ProxyConfigModal, Toggle } from "@/shared/components";
|
||||
import { useTranslations } from "next-intl";
|
||||
import ProxyRegistryManager from "./ProxyRegistryManager";
|
||||
|
||||
@@ -11,6 +11,8 @@ export default function ProxyTab() {
|
||||
const mountedRef = useRef(true);
|
||||
const t = useTranslations("settings");
|
||||
const tc = useTranslations("common");
|
||||
const [debugMode, setDebugMode] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const loadGlobalProxy = async () => {
|
||||
try {
|
||||
@@ -22,6 +24,21 @@ export default function ProxyTab() {
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const updateDebugMode = async (value: boolean) => {
|
||||
try {
|
||||
const res = await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ debugMode: value }),
|
||||
});
|
||||
if (res.ok) {
|
||||
setDebugMode(value);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to update debugMode:", err);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
async function init() {
|
||||
@@ -40,6 +57,19 @@ export default function ProxyTab() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/settings")
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
setDebugMode(data.debugMode === true);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-6">
|
||||
@@ -78,6 +108,18 @@ export default function ProxyTab() {
|
||||
</Card>
|
||||
|
||||
<ProxyRegistryManager />
|
||||
<Card className="p-6 mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium">{t("debugToggle")}</p>
|
||||
</div>
|
||||
<Toggle
|
||||
checked={debugMode}
|
||||
onChange={() => updateDebugMode(!debugMode)}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<ProxyConfigModal
|
||||
|
||||
64
src/app/api/settings/__tests__/settings.test.ts
Normal file
64
src/app/api/settings/__tests__/settings.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { PATCH } from "../route";
|
||||
|
||||
// Mock the localDb functions used in the route
|
||||
vi.mock("../../../../lib/localDb", () => {
|
||||
const original = vi.importActual("../../../../lib/localDb");
|
||||
return {
|
||||
...original,
|
||||
getSettings: vi.fn(),
|
||||
updateSettings: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
import { getSettings, updateSettings } from "../../../../lib/localDb";
|
||||
|
||||
// Helper to create a Request with JSON body
|
||||
function createPatchRequest(body: unknown) {
|
||||
return new Request("http://localhost/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
describe("PATCH /api/settings", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
// Default settings before each test
|
||||
(getSettings as any).mockResolvedValue({
|
||||
debugMode: false,
|
||||
hiddenSidebarItems: [],
|
||||
});
|
||||
// Mock updateSettings to merge updates into the original
|
||||
(updateSettings as any).mockImplementation(async (updates: Record<string, unknown>) => {
|
||||
const current = await (getSettings as any)();
|
||||
return { ...current, ...updates };
|
||||
});
|
||||
});
|
||||
|
||||
it("toggles debugMode via PATCH", async () => {
|
||||
const req = createPatchRequest({ debugMode: true });
|
||||
const res = await PATCH(req as any);
|
||||
expect(res.status).toBe(200);
|
||||
const json = await res.json();
|
||||
expect(json.debugMode).toBe(true);
|
||||
// Ensure password is not leaked
|
||||
expect(json).not.toHaveProperty("password");
|
||||
// Verify DB update called with correct payload
|
||||
expect(updateSettings).toHaveBeenCalledOnce();
|
||||
const calledWith = (updateSettings as any).mock.calls[0][0];
|
||||
expect(calledWith.debugMode).toBe(true);
|
||||
});
|
||||
|
||||
it("updates hiddenSidebarItems via PATCH", async () => {
|
||||
const req = createPatchRequest({ hiddenSidebarItems: [] });
|
||||
const res = await PATCH(req as any);
|
||||
expect(res.status).toBe(200);
|
||||
const json = await res.json();
|
||||
expect(json.hiddenSidebarItems).toEqual([]);
|
||||
expect(updateSettings).toHaveBeenCalledOnce();
|
||||
const calledWith = (updateSettings as any).mock.calls[0][0];
|
||||
expect(calledWith.hiddenSidebarItems).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1678,6 +1678,8 @@
|
||||
"darkMode": "Dark Mode",
|
||||
"lightMode": "Light Mode",
|
||||
"systemTheme": "System Theme",
|
||||
"debugToggle": "Enable Debug Mode",
|
||||
"sidebarVisibilityToggle": "Show Sidebar Items",
|
||||
"enableCache": "Enable Cache",
|
||||
"cacheTTL": "Cache TTL",
|
||||
"maxCacheSize": "Max Cache Size",
|
||||
|
||||
@@ -26,6 +26,7 @@ export const updateSettingsSchema = z.object({
|
||||
requireAuthForModels: z.boolean().optional(),
|
||||
blockedProviders: z.array(z.string().max(100)).optional(),
|
||||
hideHealthCheckLogs: z.boolean().optional(),
|
||||
debugMode: z.boolean().optional(),
|
||||
hiddenSidebarItems: z.array(z.enum(HIDEABLE_SIDEBAR_ITEM_IDS)).optional(),
|
||||
// Routing settings (#134)
|
||||
fallbackStrategy: z
|
||||
|
||||
Reference in New Issue
Block a user