view data fixes
Some checks failed
Contract Testing / contract-tests (pull_request) Failing after 5m42s
Contract Testing / contract-snapshot (pull_request) Has been skipped

This commit is contained in:
2026-01-22 23:40:38 +01:00
parent 1288a9dc30
commit 18133aef4c
111 changed files with 841 additions and 324 deletions

View File

@@ -0,0 +1,33 @@
/**
* ActivityLevelDisplay
*
* Deterministic mapping of engagement rates to activity level labels.
*/
export class ActivityLevelDisplay {
/**
* Maps engagement rate to activity level label.
*/
static levelLabel(engagementRate: number): string {
if (engagementRate < 20) {
return 'Low';
} else if (engagementRate < 50) {
return 'Medium';
} else {
return 'High';
}
}
/**
* Maps engagement rate to activity level value.
*/
static levelValue(engagementRate: number): 'low' | 'medium' | 'high' {
if (engagementRate < 20) {
return 'low';
} else if (engagementRate < 50) {
return 'medium';
} else {
return 'high';
}
}
}

View File

@@ -0,0 +1,37 @@
/**
* AvatarDisplay
*
* Deterministic mapping of avatar-related data to display formats.
*/
export class AvatarDisplay {
/**
* Converts binary buffer to base64 string for display.
*/
static bufferToBase64(buffer: ArrayBuffer): string {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
/**
* Determines if avatar data is valid for display.
*/
static hasValidData(buffer: ArrayBuffer, contentType: string): boolean {
return buffer.byteLength > 0 && contentType.length > 0;
}
/**
* Formats content type for display (e.g., "image/png" → "PNG").
*/
static formatContentType(contentType: string): string {
const parts = contentType.split('/');
if (parts.length === 2) {
return parts[1].toUpperCase();
}
return contentType;
}
}

View File

@@ -1,36 +1,47 @@
/**
* CurrencyDisplay
*
*
* Deterministic currency formatting for display.
* Avoids Intl and toLocaleString to prevent SSR/hydration mismatches.
*/
export class CurrencyDisplay {
/**
* Formats an amount as currency (e.g., "$10.00").
* Formats an amount as currency (e.g., "$10.00" or "€1.000,00").
* Default currency is USD.
*/
static format(amount: number, currency: string = 'USD'): string {
const symbol = currency === 'USD' ? '$' : currency + ' ';
const symbol = currency === 'USD' ? '$' : currency === 'EUR' ? '€' : currency + ' ';
const formattedAmount = amount.toFixed(2);
// Add thousands separators
const parts = formattedAmount.split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return `${symbol}${parts.join('.')}`;
// Use dot as thousands separator for EUR, comma for USD
if (currency === 'EUR') {
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.');
return `${symbol}${parts[0]},${parts[1]}`;
} else {
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return `${symbol}${parts.join('.')}`;
}
}
/**
* Formats an amount as a compact currency (e.g., "$10").
* Formats an amount as a compact currency (e.g., "$10" or "€1.000").
*/
static formatCompact(amount: number, currency: string = 'USD'): string {
const symbol = currency === 'USD' ? '$' : currency + ' ';
const symbol = currency === 'USD' ? '$' : currency === 'EUR' ? '€' : currency + ' ';
const roundedAmount = Math.round(amount);
// Add thousands separators
const formattedAmount = roundedAmount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
const formattedAmount = roundedAmount.toString();
return `${symbol}${formattedAmount}`;
// Use dot as thousands separator for EUR, comma for USD
if (currency === 'EUR') {
return `${symbol}${formattedAmount.replace(/\B(?=(\d{3})+(?!\d))/g, '.')}`;
} else {
return `${symbol}${formattedAmount.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`;
}
}
}

View File

@@ -0,0 +1,39 @@
/**
* LeagueTierDisplay
*
* Deterministic display logic for league tiers.
*/
export interface LeagueTierDisplayData {
color: string;
bgColor: string;
border: string;
icon: string;
}
export class LeagueTierDisplay {
private static readonly CONFIG: Record<string, LeagueTierDisplayData> = {
premium: {
color: 'text-yellow-400',
bgColor: 'bg-yellow-500/10',
border: 'border-yellow-500/30',
icon: '⭐'
},
standard: {
color: 'text-primary-blue',
bgColor: 'bg-primary-blue/10',
border: 'border-primary-blue/30',
icon: '🏆'
},
starter: {
color: 'text-gray-400',
bgColor: 'bg-gray-500/10',
border: 'border-gray-500/30',
icon: '🚀'
},
};
static getDisplay(tier: 'premium' | 'standard' | 'starter'): LeagueTierDisplayData {
return this.CONFIG[tier];
}
}

View File

@@ -0,0 +1,38 @@
/**
* OnboardingStatusDisplay
*
* Deterministic mapping of onboarding status to display labels and variants.
*/
export class OnboardingStatusDisplay {
/**
* Maps onboarding success status to display label.
*/
static statusLabel(success: boolean): string {
return success ? 'Onboarding Complete' : 'Onboarding Failed';
}
/**
* Maps onboarding success status to badge variant.
*/
static statusVariant(success: boolean): string {
return success ? 'performance-green' : 'racing-red';
}
/**
* Maps onboarding success status to icon.
*/
static statusIcon(success: boolean): string {
return success ? '✅' : '❌';
}
/**
* Maps onboarding success status to message.
*/
static statusMessage(success: boolean, errorMessage?: string): string {
if (success) {
return 'Your onboarding has been completed successfully.';
}
return errorMessage || 'Failed to complete onboarding. Please try again.';
}
}

View File

@@ -0,0 +1,35 @@
/**
* SeasonStatusDisplay
*
* Deterministic display logic for season status.
*/
export interface SeasonStatusDisplayData {
color: string;
bg: string;
label: string;
}
export class SeasonStatusDisplay {
private static readonly CONFIG: Record<string, SeasonStatusDisplayData> = {
active: {
color: 'text-performance-green',
bg: 'bg-performance-green/10',
label: 'Active Season'
},
upcoming: {
color: 'text-warning-amber',
bg: 'bg-warning-amber/10',
label: 'Starting Soon'
},
completed: {
color: 'text-gray-400',
bg: 'bg-gray-400/10',
label: 'Season Ended'
},
};
static getDisplay(status: 'active' | 'upcoming' | 'completed'): SeasonStatusDisplayData {
return this.CONFIG[status];
}
}

View File

@@ -0,0 +1,19 @@
/**
* UserRoleDisplay
*
* Deterministic mapping of user role codes to display labels.
*/
export class UserRoleDisplay {
/**
* Maps user role to display label.
*/
static roleLabel(role: string): string {
const map: Record<string, string> = {
owner: 'Owner',
admin: 'Admin',
user: 'User',
};
return map[role] || role;
}
}

View File

@@ -0,0 +1,52 @@
/**
* UserStatusDisplay
*
* Deterministic mapping of user status codes to display labels and variants.
*/
export class UserStatusDisplay {
/**
* Maps user status to display label.
*/
static statusLabel(status: string): string {
const map: Record<string, string> = {
active: 'Active',
suspended: 'Suspended',
deleted: 'Deleted',
};
return map[status] || status;
}
/**
* Maps user status to badge variant.
*/
static statusVariant(status: string): string {
const map: Record<string, string> = {
active: 'performance-green',
suspended: 'yellow-500',
deleted: 'racing-red',
};
return map[status] || 'gray-500';
}
/**
* Determines if a user can be suspended.
*/
static canSuspend(status: string): boolean {
return status === 'active';
}
/**
* Determines if a user can be activated.
*/
static canActivate(status: string): boolean {
return status === 'suspended';
}
/**
* Determines if a user can be deleted.
*/
static canDelete(status: string): boolean {
return status !== 'deleted';
}
}