view data fixes
This commit is contained in:
33
apps/website/lib/display-objects/ActivityLevelDisplay.ts
Normal file
33
apps/website/lib/display-objects/ActivityLevelDisplay.ts
Normal 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
37
apps/website/lib/display-objects/AvatarDisplay.ts
Normal file
37
apps/website/lib/display-objects/AvatarDisplay.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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, ',')}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
apps/website/lib/display-objects/LeagueTierDisplay.ts
Normal file
39
apps/website/lib/display-objects/LeagueTierDisplay.ts
Normal 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];
|
||||
}
|
||||
}
|
||||
38
apps/website/lib/display-objects/OnboardingStatusDisplay.ts
Normal file
38
apps/website/lib/display-objects/OnboardingStatusDisplay.ts
Normal 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.';
|
||||
}
|
||||
}
|
||||
35
apps/website/lib/display-objects/SeasonStatusDisplay.ts
Normal file
35
apps/website/lib/display-objects/SeasonStatusDisplay.ts
Normal 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];
|
||||
}
|
||||
}
|
||||
19
apps/website/lib/display-objects/UserRoleDisplay.ts
Normal file
19
apps/website/lib/display-objects/UserRoleDisplay.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
52
apps/website/lib/display-objects/UserStatusDisplay.ts
Normal file
52
apps/website/lib/display-objects/UserStatusDisplay.ts
Normal 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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user