Expand package limit and feature details
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-06 13:20:19 +01:00
parent 4fe589f0e2
commit 10fbee4e6e
6 changed files with 263 additions and 7 deletions

View File

@@ -21,6 +21,12 @@ import { ADMIN_EVENT_VIEW_PATH, adminPath } from '../constants';
import { buildPackageUsageMetrics, PackageUsageMetric, usagePercent } from './billingUsage';
import { useBackNavigation } from './hooks/useBackNavigation';
import { useAdminTheme } from './theme';
import {
collectPackageFeatures,
formatEventUsage,
getPackageFeatureLabel,
getPackageLimitEntries,
} from './lib/packageSummary';
export default function MobileBillingPage() {
const { t } = useTranslation('management');
@@ -254,9 +260,17 @@ export default function MobileBillingPage() {
function PackageCard({ pkg, label, isActive = false }: { pkg: TenantPackageSummary; label?: string; isActive?: boolean }) {
const { t } = useTranslation('management');
const { border, primary, accentSoft, textStrong, muted } = useAdminTheme();
const remaining = pkg.remaining_events ?? (pkg.package_limits?.max_events_per_year as number | undefined) ?? 0;
const limits = (pkg.package_limits ?? null) as Record<string, unknown> | null;
const remaining = pkg.remaining_events ?? (limits?.max_events_per_year as number | undefined) ?? 0;
const expires = pkg.expires_at ? formatDate(pkg.expires_at) : null;
const usageMetrics = buildPackageUsageMetrics(pkg);
const limitEntries = getPackageLimitEntries(limits, t);
const featureKeys = collectPackageFeatures(pkg);
const eventUsageText = formatEventUsage(
typeof pkg.used_events === 'number' ? pkg.used_events : null,
typeof limits?.max_events_per_year === 'number' ? (limits?.max_events_per_year as number) : null,
t
);
return (
<MobileCard
borderColor={isActive ? primary : border}
@@ -285,6 +299,43 @@ function PackageCard({ pkg, label, isActive = false }: { pkg: TenantPackageSumma
{renderFeatureBadge(pkg, t, 'branding_allowed', t('billing.features.branding', 'Branding'))}
{renderFeatureBadge(pkg, t, 'watermark_allowed', t('billing.features.watermark', 'Watermark'))}
</XStack>
{eventUsageText ? (
<Text fontSize="$xs" color={muted}>
{eventUsageText}
</Text>
) : null}
{limitEntries.length ? (
<YStack space="$1.5" marginTop="$2">
<Text fontSize="$xs" color={muted}>
{t('mobileBilling.details.limitsTitle', 'Limits')}
</Text>
{limitEntries.map((entry) => (
<XStack key={entry.key} alignItems="center" justifyContent="space-between">
<Text fontSize="$xs" color={muted}>
{entry.label}
</Text>
<Text fontSize="$xs" color={textStrong} fontWeight="700">
{entry.value}
</Text>
</XStack>
))}
</YStack>
) : null}
{featureKeys.length ? (
<YStack space="$1.5" marginTop="$2">
<Text fontSize="$xs" color={muted}>
{t('mobileBilling.details.featuresTitle', 'Features')}
</Text>
{featureKeys.map((feature) => (
<XStack key={feature} alignItems="center" space="$2">
<Sparkles size={14} color={primary} />
<Text fontSize="$xs" color={textStrong}>
{getPackageFeatureLabel(feature, t)}
</Text>
</XStack>
))}
</YStack>
) : null}
{usageMetrics.length ? (
<YStack space="$2" marginTop="$2">
{usageMetrics.map((metric) => (