Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
531 changes: 270 additions & 261 deletions ui/app/workspace/dashboard/page.tsx

Large diffs are not rendered by default.

28 changes: 17 additions & 11 deletions ui/app/workspace/logs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { LogDetailSheet } from "@/app/workspace/logs/sheets/logDetailsSheet";
import { SessionDetailsSheet } from "@/app/workspace/logs/sheets/sessionDetailsSheet";
import { createColumns } from "@/app/workspace/logs/views/columns";
import { EmptyState } from "@/app/workspace/logs/views/emptyState";
import { LogsFilterSidebar } from "@/components/filters/logsFilterSidebar";
import { LogsDataTable } from "@/app/workspace/logs/views/logsTable";
import { LogsVolumeChart } from "@/app/workspace/logs/views/logsVolumeChart";
import FullPageLoader from "@/components/fullPageLoader";
Expand Down Expand Up @@ -224,12 +225,12 @@ export default function LogsPage() {
missing_cost_only: urlState.missing_cost_only,
metadata_filters: urlState.metadata_filters
? (() => {
try {
return JSON.parse(urlState.metadata_filters);
} catch {
return undefined;
}
})()
try {
return JSON.parse(urlState.metadata_filters);
} catch {
return undefined;
}
})()
: undefined,
}),
// Only re-derive filters when filter-related URL params change (not pagination)
Expand Down Expand Up @@ -294,7 +295,7 @@ export default function LogsPage() {
content_search: newFilters.content_search || "",
start_time: newFilters.start_time ? dateUtils.toUnixTimestamp(new Date(newFilters.start_time)) : undefined,
end_time: newFilters.end_time ? dateUtils.toUnixTimestamp(new Date(newFilters.end_time)) : undefined,
missing_cost_only: newFilters.missing_cost_only ?? filters.missing_cost_only ?? false,
missing_cost_only: newFilters.missing_cost_only ?? false,
metadata_filters: newFilters.metadata_filters ? JSON.stringify(newFilters.metadata_filters) : "",
offset: 0,
});
Expand Down Expand Up @@ -825,7 +826,8 @@ export default function LogsPage() {
title: "Success Rate",
value: fetchingStats ? <Skeleton className="h-8 w-16" /> : stats ? `${stats.success_rate.toFixed(2)}%` : "-",
icon: <CheckCircle className="size-4" />,
description: "Success rate as perceived by the system. Each fallback counts as a separate attempt. Retries on the same request are counted as one attempt.",
description:
"Success rate as perceived by the system. Each fallback counts as a separate attempt. Retries on the same request are counted as one attempt.",
},
{
title: "User Success Rate",
Expand Down Expand Up @@ -910,14 +912,18 @@ export default function LogsPage() {
);

return (
<div className="dark:bg-card h-[calc(100dvh-3.3rem)] max-h-[calc(100dvh-1.5rem)] bg-white">
<div className="dark:bg-card no-padding-parent no-border-parent h-[calc(100vh_-_16px)]">
{initialLoading ? (
<FullPageLoader />
) : showEmptyState ? (
<EmptyState isSocketConnected={isSocketConnected} error={error} />
) : (
<div className="mx-auto flex h-full w-full flex-col">
<div className="flex h-full flex-col gap-2 overflow-hidden">
<div className="bg-background flex h-full w-full grow gap-3">
{/* Sidebar Filters */}
<LogsFilterSidebar filters={filters} onFiltersChange={setFilters} />

{/* Main Content */}
<div className="bg-card flex min-w-0 flex-1 flex-col gap-2 overflow-hidden rounded-l-md p-4 pb-2">
<div className="grid shrink-0 grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-6">
{statCards.map((card) => (
<Card key={card.title} className="py-4 shadow-none">
Expand Down
2 changes: 1 addition & 1 deletion ui/app/workspace/logs/sheets/logDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ import {
} from "@/lib/constants/logs";
import { LogEntry } from "@/lib/types/logs";
import { Link } from "@tanstack/react-router";
import { Clipboard, Loader2, MoreVertical, Trash2 } from "lucide-react";
import { addMilliseconds, format } from "date-fns";
import { Clipboard, Loader2, MoreVertical, Trash2 } from "lucide-react";
import type { ReactNode } from "react";
import { toast } from "sonner";
import BlockHeader from "../views/blockHeader";
Expand Down
4 changes: 2 additions & 2 deletions ui/app/workspace/logs/views/emptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Button } from "@/components/ui/button";
import { CodeEditor } from "@/components/ui/codeEditor";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { getExampleBaseUrl } from "@/lib/utils/port";
import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
import { getExampleBaseUrl } from "@/lib/utils/port";
import { AlertTriangle, Copy } from "lucide-react";
import { useMemo, useState } from "react";

Expand Down Expand Up @@ -251,7 +251,7 @@ const result = await chain.invoke({ input: "What is LangChain?" });`,
</Alert>
)}

<div className="w-full space-y-6">
<div className="w-full space-y-6 p-4">
<div className="flex flex-row items-center gap-2">
<div>
<h3 className="text-lg font-semibold">Integrate under 60 seconds</h3>
Expand Down
60 changes: 12 additions & 48 deletions ui/app/workspace/logs/views/filters.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { FilterPopover } from "@/components/filters/filterPopover";
import { Button } from "@/components/ui/button";
import { Command, CommandItem, CommandList } from "@/components/ui/command";
import { DateTimePickerWithRange } from "@/components/ui/datePickerWithRange";
Expand All @@ -12,7 +11,6 @@ import { toast } from "sonner";

export { dateToRfc3339Local } from "@/lib/utils/date";

/** Predefined time periods for the logs date range picker (matches E2E test labels) */
const LOG_TIME_PERIODS = [
{ label: "Last hour", value: "1h" },
{ label: "Last 6 hours", value: "6h" },
Expand Down Expand Up @@ -62,6 +60,14 @@ export function LogFilters({ filters, onFiltersChange, liveEnabled, onLiveToggle
const filtersRef = useRef<LogFiltersType>(filters);
const [recalculateCosts] = useRecalculateLogCostsMutation();

const [startTime, setStartTime] = useState<Date | undefined>(filters.start_time ? new Date(filters.start_time) : undefined);
const [endTime, setEndTime] = useState<Date | undefined>(filters.end_time ? new Date(filters.end_time) : undefined);

useEffect(() => {
setStartTime(filters.start_time ? new Date(filters.start_time) : undefined);
setEndTime(filters.end_time ? new Date(filters.end_time) : undefined);
}, [filters.start_time, filters.end_time]);

// Keep filtersRef in sync so debounced search always merges with latest filters (search within filtered results)
useEffect(() => {
filtersRef.current = filters;
Expand All @@ -72,16 +78,6 @@ export function LogFilters({ filters, onFiltersChange, liveEnabled, onLiveToggle
setLocalSearch(filters.content_search || "");
}, [filters.content_search]);

// Convert ISO strings from filters to Date objects for the DateTimePicker
const [startTime, setStartTime] = useState<Date | undefined>(filters.start_time ? new Date(filters.start_time) : undefined);
const [endTime, setEndTime] = useState<Date | undefined>(filters.end_time ? new Date(filters.end_time) : undefined);

// Sync local date state when filters change from URL
useEffect(() => {
setStartTime(filters.start_time ? new Date(filters.start_time) : undefined);
setEndTime(filters.end_time ? new Date(filters.end_time) : undefined);
}, [filters.start_time, filters.end_time]);

// Cleanup timeout on unmount
useEffect(() => {
return () => {
Expand Down Expand Up @@ -123,31 +119,8 @@ export function LogFilters({ filters, onFiltersChange, liveEnabled, onLiveToggle
[onFiltersChange],
);

const handleFilterChange = useCallback(
(key: keyof LogFiltersType, values: string[] | boolean | string) => {
onFiltersChange({ ...filters, [key]: values });
},
[filters, onFiltersChange],
);

const handleMetadataFilterChange = useCallback(
(metadataKey: string, value: string | undefined) => {
const current = { ...(filters.metadata_filters || {}) };
if (value === undefined) {
delete current[metadataKey];
} else {
current[metadataKey] = value;
}
onFiltersChange({
...filters,
metadata_filters: Object.keys(current).length > 0 ? current : undefined,
});
},
[filters, onFiltersChange],
);

return (
<div className="flex items-center justify-between space-x-2">
<div className="flex grow items-center justify-between space-x-2">
<Button variant={"outline"} size="sm" className="h-7.5" onClick={() => onLiveToggle(!liveEnabled)}>
{liveEnabled ? (
<>
Expand All @@ -174,10 +147,7 @@ export function LogFilters({ filters, onFiltersChange, liveEnabled, onLiveToggle

<DateTimePickerWithRange
triggerTestId="filter-date-range"
dateTime={{
from: startTime,
to: endTime,
}}
dateTime={{ from: startTime, to: endTime }}
onDateTimeUpdate={(p) => {
setStartTime(p.from);
setEndTime(p.to);
Expand All @@ -200,15 +170,9 @@ export function LogFilters({ filters, onFiltersChange, liveEnabled, onLiveToggle
});
}}
/>
<FilterPopover
filters={filters}
onFilterChange={handleFilterChange}
onMetadataFilterChange={handleMetadataFilterChange}
showMissingCost
/>
<Popover open={openMoreActionsPopover} onOpenChange={setOpenMoreActionsPopover}>
<PopoverTrigger asChild>
<Button variant="outline" size="sm" className="h-7.5">
<Button variant="outline" size="sm" className="h-7.5 w-7.5">
<MoreVertical className="h-4 w-4" />
</Button>
</PopoverTrigger>
Expand All @@ -228,4 +192,4 @@ export function LogFilters({ filters, onFiltersChange, liveEnabled, onLiveToggle
</Popover>
</div>
);
}
}
2 changes: 1 addition & 1 deletion ui/app/workspace/logs/views/logsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export function LogsDataTable({
return (
<div className="flex h-full flex-col gap-2">
<div className="flex shrink-0 items-center gap-2">
<div className="flex-1">
<div className="flex flex-1 items-center gap-2">
<LogFiltersComponent
filters={filters}
onFiltersChange={onFiltersChange}
Expand Down
2 changes: 1 addition & 1 deletion ui/app/workspace/logs/views/logsVolumeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ export function LogsVolumeChart({
</div>
</div>
</div>
<div className="" style={{ height: "131px", marginTop: 4 }}>
<div className="" style={{ height: "128px", marginTop: 8 }}>
<Skeleton className="h-full w-full" />
</div>
</Card>
Expand Down
49 changes: 28 additions & 21 deletions ui/app/workspace/mcp-logs/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MCPFilterSidebar } from "@/components/filters/mcpFilterSidebar";
import FullPageLoader from "@/components/fullPageLoader";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Card, CardContent } from "@/components/ui/card";
Expand Down Expand Up @@ -516,29 +517,35 @@ export default function MCPLogsPage() {
}
/>
) : (
<div className="mx-auto w-full space-y-6">
<div className="space-y-6">
<div className="no-padding-parent no-border-parent bg-background flex h-[calc(100vh_-_16px)] w-full gap-3">
{/* Sidebar Filters */}
<MCPFilterSidebar filters={filters} onFiltersChange={setFilters} />

{/* Main Content */}
<div className="bg-card flex min-w-0 flex-1 flex-col gap-2 overflow-hidden rounded-l-md">
{/* Quick Stats */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-4">
{statCards.map((card) => (
<Card key={card.title} className="py-4 shadow-none">
<CardContent className="flex items-center justify-between px-4">
<div className="w-full min-w-0">
<div className="text-muted-foreground text-xs">{card.title}</div>
<div className="truncate font-mono text-xl font-medium sm:text-2xl">{card.value}</div>
</div>
</CardContent>
</Card>
))}
</div>
<div className="px-4">
<div className="grid shrink-0 grid-cols-1 gap-4 md:grid-cols-4">
{statCards.map((card) => (
<Card key={card.title} className="py-4 shadow-none">
<CardContent className="flex items-center justify-between px-4">
<div className="w-full min-w-0">
<div className="text-muted-foreground text-xs">{card.title}</div>
<div className="truncate font-mono text-xl font-medium sm:text-2xl">{card.value}</div>
</div>
</CardContent>
</Card>
))}
</div>

{/* Error Alert */}
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Error Alert */}
{error && (
<Alert variant="destructive" className="shrink-0">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
</div>

<MCPLogsDataTable
columns={columns}
Expand Down
Loading
Loading