import { useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import {
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query';
import type {
AuthResponse,
CompleteCodexManualLoginInput,
LoginInput,
RegisterInput,
StartCodexLoginInput,
} from '@codexdash/shared-types';
import {
Activity,
CirclePlus,
ExternalLink,
Gauge,
Link as LinkIcon,
LoaderCircle,
LogOut,
RefreshCw,
ShieldCheck,
Trash2,
Waypoints,
} from 'lucide-react';
import { toast, Toaster } from 'sonner';
import { api } from '@/lib/api';
import { clearToken, getToken, setToken } from '@/lib/storage';
import {
extractUsageWindows,
formatDate,
formatDurationSeconds,
getFastestResetAt,
getUsageProgressTone,
summarizeUsageWindows,
} from '@/lib/utils';
import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import { Progress } from '@/components/ui/progress';
import { Separator } from '@/components/ui/separator';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
const registerSchema = z.object({
name: z.string().min(2),
email: z.email(),
password: z.string().min(8),
});
const loginSchema = z.object({
email: z.email(),
password: z.string().min(8),
});
const connectSchema = z.object({
label: z.string().min(2),
emailHint: z.string().optional(),
});
const manualCallbackSchema = z.object({
callbackUrl: z.string().min(10),
});
function AuthShell({
onAuthenticated,
}: {
onAuthenticated: (response: AuthResponse) => void;
}) {
const [mode, setMode] = useState<'login' | 'register'>('register');
const schema = mode === 'register' ? registerSchema : loginSchema;
const form = useForm<{ name?: string; email: string; password: string }>({
resolver: zodResolver(schema),
defaultValues: {
email: '',
password: '',
...(mode === 'register' ? { name: '' } : {}),
},
});
const mutation = useMutation({
mutationFn: async (values: {
name?: string;
email: string;
password: string;
}) => {
return mode === 'register'
? api.register(values as RegisterInput)
: api.login({
email: values.email,
password: values.password,
} as LoginInput);
},
onSuccess: (response) => {
setToken(response.token);
onAuthenticated(response);
toast.success(
mode === 'register'
? 'Welcome to CodexDash.'
: 'Signed in successfully.',
);
},
onError: (error: Error) => toast.error(error.message),
});
return (
Mobile-first Codex monitor
CodexDash keeps every Codex account in one gorgeous live dashboard.
Sign into CodexDash, connect multiple OpenAI Codex accounts through a real login flow, and view combined limits,
remaining usage, raw API payloads, and per-account drilldowns from a single responsive UI.
{[
{
icon: Gauge,
title: 'Unified usage',
desc: 'Merge multiple OpenAI accounts into one overview.',
},
{
icon: ShieldCheck,
title: 'Stored safely',
desc: 'OAuth session data is encrypted at rest.',
},
{
icon: Activity,
title: 'Live detail',
desc: 'See refreshed usage plus raw usage payloads.',
},
].map((item) => (
))}
{mode === 'register' ? 'Create your account' : 'Welcome back'}
{mode === 'register'
? 'Start with your CodexDash account, then connect OpenAI Codex logins inside the dashboard.'
: 'Log in to continue monitoring your combined Codex usage.'}
OpenAI account connection now uses a real sign-in flow based on the Codex client OAuth pattern.
After you click connect, CodexDash opens OpenAI login in a popup and can also finish from a pasted callback URL if localhost is unavailable.
setMode(mode === 'register' ? 'login' : 'register')
}
>
{mode === 'register'
? 'Already have an account? Sign in'
: 'Need an account? Register'}
);
}
function ConnectAccountDialog() {
const queryClient = useQueryClient();
const [open, setOpen] = useState(false);
const [attemptId, setAttemptId] = useState(null);
const [authorizeUrl, setAuthorizeUrl] = useState(null);
const popupRef = useRef(null);
const handledAttemptStatusRef = useRef(null);
const form = useForm>({
resolver: zodResolver(connectSchema),
defaultValues: { label: '', emailHint: '' },
});
const manualCallbackForm = useForm>({
resolver: zodResolver(manualCallbackSchema),
defaultValues: { callbackUrl: '' },
});
const startMutation = useMutation({
mutationFn: api.startCodexLogin,
onSuccess: (response) => {
setAttemptId(response.attemptId);
setAuthorizeUrl(response.authorizeUrl);
manualCallbackForm.reset();
popupRef.current = window.open(
response.authorizeUrl,
'codexdash-openai-login',
'popup=yes,width=520,height=760',
);
if (!popupRef.current) {
toast.error('Popup was blocked. Use the fallback link inside the dialog.');
} else {
toast.success('Continue the OpenAI sign-in flow in the popup.');
}
},
onError: (error: Error) => toast.error(error.message),
});
const manualCompleteMutation = useMutation({
mutationFn: ({
callbackUrl,
currentAttemptId,
}: CompleteCodexManualLoginInput & { currentAttemptId: string }) =>
api.completeCodexManualLogin(currentAttemptId, { callbackUrl }),
onSuccess: () => {
toast.success('Processing the pasted OpenAI callback URL…');
void attemptQuery.refetch();
},
onError: (error: Error) => toast.error(error.message),
});
const attemptQuery = useQuery({
enabled: Boolean(attemptId),
queryKey: ['codex-login-attempt', attemptId],
queryFn: () => api.getCodexLoginAttempt(attemptId as string),
refetchInterval: (query) =>
query.state.data?.status === 'pending' ? 2_000 : false,
});
const cancelMutation = useMutation({
mutationFn: api.cancelCodexLoginAttempt,
onSuccess: () => {
toast.success('Login attempt cancelled.');
setAttemptId(null);
setAuthorizeUrl(null);
manualCallbackForm.reset();
popupRef.current?.close();
},
onError: (error: Error) => toast.error(error.message),
});
useEffect(() => {
const attempt = attemptQuery.data;
if (!attempt) {
return;
}
const statusKey = `${attempt.id}:${attempt.status}:${attempt.completedAt ?? ''}:${attempt.lastError ?? ''}`;
if (handledAttemptStatusRef.current === statusKey) {
return;
}
if (attempt.status === 'completed') {
handledAttemptStatusRef.current = statusKey;
window.setTimeout(() => {
toast.success('OpenAI Codex account connected.');
setOpen(false);
setAttemptId(null);
setAuthorizeUrl(null);
form.reset();
manualCallbackForm.reset();
popupRef.current?.close();
void queryClient.invalidateQueries({ queryKey: ['usage-summary'] });
}, 0);
return;
}
if (attempt.status === 'error' || attempt.status === 'expired') {
handledAttemptStatusRef.current = statusKey;
toast.error(attempt.lastError || 'OpenAI login failed.');
}
}, [attemptQuery.data, form, manualCallbackForm, queryClient]);
useEffect(() => {
function onMessage(event: MessageEvent) {
if (event.data?.type !== 'codexdash:oauth-complete') {
return;
}
if (event.data?.attemptId && event.data.attemptId === attemptId) {
void attemptQuery.refetch();
}
}
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
}, [attemptId, attemptQuery]);
const attempt = attemptQuery.data;
const isPendingAttempt = attempt?.status === 'pending';
return (
{
setOpen(next);
if (!next && isPendingAttempt && attemptId) {
cancelMutation.mutate(attemptId);
}
}}
>
Connect OpenAI account
Connect an OpenAI Codex account
Start a real OpenAI sign-in flow. CodexDash opens the official login,
completes the Codex-style OAuth callback locally, then stores the
encrypted session for future usage refreshes.
{attemptId ? (
Waiting for OpenAI login
{attempt?.status === 'completed'
? 'The login finished successfully. Closing this dialog…'
: 'Finish the sign-in flow in the popup window. CodexDash will detect the callback automatically.'}
Expires: {formatDate(attempt?.expiresAt ?? null)}
{authorizeUrl ? (
{
popupRef.current = window.open(
authorizeUrl,
'codexdash-openai-login',
'popup=yes,width=520,height=760',
);
}}
>
Re-open login popup
Open login in new tab
) : null}
Manual fallback
If localhost:1455 is not listening, OpenAI may finish on a browser error page.
Copy the full URL from the address bar and paste it below to complete the login manually.
{attempt?.lastError ? (
{attempt.lastError}
) : null}
void attemptQuery.refetch()}
>
Check status
attemptId && cancelMutation.mutate(attemptId)}
>
Cancel
) : (
)}
);
}
function Dashboard() {
const queryClient = useQueryClient();
const summaryQuery = useQuery({
queryKey: ['usage-summary'],
queryFn: () => api.getUsageSummary(true),
});
const userQuery = useQuery({ queryKey: ['me'], queryFn: api.me });
const deleteMutation = useMutation({
mutationFn: api.deleteAccount,
onSuccess: () => {
toast.success('OpenAI account removed.');
void queryClient.invalidateQueries({ queryKey: ['usage-summary'] });
},
onError: (error: Error) => toast.error(error.message),
});
if (summaryQuery.isLoading || userQuery.isLoading) {
return (
Loading CodexDash…
);
}
if (summaryQuery.isError || userQuery.isError) {
return (
Unable to load dashboard
{(summaryQuery.error as Error | undefined)?.message ??
(userQuery.error as Error | undefined)?.message}
window.location.reload()}>Retry
);
}
const summary = summaryQuery.data!;
const user = userQuery.data!;
const usageWindows = summarizeUsageWindows(
summary.accounts.map((account) => account.usage),
);
const windowCards = [
{
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;
} => item.window !== null,
);
const fastestResetAt = getFastestResetAt(
windowCards.map((item) => item.window.resetAt),
);
const accountCapacityItems = summary.accounts.map((account) => {
const accountWindows = extractUsageWindows(account.usage);
const accountCapacityBars = [accountWindows.primary, accountWindows.secondary].filter(
(window): window is NonNullable => window !== null,
);
return {
account,
accountCapacityBars,
};
});
return (
Signed in as {user.name}
CodexDash overview
Combined usage is refreshed by calling Codex usage endpoints for each attached OpenAI account and merging numeric fields into one dashboard.
summaryQuery.refetch()}>
Refresh now
{
clearToken();
window.location.reload();
}}
>
Sign out
Unified capacity
{windowCards.length > 0 ? (
{windowCards.map((item) => {
const usedPercent = item.window.usedPercent;
const progressValue =
usedPercent !== null && usedPercent !== undefined
? Math.max(0, Math.min(100, usedPercent))
: 0;
return (
{item.title}
{usedPercent !== null && usedPercent !== undefined
? `${usedPercent.toFixed(0)}%`
: '—'}
{item.window.limitWindowSeconds !== null ? (
{formatDurationSeconds(item.window.limitWindowSeconds)}
) : null}
Replenishes at {formatDate(item.window.resetAt)}
);
})}
) : (
No rate-limit window data yet. Connect an OpenAI account and refresh to load Codex usage windows.
)}
{fastestResetAt ? (
Earliest replenishment {formatDate(fastestResetAt)}
) : null}
Accounts: {summary.totals.totalAccounts}
Healthy: {summary.totals.activeAccounts}
Errors: {summary.totals.erroredAccounts}
Updated: {formatDate(summary.refreshedAt)}
{[
{
title: 'Connected sessions',
value: summary.totals.totalAccounts,
tone: 'text-sky-300',
},
{
title: 'Healthy sessions',
value: summary.totals.activeAccounts,
tone: 'text-emerald-300',
},
{
title: 'Errored sessions',
value: summary.totals.erroredAccounts,
tone: 'text-rose-300',
},
].map((item) => (
{item.title}
{item.value.toLocaleString()}
))}
Connected OpenAI accounts
Merged by default. Inspect each account below.
{summary.accounts.length === 0 ? (
No OpenAI accounts connected yet.
) : (
{summary.accounts.map((account) => (
{account.label}
))}
{summary.accounts.map((account) => (
{account.label}
{account.providerEmail ||
account.emailHint ||
'No email available yet'}
{account.status}
Auth: {account.authType}
Plan: {account.planType || 'Unknown'}
Provider account:{' '}
{account.providerAccountId || 'Unknown'}
Session expires: {formatDate(account.sessionExpiresAt)}
Last synced: {formatDate(account.lastSyncedAt)}
Connected: {formatDate(account.createdAt)}
Error:{' '}
{account.lastError || 'None'}
summaryQuery.refetch()}
>
Refresh
deleteMutation.mutate(account.id)}
>
Remove
))}
)}
Per-account capacity
Compact rate-limit bars for each connected OpenAI account.
{accountCapacityItems.length === 0 ? (
No account capacity data yet.
) : (
{accountCapacityItems.map(({ account, accountCapacityBars }) => (
{account.label}
{account.providerEmail || account.emailHint || 'No email available yet'}
{account.status}
{accountCapacityBars.length > 0 ? (
{accountCapacityBars.map((window, index) => {
const progressValue =
window.usedPercent !== null
? Math.max(0, Math.min(100, window.usedPercent))
: 0;
return (
{window.usedPercent !== null
? `${window.usedPercent.toFixed(0)}%`
: '—'}
);
})}
) : (
No window data yet.
)}
))}
)}
);
}
export default function App() {
const [authenticated, setAuthenticated] = useState(Boolean(getToken()));
return (
{authenticated ? (
) : (
setAuthenticated(true)} />
)}
);
}