Files
textbee/web/lib/status.ts
isra el ad7bcd99a5 fix: stop the billing page asserting things it does not know
The status pill hardcoded a check mark and varied only its colour, so a
past_due or canceled subscriber was shown a tick beside the bad news.
It also fell back to the label "Active" when the payload carried no
status at all, inventing a healthy state on a billing screen. The icon
now comes from the status, and an absent status reads "Unknown".

formatPrice returned "Free" whenever the currency was missing, even
with a real amount. Since the badge only renders when amount > 0, the
one case it could produce was a paying customer being shown
"Free / monthly". Default the currency instead.

The page also derived its usage numbers inline instead of calling
deriveUsage, which exists precisely so billing and the dashboard cannot
disagree. The inline copy skipped the helper's 0-100 clamp, so the
invariant was already broken.

Monthly is a rolling 30-day window server-side, not a calendar month,
which the dashboard already labels correctly. Billing said "this
month", so a user capping out late in the month would wait for a reset
that never arrives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 01:50:30 +03:00

68 lines
1.7 KiB
TypeScript

import {
AlertTriangle,
Check,
CircleHelp,
XCircle,
type LucideIcon,
} from 'lucide-react'
import type { SubscriptionStatus } from '@/lib/api/types'
// Subscription status colors, previously repeated across subscription-info's
// badge, icon and text. Returns semantic tone classes for text + background.
export type StatusTone = {
text: string
bg: string
}
export function subscriptionStatusTone(
status: SubscriptionStatus | null | undefined
): StatusTone {
switch (status) {
case 'active':
return {
text: 'text-green-600 dark:text-green-400',
bg: 'bg-green-50 dark:bg-green-900/30',
}
case 'past_due':
return {
text: 'text-amber-600 dark:text-amber-400',
bg: 'bg-amber-50 dark:bg-amber-900/30',
}
default:
return {
text: 'text-muted-foreground',
bg: 'bg-muted',
}
}
}
/**
* Icon for a subscription status.
*
* Picked from the status for the same reason the tone is: the billing card
* used to hardcode a check mark, so a past_due or canceled subscriber was
* shown a tick next to the bad news, at exactly the moment they needed a
* warning instead.
*/
export function subscriptionStatusIcon(
status: SubscriptionStatus | null | undefined
): LucideIcon {
switch (status) {
case 'active':
return Check
case 'past_due':
return AlertTriangle
case 'canceled':
return XCircle
default:
return CircleHelp
}
}
// Usage meter color by percentage: green under 80, amber 80-99, red at 100+.
export function usageMeterColor(percentage: number): string {
if (percentage >= 100) return 'bg-red-500'
if (percentage >= 80) return 'bg-amber-500'
return 'bg-green-500'
}