84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
import type { TenantPackageSummary } from '../api';
|
|
|
|
export type PackageUsageMetricKey = 'events' | 'guests' | 'photos';
|
|
|
|
export type PackageUsageMetric = {
|
|
key: PackageUsageMetricKey;
|
|
limit: number | null;
|
|
used: number | null;
|
|
remaining: number | null;
|
|
};
|
|
|
|
const toNumber = (value: unknown): number | null => {
|
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
return value;
|
|
}
|
|
|
|
if (typeof value === 'string' && value.trim() !== '') {
|
|
const parsed = Number(value);
|
|
if (Number.isFinite(parsed)) {
|
|
return parsed;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const resolveLimitValue = (limits: Record<string, unknown> | null, key: string): number | null => {
|
|
const value = limits ? toNumber(limits[key]) : null;
|
|
if (value === null || value <= 0) {
|
|
return null;
|
|
}
|
|
return value;
|
|
};
|
|
|
|
const resolveUsageValue = (value: unknown): number | null => {
|
|
const normalized = toNumber(value);
|
|
if (normalized === null || normalized < 0) {
|
|
return null;
|
|
}
|
|
return normalized;
|
|
};
|
|
|
|
const deriveUsedFromRemaining = (limit: number | null, remaining: number | null): number | null => {
|
|
if (limit === null || remaining === null) {
|
|
return null;
|
|
}
|
|
|
|
return Math.max(limit - remaining, 0);
|
|
};
|
|
|
|
export const buildPackageUsageMetrics = (pkg: TenantPackageSummary): PackageUsageMetric[] => {
|
|
const limits = pkg.package_limits ?? {};
|
|
|
|
const eventLimit = resolveLimitValue(limits, 'max_events_per_year');
|
|
const eventRemaining = resolveUsageValue(pkg.remaining_events);
|
|
const eventUsed = resolveUsageValue(pkg.used_events) ?? deriveUsedFromRemaining(eventLimit, eventRemaining);
|
|
|
|
const guestLimit = resolveLimitValue(limits, 'max_guests');
|
|
const guestRemaining = resolveUsageValue(limits['remaining_guests']);
|
|
const guestUsed = resolveUsageValue(limits['used_guests']) ?? deriveUsedFromRemaining(guestLimit, guestRemaining);
|
|
|
|
const photoLimit = resolveLimitValue(limits, 'max_photos');
|
|
const photoRemaining = resolveUsageValue(limits['remaining_photos']);
|
|
const photoUsed = resolveUsageValue(limits['used_photos']) ?? deriveUsedFromRemaining(photoLimit, photoRemaining);
|
|
|
|
return [
|
|
{ key: 'events', limit: eventLimit, used: eventUsed, remaining: eventRemaining },
|
|
{ key: 'guests', limit: guestLimit, used: guestUsed, remaining: guestRemaining },
|
|
{ key: 'photos', limit: photoLimit, used: photoUsed, remaining: photoRemaining },
|
|
].filter((metric) => metric.limit !== null);
|
|
};
|
|
|
|
export const usagePercent = (metric: PackageUsageMetric): number => {
|
|
if (!metric.limit || metric.limit <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (metric.used === null || metric.used < 0) {
|
|
return 100;
|
|
}
|
|
|
|
return Math.min(100, Math.max(0, Math.round((metric.used / metric.limit) * 100)));
|
|
};
|