[verified] refactor: unify dashboard rate windows

This commit is contained in:
2026-05-09 16:54:32 +09:00
parent c763926b32
commit 1fb40fd5fe
6 changed files with 445 additions and 64 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@@ -30,7 +30,7 @@ import {
import { toast, Toaster } from 'sonner'; import { toast, Toaster } from 'sonner';
import { api } from '@/lib/api'; import { api } from '@/lib/api';
import { clearToken, getToken, setToken } from '@/lib/storage'; import { clearToken, getToken, setToken } from '@/lib/storage';
import { flattenNumericMetrics, formatDate, titleizeMetric } from '@/lib/utils'; import { summarizeUsageWindows, formatDate, formatDurationSeconds } from '@/lib/utils';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Card, Card,
@@ -568,10 +568,6 @@ function Dashboard() {
onError: (error: Error) => toast.error(error.message), onError: (error: Error) => toast.error(error.message),
}); });
const metricCards = useMemo(() => {
return flattenNumericMetrics(summaryQuery.data?.aggregatedUsage).slice(0, 6);
}, [summaryQuery.data?.aggregatedUsage]);
if (summaryQuery.isLoading || userQuery.isLoading) { if (summaryQuery.isLoading || userQuery.isLoading) {
return ( return (
<div className="flex min-h-screen items-center justify-center text-slate-300"> <div className="flex min-h-screen items-center justify-center text-slate-300">
@@ -601,12 +597,27 @@ function Dashboard() {
const summary = summaryQuery.data!; const summary = summaryQuery.data!;
const user = userQuery.data!; const user = userQuery.data!;
const firstMetric = metricCards[0]?.value ?? 0; const usageWindows = summarizeUsageWindows(
const secondMetric = metricCards[1]?.value ?? 0; summary.accounts.map((account) => account.usage),
const progressValue = );
firstMetric + secondMetric > 0 const windowCards = [
? (firstMetric / (firstMetric + secondMetric)) * 100 {
: 0; title: 'Primary window',
tone: 'text-sky-300',
window: usageWindows.primary,
},
{
title: 'Secondary window',
tone: 'text-violet-300',
window: usageWindows.secondary,
},
].filter(
(item): item is {
title: string;
tone: string;
window: NonNullable<typeof usageWindows.primary>;
} => item.window !== null,
);
return ( return (
<div className="mx-auto min-h-screen max-w-7xl px-4 py-5 sm:px-6 lg:px-8"> <div className="mx-auto min-h-screen max-w-7xl px-4 py-5 sm:px-6 lg:px-8">
@@ -647,25 +658,53 @@ function Dashboard() {
<CardTitle>Unified capacity</CardTitle> <CardTitle>Unified capacity</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div className="flex items-end justify-between gap-4"> {windowCards.length > 0 ? (
<div className="grid gap-4 md:grid-cols-2">
{windowCards.map((item) => {
const usedPercent = item.window.usedPercent;
const progressValue =
usedPercent !== null && usedPercent !== undefined
? Math.max(0, Math.min(100, usedPercent))
: 0;
return (
<div
key={item.title}
className="rounded-2xl border border-white/10 bg-white/4 p-4"
>
<div className="flex items-start justify-between gap-4">
<div> <div>
<div className="text-4xl font-semibold text-white"> <div className="text-sm text-slate-400">{item.title}</div>
{firstMetric.toLocaleString()} <div className={`mt-2 text-3xl font-semibold ${item.tone}`}>
{usedPercent !== null && usedPercent !== undefined
? `${usedPercent.toFixed(0)}%`
: '—'}
</div> </div>
<div className="mt-1 text-sm text-slate-400"> <div className="mt-1 text-xs uppercase tracking-[0.2em] text-slate-500">
{titleizeMetric(metricCards[0]?.label ?? 'Primary metric')} {item.window.accountCount
? `Window data from ${item.window.accountCount} account${item.window.accountCount === 1 ? '' : 's'}`
: 'Used'}
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-right text-sm text-slate-400">
<div className="text-2xl font-semibold text-slate-100"> {item.window.limitWindowSeconds !== null ? (
{secondMetric.toLocaleString()} <div>{formatDurationSeconds(item.window.limitWindowSeconds)}</div>
</div> ) : null}
<div className="mt-1 text-sm text-slate-500"> <div className={item.window.limitWindowSeconds !== null ? 'mt-1' : ''}>
{titleizeMetric(metricCards[1]?.label ?? 'Secondary metric')} Resets {formatDate(item.window.resetAt)}
</div> </div>
</div> </div>
</div> </div>
<Progress value={progressValue} /> <Progress value={progressValue} className="mt-4" />
</div>
);
})}
</div>
) : (
<div className="rounded-2xl border border-dashed border-white/10 bg-white/3 p-6 text-sm text-slate-400">
No rate-limit window data yet. Connect an OpenAI account and refresh to load Codex usage windows.
</div>
)}
<div className="flex flex-wrap gap-3 text-sm text-slate-400"> <div className="flex flex-wrap gap-3 text-sm text-slate-400">
<span>Accounts: {summary.totals.totalAccounts}</span> <span>Accounts: {summary.totals.totalAccounts}</span>
<span>Healthy: {summary.totals.activeAccounts}</span> <span>Healthy: {summary.totals.activeAccounts}</span>
@@ -705,35 +744,6 @@ function Dashboard() {
</div> </div>
</div> </div>
<Card className="mt-6 min-w-0">
<CardHeader>
<CardTitle>Usage metrics</CardTitle>
</CardHeader>
<CardContent className="min-w-0">
{metricCards.length === 0 ? (
<div className="rounded-2xl border border-dashed border-white/10 bg-white/3 p-6 text-sm text-slate-400">
No usage data yet. Connect an OpenAI account and complete the sign-in flow to start refreshing.
</div>
) : (
<div className="grid gap-3 sm:grid-cols-2">
{metricCards.map((metric) => (
<div
key={metric.label}
className="min-w-0 rounded-2xl border border-white/10 bg-white/4 p-4"
>
<div className="text-sm text-slate-400 break-words">
{titleizeMetric(metric.label)}
</div>
<div className="mt-3 text-2xl font-semibold text-white">
{metric.value.toLocaleString()}
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
<Card className="mt-6"> <Card className="mt-6">
<CardHeader> <CardHeader>
<CardTitle>Connected OpenAI accounts</CardTitle> <CardTitle>Connected OpenAI accounts</CardTitle>

View File

@@ -13,6 +13,181 @@ export function formatDate(value: string | null) {
}).format(new Date(value)); }).format(new Date(value));
} }
type UsageWindowSummary = {
usedPercent: number | null;
limitWindowSeconds: number | null;
resetAt: string | null;
};
type AggregatedUsageWindowSummary = UsageWindowSummary & {
accountCount: number;
};
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function toFiniteNumber(value: unknown) {
return typeof value === 'number' && Number.isFinite(value) ? value : null;
}
function getPath(value: unknown, path: string[]) {
let current: unknown = value;
for (const segment of path) {
if (!isRecord(current) || !(segment in current)) {
return null;
}
current = current[segment];
}
return current;
}
function normalizeResetAt(value: unknown) {
if (typeof value === 'string' && value.trim()) {
return value;
}
if (typeof value === 'number' && Number.isFinite(value)) {
return new Date(value * 1000).toISOString();
}
return null;
}
function extractWindow(value: unknown, paths: string[][]): UsageWindowSummary | null {
for (const path of paths) {
const windowValue = getPath(value, path);
if (!isRecord(windowValue)) {
continue;
}
const summary = {
usedPercent: toFiniteNumber(windowValue.used_percent),
limitWindowSeconds:
toFiniteNumber(windowValue.limit_window_seconds) ??
(toFiniteNumber(windowValue.window_minutes) !== null
? toFiniteNumber(windowValue.window_minutes)! * 60
: null),
resetAt: normalizeResetAt(windowValue.reset_at ?? windowValue.resets_at),
};
if (
summary.usedPercent === null &&
summary.limitWindowSeconds === null &&
summary.resetAt === null
) {
continue;
}
return summary;
}
return null;
}
export function extractUsageWindows(value: unknown) {
return {
primary: extractWindow(value, [
['rate_limit', 'primary_window'],
['rate_limits', 'primary'],
['primary_window'],
]),
secondary: extractWindow(value, [
['rate_limit', 'secondary_window'],
['rate_limits', 'secondary'],
['secondary_window'],
]),
};
}
function average(values: number[]) {
if (values.length === 0) {
return null;
}
return values.reduce((sum, value) => sum + value, 0) / values.length;
}
function earliestDate(values: Array<string | null>) {
const validDates = values
.filter((value): value is string => Boolean(value))
.map((value) => ({ value, timestamp: Date.parse(value) }))
.filter((value) => Number.isFinite(value.timestamp))
.sort((left, right) => left.timestamp - right.timestamp);
return validDates[0]?.value ?? null;
}
function summarizeWindowCollection(
windows: Array<UsageWindowSummary | null>,
): AggregatedUsageWindowSummary | null {
const presentWindows = windows.filter(
(window): window is UsageWindowSummary => window !== null,
);
if (presentWindows.length === 0) {
return null;
}
const usedPercentValues = presentWindows
.map((window) => window.usedPercent)
.filter((value): value is number => value !== null);
const limitWindowValues = presentWindows
.map((window) => window.limitWindowSeconds)
.filter((value): value is number => value !== null);
const uniqueLimitWindowValues = [...new Set(limitWindowValues)];
return {
accountCount: presentWindows.length,
usedPercent: average(usedPercentValues),
limitWindowSeconds:
limitWindowValues.length === presentWindows.length &&
uniqueLimitWindowValues.length === 1
? uniqueLimitWindowValues[0]
: null,
resetAt: earliestDate(presentWindows.map((window) => window.resetAt)),
};
}
export function summarizeUsageWindows(values: Array<unknown>) {
const extractedWindows = values.map((value) => extractUsageWindows(value));
return {
primary: summarizeWindowCollection(
extractedWindows.map((windows) => windows.primary),
),
secondary: summarizeWindowCollection(
extractedWindows.map((windows) => windows.secondary),
),
};
}
export function formatDurationSeconds(value: number | null) {
if (!value || value <= 0) {
return 'Unknown window';
}
if (value % 86_400 === 0) {
const days = value / 86_400;
return `${days}d window`;
}
if (value % 3_600 === 0) {
const hours = value / 3_600;
return `${hours}h window`;
}
if (value % 60 === 0) {
const minutes = value / 60;
return `${minutes}m window`;
}
return `${value}s window`;
}
export function flattenNumericMetrics( export function flattenNumericMetrics(
value: unknown, value: unknown,
path: string[] = [], path: string[] = [],

View File

@@ -0,0 +1,20 @@
import { describe, expect, test } from 'bun:test';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8');
describe('dashboard unified capacity windows', () => {
test('shows primary and secondary windows inside unified capacity and removes usage metrics card', () => {
expect(appSource).toContain('<CardTitle>Unified capacity</CardTitle>');
expect(appSource).toContain('Primary window');
expect(appSource).toContain('Secondary window');
expect(appSource).toContain('const windowCards = [');
expect(appSource).toContain("} => item.window !== null,");
expect(appSource).toContain('item.window.limitWindowSeconds !== null ? (');
expect(appSource).not.toContain('<CardTitle>Usage metrics</CardTitle>');
expect(appSource).not.toContain('flattenNumericMetrics(summaryQuery.data?.aggregatedUsage).slice(0, 6)');
expect(appSource).not.toContain('const firstMetric = metricCards[0]?.value ?? 0;');
expect(appSource).not.toContain('const secondMetric = metricCards[1]?.value ?? 0;');
});
});

View File

@@ -7,8 +7,10 @@ const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8');
describe('dashboard card copy', () => { describe('dashboard card copy', () => {
test('removes verbose dashboard card descriptions and keeps concise labels', () => { test('removes verbose dashboard card descriptions and keeps concise labels', () => {
expect(appSource).toContain('<CardTitle>Unified capacity</CardTitle>'); expect(appSource).toContain('<CardTitle>Unified capacity</CardTitle>');
expect(appSource).toContain('<CardTitle>Usage metrics</CardTitle>');
expect(appSource).toContain('<CardTitle>Connected OpenAI accounts</CardTitle>'); expect(appSource).toContain('<CardTitle>Connected OpenAI accounts</CardTitle>');
expect(appSource).toContain('Primary window');
expect(appSource).toContain('Secondary window');
expect(appSource).not.toContain('<CardTitle>Usage metrics</CardTitle>');
expect(appSource).toContain(">Merged by default. Inspect each account below.<"); expect(appSource).toContain(">Merged by default. Inspect each account below.<");
expect(appSource).not.toContain( expect(appSource).not.toContain(
'Fast glance card for the first two numeric metrics extracted from the merged usage payload.', 'Fast glance card for the first two numeric metrics extracted from the merged usage payload.',

View File

@@ -5,11 +5,10 @@ import { join } from 'node:path';
const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8'); const appSource = readFileSync(join(import.meta.dir, '../src/App.tsx'), 'utf8');
describe('mobile overflow guards', () => { describe('mobile overflow guards', () => {
test('usage metrics cards allow long metric labels to wrap on mobile', () => { test('unified capacity window cards stay in a responsive two-column grid', () => {
expect(appSource).toContain('className="mt-6 min-w-0"'); expect(appSource).toContain('className="grid gap-4 md:grid-cols-2"');
expect(appSource).toContain('className="grid gap-3 sm:grid-cols-2"'); expect(appSource).toContain('className="rounded-2xl border border-white/10 bg-white/4 p-4"');
expect(appSource).toContain('className="min-w-0 rounded-2xl border border-white/10 bg-white/4 p-4"'); expect(appSource).toContain('className="text-sm text-slate-400">{item.title}</div>');
expect(appSource).toContain('className="text-sm text-slate-400 break-words"');
}); });
test('connected account tabs no longer render a side-by-side payload column', () => { test('connected account tabs no longer render a side-by-side payload column', () => {

View File

@@ -0,0 +1,175 @@
import { describe, expect, test } from 'bun:test';
import {
extractUsageWindows,
formatDurationSeconds,
summarizeUsageWindows,
} from '../src/lib/utils';
describe('usage window helpers', () => {
test('extracts primary and secondary windows from codex rate_limit payloads', () => {
const windows = extractUsageWindows({
rate_limit: {
primary_window: {
used_percent: 14,
limit_window_seconds: 18_000,
reset_at: '2026-05-09T12:00:00.000Z',
},
secondary_window: {
used_percent: 19,
limit_window_seconds: 604_800,
reset_at: '2026-05-16T12:00:00.000Z',
},
},
});
expect(windows).toEqual({
primary: {
usedPercent: 14,
limitWindowSeconds: 18_000,
resetAt: '2026-05-09T12:00:00.000Z',
},
secondary: {
usedPercent: 19,
limitWindowSeconds: 604_800,
resetAt: '2026-05-16T12:00:00.000Z',
},
});
});
test('formats canonical codex limit windows into readable labels', () => {
expect(formatDurationSeconds(18_000)).toBe('5h window');
expect(formatDurationSeconds(604_800)).toBe('7d window');
expect(formatDurationSeconds(null)).toBe('Unknown window');
});
test('summarizes windows across multiple account payloads without summing percentages', () => {
const windows = summarizeUsageWindows([
{
rate_limit: {
primary_window: {
used_percent: 20,
limit_window_seconds: 18_000,
reset_at: '2026-05-09T12:00:00.000Z',
},
secondary_window: {
used_percent: 40,
limit_window_seconds: 604_800,
reset_at: '2026-05-16T12:00:00.000Z',
},
},
},
{
rate_limit: {
primary_window: {
used_percent: 60,
limit_window_seconds: 18_000,
reset_at: '2026-05-09T10:00:00.000Z',
},
secondary_window: {
used_percent: 80,
limit_window_seconds: 604_800,
reset_at: '2026-05-15T10:00:00.000Z',
},
},
},
]);
expect(windows).toEqual({
primary: {
accountCount: 2,
usedPercent: 40,
limitWindowSeconds: 18_000,
resetAt: '2026-05-09T10:00:00.000Z',
},
secondary: {
accountCount: 2,
usedPercent: 60,
limitWindowSeconds: 604_800,
resetAt: '2026-05-15T10:00:00.000Z',
},
});
});
test('keeps window summaries null-safe for partial or inconsistent account data', () => {
const windows = summarizeUsageWindows([
{
rate_limit: {
primary_window: {
limit_window_seconds: 18_000,
reset_at: '2026-05-09T12:00:00.000Z',
},
},
},
{
rate_limit: {
primary_window: {
used_percent: 50,
reset_at: '2026-05-09T11:00:00.000Z',
},
secondary_window: {
used_percent: 22,
limit_window_seconds: 604_800,
reset_at: '2026-05-16T12:00:00.000Z',
},
},
},
{
rate_limit: {
secondary_window: {
used_percent: 44,
limit_window_seconds: 86_400,
reset_at: '2026-05-15T12:00:00.000Z',
},
},
},
]);
expect(windows).toEqual({
primary: {
accountCount: 2,
usedPercent: 50,
limitWindowSeconds: null,
resetAt: '2026-05-09T11:00:00.000Z',
},
secondary: {
accountCount: 2,
usedPercent: 33,
limitWindowSeconds: null,
resetAt: '2026-05-15T12:00:00.000Z',
},
});
});
test('ignores empty window shells with no meaningful values', () => {
expect(
extractUsageWindows({
rate_limit: {
primary_window: {},
secondary_window: null,
},
}),
).toEqual({ primary: null, secondary: null });
});
test('falls back to later candidate paths when earlier window shells are empty', () => {
expect(
extractUsageWindows({
rate_limit: {
primary_window: {},
},
primary_window: {
used_percent: 42,
limit_window_seconds: 18_000,
reset_at: '2026-05-09T12:00:00.000Z',
},
}),
).toEqual({
primary: {
usedPercent: 42,
limitWindowSeconds: 18_000,
resetAt: '2026-05-09T12:00:00.000Z',
},
secondary: null,
});
});
});