feat(usage): show Zed plan quota on the dashboard

Add a Zed usage handler so connected Zed accounts appear on
/dashboard/quota. Reads GET /client/users/me for plan, edit
predictions, optional hosted model requests and billing-cycle reset.

Render unlimited rows as "N used · Unlimited" instead of 0 / ∞, and
surface overdue-invoice / token-billing messages.
This commit is contained in:
Amir Seify
2026-08-28 15:52:51 +07:00
committed by decolua
parent 67d9182e1a
commit e5a13c3ab7
8 changed files with 459 additions and 12 deletions

View File

@@ -150,6 +150,7 @@ export default function QuotaTable({
<div className="space-y-px">
{currentPageRows.map((quota) => {
const isUnlimited = quota.unlimited === true;
const colors = getColorClasses(quota.remaining);
const countdown = formatResetTime(quota.resetAt);
const resetDisplay = formatResetTimeDisplay(quota.resetAt);
@@ -174,6 +175,7 @@ export default function QuotaTable({
{/* Progress + used/total */}
<div className={`min-w-0 flex-1 ${compact ? "space-y-1" : "space-y-1.5"}`}>
{!isUnlimited && (
<div className={`${compact ? "h-1" : "h-1.5"} rounded-full overflow-hidden border ${colors.bgLight} ${
quota.remaining === 0 ? "border-black/10 dark:border-white/10" : "border-transparent"
}`}>
@@ -182,16 +184,23 @@ export default function QuotaTable({
style={{ width: `${Math.min(quota.remaining, 100)}%` }}
/>
</div>
)}
<div className={`flex items-center justify-between gap-1 min-w-0 ${compact ? "text-[10px]" : "text-xs"}`}>
<span
className="text-text-muted truncate"
title={`${quota.used.toLocaleString()} / ${quota.total > 0 ? quota.total.toLocaleString() : "∞"}`}
title={
isUnlimited
? `${quota.used.toLocaleString()} used · Unlimited`
: `${quota.used.toLocaleString()} / ${quota.total > 0 ? quota.total.toLocaleString() : "∞"}`
}
>
{quota.used.toLocaleString()} / {quota.total > 0 ? quota.total.toLocaleString() : "∞"}
{isUnlimited
? `${quota.used.toLocaleString()} used · Unlimited`
: `${quota.used.toLocaleString()} / ${quota.total > 0 ? quota.total.toLocaleString() : "∞"}`}
</span>
<span className={`font-medium ${colors.text} shrink-0`}>
{quota.remaining}%
<span className={`font-medium ${isUnlimited ? "text-green-600 dark:text-green-400" : colors.text} shrink-0`}>
{isUnlimited ? "Unlimited" : `${quota.remaining}%`}
</span>
</div>
</div>

View File

@@ -1265,6 +1265,11 @@ export default function ProviderLimits() {
onHideQuota={(quotaRow) => handleHideQuota(conn.provider, quotaRow)}
/>
)}
{quota?.message && !error && !isLoading && (
<p className="mt-2 px-1 text-[10px] leading-relaxed text-text-muted">
{quota.message}
</p>
)}
{hiddenQuotaRows.length > 0 && (
<div className="mt-2 flex min-w-0 items-center gap-1 border-t border-black/5 pt-2 text-[10px] text-text-muted dark:border-white/5">
<span className="material-symbols-outlined shrink-0 text-[14px]">

View File

@@ -558,6 +558,22 @@ export function parseQuotaData(provider, data) {
}
break;
case "zed":
// Edit predictions + optional hosted model_requests; unlimited uses remainingPercentage.
if (data.quotas) {
Object.entries(data.quotas).forEach(([name, quota]) => {
normalizedQuotas.push({
name,
used: quota.used || 0,
total: quota.total || 0,
resetAt: quota.resetAt || null,
remainingPercentage: quota.remainingPercentage,
unlimited: quota.unlimited,
});
});
}
break;
default:
// Generic fallback for unknown providers
if (data.quotas) {