Show event-per-purchase for endcustomer packages
This commit is contained in:
@@ -2882,6 +2882,7 @@
|
|||||||
},
|
},
|
||||||
"mobileBilling": {
|
"mobileBilling": {
|
||||||
"packageFallback": "Paket",
|
"packageFallback": "Paket",
|
||||||
|
"eventPerPurchase": "1 Event pro Kauf",
|
||||||
"remainingEvents": "{{count}} Events",
|
"remainingEvents": "{{count}} Events",
|
||||||
"remainingEventsOf": "{{remaining}} von {{limit}} Events übrig",
|
"remainingEventsOf": "{{remaining}} von {{limit}} Events übrig",
|
||||||
"remainingEventsZero": "Keine Events mehr verfügbar",
|
"remainingEventsZero": "Keine Events mehr verfügbar",
|
||||||
|
|||||||
@@ -2886,6 +2886,7 @@
|
|||||||
},
|
},
|
||||||
"mobileBilling": {
|
"mobileBilling": {
|
||||||
"packageFallback": "Package",
|
"packageFallback": "Package",
|
||||||
|
"eventPerPurchase": "1 event per purchase",
|
||||||
"remainingEvents": "{{count}} events",
|
"remainingEvents": "{{count}} events",
|
||||||
"remainingEventsOf": "{{remaining}} of {{limit}} events remaining",
|
"remainingEventsOf": "{{remaining}} of {{limit}} events remaining",
|
||||||
"remainingEventsZero": "No events remaining",
|
"remainingEventsZero": "No events remaining",
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ import {
|
|||||||
import { TenantAddonHistoryEntry, getTenantAddonHistory } from '../api';
|
import { TenantAddonHistoryEntry, getTenantAddonHistory } from '../api';
|
||||||
import { getApiErrorMessage } from '../lib/apiError';
|
import { getApiErrorMessage } from '../lib/apiError';
|
||||||
import { ADMIN_EVENT_VIEW_PATH, adminPath } from '../constants';
|
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 { useBackNavigation } from './hooks/useBackNavigation';
|
||||||
import { useAdminTheme } from './theme';
|
import { useAdminTheme } from './theme';
|
||||||
import {
|
import {
|
||||||
@@ -517,16 +523,7 @@ function PackageCard({
|
|||||||
? t('shop.partner.tiers.premium', 'Premium')
|
? t('shop.partner.tiers.premium', 'Premium')
|
||||||
: pkg.included_package_slug;
|
: pkg.included_package_slug;
|
||||||
const limitMaxEvents = typeof limits?.max_events_per_year === 'number' ? (limits?.max_events_per_year as number) : null;
|
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 = formatPackageEventAllowance(pkg, t);
|
||||||
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 expires = pkg.expires_at ? formatDate(pkg.expires_at) : null;
|
const expires = pkg.expires_at ? formatDate(pkg.expires_at) : null;
|
||||||
const usageMetrics = buildPackageUsageMetrics(pkg);
|
const usageMetrics = buildPackageUsageMetrics(pkg);
|
||||||
const usageStates = usageMetrics.map((metric) => getUsageState(metric));
|
const usageStates = usageMetrics.map((metric) => getUsageState(metric));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import type { TenantPackageSummary } from '../../api';
|
import type { TenantPackageSummary } from '../../api';
|
||||||
import { buildPackageUsageMetrics, getUsageState, usagePercent } from '../billingUsage';
|
import { buildPackageUsageMetrics, formatPackageEventAllowance, getUsageState, usagePercent } from '../billingUsage';
|
||||||
|
|
||||||
const basePackage: TenantPackageSummary = {
|
const basePackage: TenantPackageSummary = {
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -92,3 +92,34 @@ describe('getUsageState', () => {
|
|||||||
expect(getUsageState({ ...metrics[0], used: 10, remaining: 0 })).toBe('danger');
|
expect(getUsageState({ ...metrics[0], used: 10, remaining: 0 })).toBe('danger');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('formatPackageEventAllowance', () => {
|
||||||
|
const t = (key: string, options?: Record<string, unknown>) => {
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -103,6 +103,37 @@ export const buildPackageUsageMetrics = (pkg: TenantPackageSummary): PackageUsag
|
|||||||
].filter((metric) => metric.limit !== null);
|
].filter((metric) => metric.limit !== null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatPackageEventAllowance = (
|
||||||
|
pkg: TenantPackageSummary,
|
||||||
|
t: (key: string, options?: Record<string, unknown>) => string
|
||||||
|
): string => {
|
||||||
|
if (pkg.package_type !== 'reseller') {
|
||||||
|
return t('mobileBilling.eventPerPurchase', { defaultValue: '1 event per purchase' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const limits = (pkg.package_limits ?? {}) as Record<string, unknown>;
|
||||||
|
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 => {
|
export const usagePercent = (metric: PackageUsageMetric): number => {
|
||||||
if (!metric.limit || metric.limit <= 0) {
|
if (!metric.limit || metric.limit <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user