mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(dashboard): v3.8.8 screen quick wins (Phase 1)
- search-tools: export modal no longer opens by default / stuck — guard on exportOpen and drop the invalid isOpen prop the modal never read. - logs: remove duplicate proxy/console tabs + SegmentedControl (dedicated /dashboard/logs/proxy and /console pages already exist in the menu). - memory: order tabs Memories -> Engine -> Playground. - ui(Select): render children and suppress the placeholder/options branch when children are provided — fixes the "empty" memory type/strategy dropdowns (children were being shadowed by the component's own option list). - test: source-level regression guards for all four.
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { ConfirmModal, RequestLoggerV2, ProxyLogger, SegmentedControl } from "@/shared/components";
|
||||
import ConsoleLogViewer from "@/shared/components/ConsoleLogViewer";
|
||||
import { ConfirmModal, RequestLoggerV2 } from "@/shared/components";
|
||||
import EmailPrivacyToggle from "@/shared/components/EmailPrivacyToggle";
|
||||
import ActiveRequestsPanel from "@/shared/components/ActiveRequestsPanel";
|
||||
import { useTranslations } from "next-intl";
|
||||
@@ -15,18 +13,7 @@ const TIME_RANGES = [
|
||||
{ label: "24h", hours: 24 },
|
||||
];
|
||||
|
||||
const TAB_TO_LOG_TYPE: Record<string, string> = {
|
||||
"request-logs": "request-logs",
|
||||
"proxy-logs": "proxy-logs",
|
||||
console: "call-logs",
|
||||
};
|
||||
|
||||
export default function LogsPage() {
|
||||
const searchParams = useSearchParams();
|
||||
const requestedTab = searchParams.get("tab");
|
||||
const [activeTab, setActiveTab] = useState(
|
||||
requestedTab && TAB_TO_LOG_TYPE[requestedTab] ? requestedTab : "request-logs"
|
||||
);
|
||||
const [showExport, setShowExport] = useState(false);
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [showCleanHistory, setShowCleanHistory] = useState(false);
|
||||
@@ -36,12 +23,6 @@ export default function LogsPage() {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const t = useTranslations("logs");
|
||||
|
||||
useEffect(() => {
|
||||
if (requestedTab && TAB_TO_LOG_TYPE[requestedTab] && requestedTab !== activeTab) {
|
||||
setActiveTab(requestedTab);
|
||||
}
|
||||
}, [activeTab, requestedTab]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
||||
@@ -56,7 +37,7 @@ export default function LogsPage() {
|
||||
setExporting(true);
|
||||
setShowExport(false);
|
||||
try {
|
||||
const logType = TAB_TO_LOG_TYPE[activeTab] || "call-logs";
|
||||
const logType = "request-logs";
|
||||
const res = await fetch(`/api/logs/export?hours=${hours}&type=${logType}`);
|
||||
if (!res.ok) throw new Error(t("exportFailed"));
|
||||
const blob = await res.blob();
|
||||
@@ -108,15 +89,7 @@ export default function LogsPage() {
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||
<SegmentedControl
|
||||
options={[
|
||||
{ value: "request-logs", label: t("requestLogs") },
|
||||
{ value: "proxy-logs", label: t("proxyLogs") },
|
||||
{ value: "console", label: t("console") },
|
||||
]}
|
||||
value={activeTab}
|
||||
onChange={setActiveTab}
|
||||
/>
|
||||
<h2 className="text-lg font-semibold text-text-main">{t("requestLogs")}</h2>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<EmailPrivacyToggle size="md" />
|
||||
@@ -211,14 +184,10 @@ export default function LogsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "request-logs" && (
|
||||
<div className="flex flex-col gap-6">
|
||||
<ActiveRequestsPanel />
|
||||
<RequestLoggerV2 key={requestLogKey} />
|
||||
</div>
|
||||
)}
|
||||
{activeTab === "proxy-logs" && <ProxyLogger />}
|
||||
{activeTab === "console" && <ConsoleLogViewer />}
|
||||
<div className="flex flex-col gap-6">
|
||||
<ActiveRequestsPanel />
|
||||
<RequestLoggerV2 key={requestLogKey} />
|
||||
</div>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={showCleanHistory}
|
||||
|
||||
@@ -10,7 +10,7 @@ import EngineTab from "./components/tabs/EngineTab";
|
||||
|
||||
type TabId = "memories" | "playground" | "engine";
|
||||
|
||||
const TABS: TabId[] = ["memories", "playground", "engine"];
|
||||
const TABS: TabId[] = ["memories", "engine", "playground"];
|
||||
|
||||
function MemoryPageContent() {
|
||||
const t = useTranslations("memory");
|
||||
|
||||
@@ -86,12 +86,8 @@ export default function SearchToolsTopBar({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{exportState && (
|
||||
<ExportCodeModal
|
||||
isOpen={exportOpen}
|
||||
onClose={() => setExportOpen(false)}
|
||||
state={exportState}
|
||||
/>
|
||||
{exportOpen && exportState != null && (
|
||||
<ExportCodeModal onClose={() => setExportOpen(false)} state={exportState} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -30,6 +30,7 @@ export default function Select({
|
||||
className,
|
||||
selectClassName,
|
||||
id: externalId,
|
||||
children,
|
||||
...props
|
||||
}: SelectProps) {
|
||||
const generatedId = useId();
|
||||
@@ -71,14 +72,18 @@ export default function Select({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<option value="" disabled className="bg-surface text-text-muted">
|
||||
{placeholder}
|
||||
</option>
|
||||
{options.map((option) => (
|
||||
<option key={option.value} value={option.value} className="bg-surface text-text-main">
|
||||
{option.label}
|
||||
{!children && placeholder && (
|
||||
<option value="" disabled className="bg-surface text-text-muted">
|
||||
{placeholder}
|
||||
</option>
|
||||
))}
|
||||
)}
|
||||
{!children &&
|
||||
options.map((option) => (
|
||||
<option key={option.value} value={option.value} className="bg-surface text-text-main">
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
{children}
|
||||
</select>
|
||||
<div
|
||||
className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-text-muted"
|
||||
|
||||
35
tests/unit/v388-phase1-screen-fixes.test.ts
Normal file
35
tests/unit/v388-phase1-screen-fixes.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Regression guards for v3.8.8 screen-fix Phase 1 (quick wins).
|
||||
const root = join(import.meta.dirname, "../..");
|
||||
const read = (p: string) => readFileSync(join(root, p), "utf8");
|
||||
|
||||
test("search-tools: export modal mounts on exportOpen (not by default) without invalid isOpen prop", () => {
|
||||
const src = read("src/app/(dashboard)/dashboard/search-tools/components/SearchToolsTopBar.tsx");
|
||||
assert.ok(src.includes("{exportOpen && exportState != null && ("), "modal guarded by exportOpen");
|
||||
assert.equal(/<ExportCodeModal[^>]*\bisOpen=/.test(src), false, "no invalid isOpen prop on ExportCodeModal");
|
||||
});
|
||||
|
||||
test("memory: tabs ordered memories -> engine -> playground", () => {
|
||||
const src = read("src/app/(dashboard)/dashboard/memory/page.tsx");
|
||||
assert.ok(src.includes('["memories", "engine", "playground"]'), "TABS order is memories, engine, playground");
|
||||
});
|
||||
|
||||
test("shared Select: renders children and guards placeholder/options when children present", () => {
|
||||
const src = read("src/shared/components/Select.tsx");
|
||||
assert.ok(src.includes("{children}"), "renders children passed by callers");
|
||||
assert.ok(src.includes("!children && placeholder"), "placeholder guarded by !children");
|
||||
assert.ok(src.includes("!children &&\n options.map") || src.includes("!children &&"), "options guarded by !children");
|
||||
});
|
||||
|
||||
test("logs: proxy/console tabs removed (dedicated menu pages exist)", () => {
|
||||
const src = read("src/app/(dashboard)/dashboard/logs/page.tsx");
|
||||
assert.equal(/value:\s*"proxy-logs"/.test(src), false, "proxy-logs tab removed");
|
||||
assert.equal(/value:\s*"console"/.test(src), false, "console tab removed");
|
||||
assert.equal(src.includes("<ProxyLogger"), false, "ProxyLogger not rendered here");
|
||||
assert.equal(src.includes("<ConsoleLogViewer"), false, "ConsoleLogViewer not rendered here");
|
||||
assert.equal(src.includes("SegmentedControl"), false, "SegmentedControl removed (single tab left)");
|
||||
});
|
||||
Reference in New Issue
Block a user