import { useState, useEffect, useCallback, useRef } from "react";
import { X, RefreshCw, Activity, TrendingUp, CheckCircle, AlertTriangle } from "lucide-react";
import type { ProviderUsage, UsageWindow } from "../api";
import { useUsageData } from "../hooks/useUsageData";
import { ProviderIcon } from "./ProviderIcon";
interface UsageIndicatorProps {
isOpen: boolean;
onClose: () => void;
}
/**
* Format an ISO 8601 timestamp into a user-friendly absolute time string.
* Shows time like "2:30 PM" for today, "Tue 2:30 PM" for this week,
* or "Jan 15, 2:30 PM" for later dates.
*
* Used by UsageWindowRow to display the absolute reset time next to the
* relative "resets in X" text when the backend provides a canonical resetAt
* timestamp. Currently populated only for Claude session/windows where the
* reset timestamp is available from the API or CLI fallback parser.
*/
function formatResetAt(isoTimestamp: string): string {
const date = new Date(isoTimestamp);
const now = new Date();
const timeStr = date.toLocaleTimeString(undefined, {
hour: "numeric",
minute: "2-digit",
hour12: true,
});
const isToday = date.toDateString() === now.toDateString();
if (isToday) {
return timeStr;
}
// Check if within the next 7 days — show short weekday
const daysUntil = Math.round(
(date.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)
);
if (daysUntil > 0 && daysUntil <= 6) {
const weekday = date.toLocaleDateString(undefined, { weekday: "short" });
return `${weekday} ${timeStr}`;
}
// Beyond a week — show full date
const dateStr = date.toLocaleDateString(undefined, {
month: "short",
day: "numeric",
});
return `${dateStr}, ${timeStr}`;
}
/**
* Get color class for usage percentage
* - >90%: high (red/error color)
* - >70%: medium (yellow/triage color)
* - <=70%: low (green/success color)
*/
function getUsageColorClass(percentUsed: number): string {
if (percentUsed > 90) return "usage-progress-fill--high";
if (percentUsed > 70) return "usage-progress-fill--medium";
return "usage-progress-fill--low";
}
interface UsageWindowRowProps {
window: UsageWindow;
viewMode: 'used' | 'remaining';
providerName: string;
}
/**
* Single usage window row with progress bar
*/
function UsageWindowRow({ window, viewMode, providerName }: UsageWindowRowProps) {
const colorClass = getUsageColorClass(window.percentUsed);
const isRemainingMode = viewMode === 'remaining';
// Display percentage based on view mode, but color always based on actual usage
// Round percentages for cleaner display
const displayPercent = Math.round(isRemainingMode ? window.percentLeft : window.percentUsed);
const headerText = isRemainingMode ? `${Math.round(window.percentLeft)}% remaining` : `${Math.round(window.percentUsed)}% used`;
const footerText = isRemainingMode ? `${Math.round(window.percentUsed)}% used` : `${Math.round(window.percentLeft)}% left`;
// If resetText is null but resetAt exists, generate relative text from resetAt as a fallback
let displayResetText = window.resetText;
if (!displayResetText && window.resetAt) {
const msLeft = new Date(window.resetAt).getTime() - Date.now();
if (msLeft > 0) {
const hours = Math.floor(msLeft / (60 * 60 * 1000));
const days = Math.floor(hours / 24);
const remHours = hours % 24;
if (days > 0 && remHours > 0) {
displayResetText = `resets in ${days}d ${remHours}h`;
} else if (days > 0) {
displayResetText = `resets in ${days}d`;
} else if (hours > 0) {
displayResetText = `resets in ${hours}h`;
} else {
const mins = Math.floor(msLeft / (60 * 1000));
displayResetText = `resets in ${mins}m`;
}
}
}
// Use pace from backend if available (for weekly windows)
const pace = window.pace;
const shouldShowPace = pace !== undefined;
// Marker position for pace indicator (shows elapsed time position on progress bar)
let markerPosition = 0;
if (shouldShowPace) {
markerPosition = isRemainingMode ? (100 - pace.percentElapsed) : pace.percentElapsed;
}
// Determine pace display status
const isAhead = pace?.status === "ahead";
const isBehind = pace?.status === "behind";
const isOnTrack = pace?.status === "on-track";
return (
{window.label}{headerText}
{shouldShowPace && (
)}
{footerText}
{/* Reset group: shows relative text ("resets in 2h") and, when available,
the absolute reset time derived from the canonical resetAt timestamp.
The absolute time is populated by the backend for Claude session/windows
where the reset timestamp is known. Other providers will only show
the relative text unless they also provide resetAt. */}
{displayResetText && (
{displayResetText}
)}
{/* Absolute reset timestamp: shown for all windows when resetAt is available. */}
{window.resetAt && (
{formatResetAt(window.resetAt)}
)}