diff --git a/resources/js/admin/i18n/locales/de/management.json b/resources/js/admin/i18n/locales/de/management.json index bc4ffa7..efc3db4 100644 --- a/resources/js/admin/i18n/locales/de/management.json +++ b/resources/js/admin/i18n/locales/de/management.json @@ -2882,6 +2882,7 @@ }, "mobileBilling": { "packageFallback": "Paket", + "eventPerPurchase": "1 Event pro Kauf", "remainingEvents": "{{count}} Events", "remainingEventsOf": "{{remaining}} von {{limit}} Events übrig", "remainingEventsZero": "Keine Events mehr verfügbar", diff --git a/resources/js/admin/i18n/locales/en/management.json b/resources/js/admin/i18n/locales/en/management.json index 7e53ee8..ae3d0c9 100644 --- a/resources/js/admin/i18n/locales/en/management.json +++ b/resources/js/admin/i18n/locales/en/management.json @@ -2886,6 +2886,7 @@ }, "mobileBilling": { "packageFallback": "Package", + "eventPerPurchase": "1 event per purchase", "remainingEvents": "{{count}} events", "remainingEventsOf": "{{remaining}} of {{limit}} events remaining", "remainingEventsZero": "No events remaining", diff --git a/resources/js/admin/mobile/BillingPage.tsx b/resources/js/admin/mobile/BillingPage.tsx index 98a0290..d4b94a5 100644 --- a/resources/js/admin/mobile/BillingPage.tsx +++ b/resources/js/admin/mobile/BillingPage.tsx @@ -19,7 +19,13 @@ import { import { TenantAddonHistoryEntry, getTenantAddonHistory } from '../api'; import { getApiErrorMessage } from '../lib/apiError'; import { ADMIN_EVENT_VIEW_PATH, adminPath } from '../constants'; -import { buildPackageUsageMetrics, getUsageState, PackageUsageMetric, usagePercent } from './billingUsage'; +import { + buildPackageUsageMetrics, + formatPackageEventAllowance, + getUsageState, + PackageUsageMetric, + usagePercent, +} from './billingUsage'; import { useBackNavigation } from './hooks/useBackNavigation'; import { useAdminTheme } from './theme'; import { @@ -517,16 +523,7 @@ function PackageCard({ ? t('shop.partner.tiers.premium', 'Premium') : pkg.included_package_slug; const limitMaxEvents = typeof limits?.max_events_per_year === 'number' ? (limits?.max_events_per_year as number) : null; - const remaining = pkg.remaining_events ?? limitMaxEvents ?? 0; - const remainingText = - remaining === 0 - ? t('mobileBilling.remainingEventsZero', 'No events remaining') - : limitMaxEvents - ? t('mobileBilling.remainingEventsOf', '{{remaining}} of {{limit}} events remaining', { - remaining, - limit: limitMaxEvents, - }) - : t('mobileBilling.remainingEvents', '{{count}} events', { count: remaining }); + const remainingText = formatPackageEventAllowance(pkg, t); const expires = pkg.expires_at ? formatDate(pkg.expires_at) : null; const usageMetrics = buildPackageUsageMetrics(pkg); const usageStates = usageMetrics.map((metric) => getUsageState(metric)); diff --git a/resources/js/admin/mobile/__tests__/billingUsage.test.ts b/resources/js/admin/mobile/__tests__/billingUsage.test.ts index 2e901e4..cd9f99b 100644 --- a/resources/js/admin/mobile/__tests__/billingUsage.test.ts +++ b/resources/js/admin/mobile/__tests__/billingUsage.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; import type { TenantPackageSummary } from '../../api'; -import { buildPackageUsageMetrics, getUsageState, usagePercent } from '../billingUsage'; +import { buildPackageUsageMetrics, formatPackageEventAllowance, getUsageState, usagePercent } from '../billingUsage'; const basePackage: TenantPackageSummary = { id: 1, @@ -92,3 +92,34 @@ describe('getUsageState', () => { expect(getUsageState({ ...metrics[0], used: 10, remaining: 0 })).toBe('danger'); }); }); + +describe('formatPackageEventAllowance', () => { + const t = (key: string, options?: Record) => { + if (key === 'mobileBilling.eventPerPurchase') { + return String(options?.defaultValue ?? '1 event per purchase'); + } + if (key === 'mobileBilling.remainingEventsZero') { + return String(options?.defaultValue ?? 'No events remaining'); + } + if (key === 'mobileBilling.remainingEventsOf') { + return `${options?.remaining} of ${options?.limit} events remaining`; + } + if (key === 'mobileBilling.remainingEvents') { + return `${options?.count} events`; + } + return key; + }; + + it('returns event-per-purchase for endcustomer packages', () => { + const label = formatPackageEventAllowance( + { ...basePackage, package_type: 'endcustomer', remaining_events: null }, + t + ); + expect(label).toBe('1 event per purchase'); + }); + + it('returns remaining events for reseller packages', () => { + const label = formatPackageEventAllowance(basePackage, t); + expect(label).toBe('3 of 5 events remaining'); + }); +}); diff --git a/resources/js/admin/mobile/billingUsage.ts b/resources/js/admin/mobile/billingUsage.ts index 8cc3aed..fef6bc8 100644 --- a/resources/js/admin/mobile/billingUsage.ts +++ b/resources/js/admin/mobile/billingUsage.ts @@ -103,6 +103,37 @@ export const buildPackageUsageMetrics = (pkg: TenantPackageSummary): PackageUsag ].filter((metric) => metric.limit !== null); }; +export const formatPackageEventAllowance = ( + pkg: TenantPackageSummary, + t: (key: string, options?: Record) => string +): string => { + if (pkg.package_type !== 'reseller') { + return t('mobileBilling.eventPerPurchase', { defaultValue: '1 event per purchase' }); + } + + const limits = (pkg.package_limits ?? {}) as Record; + const limitMaxEvents = + typeof limits.max_events_per_year === 'number' ? (limits.max_events_per_year as number) : null; + const remaining = pkg.remaining_events ?? limitMaxEvents ?? 0; + + if (remaining === 0) { + return t('mobileBilling.remainingEventsZero', { defaultValue: 'No events remaining' }); + } + + if (limitMaxEvents) { + return t('mobileBilling.remainingEventsOf', { + remaining, + limit: limitMaxEvents, + defaultValue: '{{remaining}} of {{limit}} events remaining', + }); + } + + return t('mobileBilling.remainingEvents', { + count: remaining, + defaultValue: '{{count}} events', + }); +}; + export const usagePercent = (metric: PackageUsageMetric): number => { if (!metric.limit || metric.limit <= 0) { return 0;