feat: unified credits and its cost calculations

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-05-02 14:34:23 -07:00
parent 451a98936e
commit ae9d36d77f
61 changed files with 5835 additions and 272 deletions

View file

@ -5,6 +5,14 @@ import { Progress } from "@/components/ui/progress";
import { useIsAnonymous } from "@/contexts/anonymous-mode";
import { queries } from "@/zero/queries";
/**
* Premium credit balance shown in the sidebar.
*
* Values come from Zero (live-replicated from Postgres) and are stored as
* integer micro-USD (1_000_000 == $1.00). We render in dollars because
* users top up at $1/pack and the credit gets debited at actual provider
* cost.
*/
export function PremiumTokenUsageDisplay() {
const isAnonymous = useIsAnonymous();
const [me] = useQuery(queries.user.me({}));
@ -12,21 +20,26 @@ export function PremiumTokenUsageDisplay() {
if (isAnonymous || !me) return null;
const usagePercentage = Math.min(
(me.premiumTokensUsed / Math.max(me.premiumTokensLimit, 1)) * 100,
(me.premiumCreditMicrosUsed / Math.max(me.premiumCreditMicrosLimit, 1)) * 100,
100
);
const formatTokens = (n: number) => {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
if (n >= 1_000) return `${(n / 1_000).toFixed(0)}K`;
return n.toLocaleString();
const formatUsd = (micros: number) => {
const dollars = micros / 1_000_000;
if (dollars >= 100) return `$${dollars.toFixed(0)}`;
if (dollars >= 1) return `$${dollars.toFixed(2)}`;
// Sub-dollar balances need extra precision so the bar still tells the
// user what's left ("$0.04 of credit") instead of rounding to "$0".
if (dollars > 0) return `$${dollars.toFixed(3)}`;
return "$0";
};
return (
<div className="space-y-1.5">
<div className="flex justify-between items-center text-xs">
<span className="text-muted-foreground">
{formatTokens(me.premiumTokensUsed)} / {formatTokens(me.premiumTokensLimit)} tokens
{formatUsd(me.premiumCreditMicrosUsed)} / {formatUsd(me.premiumCreditMicrosLimit)} of
credit
</span>
<span className="font-medium">{usagePercentage.toFixed(0)}%</span>
</div>