Files
gridpilot.gg/apps/website/lib/display-objects/ProfileDisplay.ts
2026-01-11 13:04:33 +01:00

261 lines
7.8 KiB
TypeScript

/**
* Profile Display Objects
*
* Deterministic formatting for profile data.
* NO Intl.*, NO Date.toLocale*, NO dynamic formatting.
*/
// ============================================================================
// COUNTRY FLAG DISPLAY
// ============================================================================
export interface CountryFlagDisplayData {
flag: string;
label: string;
}
export const countryFlagDisplay: Record<string, CountryFlagDisplayData> = {
// Common country codes - add as needed
US: { flag: '🇺🇸', label: 'United States' },
GB: { flag: '🇬🇧', label: 'United Kingdom' },
DE: { flag: '🇩🇪', label: 'Germany' },
FR: { flag: '🇫🇷', label: 'France' },
IT: { flag: '🇮🇹', label: 'Italy' },
ES: { flag: '🇪🇸', label: 'Spain' },
JP: { flag: '🇯🇵', label: 'Japan' },
AU: { flag: '🇦🇺', label: 'Australia' },
CA: { flag: '🇨🇦', label: 'Canada' },
BR: { flag: '🇧🇷', label: 'Brazil' },
// Fallback for unknown codes
DEFAULT: { flag: '🏁', label: 'Unknown' },
} as const;
export function getCountryFlagDisplay(countryCode: string): CountryFlagDisplayData {
const code = countryCode.toUpperCase();
return countryFlagDisplay[code] || countryFlagDisplay.DEFAULT;
}
// ============================================================================
// ACHIEVEMENT RARITY DISPLAY
// ============================================================================
export interface AchievementRarityDisplayData {
text: string;
badgeClasses: string;
borderClasses: string;
}
export const achievementRarityDisplay: Record<string, AchievementRarityDisplayData> = {
common: {
text: 'Common',
badgeClasses: 'bg-gray-400/10 text-gray-400',
borderClasses: 'border-gray-400/30',
},
rare: {
text: 'Rare',
badgeClasses: 'bg-primary-blue/10 text-primary-blue',
borderClasses: 'border-primary-blue/30',
},
epic: {
text: 'Epic',
badgeClasses: 'bg-purple-400/10 text-purple-400',
borderClasses: 'border-purple-400/30',
},
legendary: {
text: 'Legendary',
badgeClasses: 'bg-yellow-400/10 text-yellow-400',
borderClasses: 'border-yellow-400/30',
},
} as const;
export function getAchievementRarityDisplay(rarity: string): AchievementRarityDisplayData {
return achievementRarityDisplay[rarity] || achievementRarityDisplay.common;
}
// ============================================================================
// ACHIEVEMENT ICON DISPLAY
// ============================================================================
export type AchievementIconType = 'trophy' | 'medal' | 'star' | 'crown' | 'target' | 'zap';
export interface AchievementIconDisplayData {
name: string;
// Icon component will be resolved in UI layer
}
export const achievementIconDisplay: Record<AchievementIconType, AchievementIconDisplayData> = {
trophy: { name: 'Trophy' },
medal: { name: 'Medal' },
star: { name: 'Star' },
crown: { name: 'Crown' },
target: { name: 'Target' },
zap: { name: 'Zap' },
} as const;
export function getAchievementIconDisplay(icon: string): AchievementIconDisplayData {
return achievementIconDisplay[icon as AchievementIconType] || achievementIconDisplay.trophy;
}
// ============================================================================
// SOCIAL PLATFORM DISPLAY
// ============================================================================
export interface SocialPlatformDisplayData {
name: string;
hoverClasses: string;
}
export const socialPlatformDisplay: Record<string, SocialPlatformDisplayData> = {
twitter: {
name: 'Twitter',
hoverClasses: 'hover:text-sky-400 hover:bg-sky-400/10',
},
youtube: {
name: 'YouTube',
hoverClasses: 'hover:text-red-500 hover:bg-red-500/10',
},
twitch: {
name: 'Twitch',
hoverClasses: 'hover:text-purple-400 hover:bg-purple-400/10',
},
discord: {
name: 'Discord',
hoverClasses: 'hover:text-indigo-400 hover:bg-indigo-400/10',
},
} as const;
export function getSocialPlatformDisplay(platform: string): SocialPlatformDisplayData {
return socialPlatformDisplay[platform] || socialPlatformDisplay.discord;
}
// ============================================================================
// DATE FORMATTING (DETERMINISTIC)
// ============================================================================
/**
* Format date string to "Month Year" format
* Input: ISO date string (e.g., "2024-01-15T10:30:00Z")
* Output: "Jan 2024"
*/
export function formatMonthYear(dateString: string): string {
const date = new Date(dateString);
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const month = months[date.getUTCMonth()];
const year = date.getUTCFullYear();
return `${month} ${year}`;
}
/**
* Format date string to "Month Day, Year" format
* Input: ISO date string
* Output: "Jan 15, 2024"
*/
export function formatMonthDayYear(dateString: string): string {
const date = new Date(dateString);
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const month = months[date.getUTCMonth()];
const day = date.getUTCDate();
const year = date.getUTCFullYear();
return `${month} ${day}, ${year}`;
}
// ============================================================================
// STATISTICS FORMATTING
// ============================================================================
export interface StatDisplayData {
value: string;
label: string;
}
/**
* Format percentage with 1 decimal place
* Input: 0.1234
* Output: "12.3%"
*/
export function formatPercentage(value: number | null): string {
if (value === null || value === undefined) return '0.0%';
return `${(value * 100).toFixed(1)}%`;
}
/**
* Format finish position
* Input: 1
* Output: "P1"
*/
export function formatFinishPosition(position: number | null): string {
if (position === null || position === undefined) return 'P-';
return `P${position}`;
}
/**
* Format average finish with 1 decimal place
* Input: 3.456
* Output: "P3.5"
*/
export function formatAvgFinish(avg: number | null): string {
if (avg === null || avg === undefined) return 'P-';
return `P${avg.toFixed(1)}`;
}
/**
* Format rating (whole number)
* Input: 1234.56
* Output: "1235"
*/
export function formatRating(rating: number | null): string {
if (rating === null || rating === undefined) return '0';
return Math.round(rating).toString();
}
/**
* Format consistency percentage
* Input: 87.5
* Output: "88%"
*/
export function formatConsistency(consistency: number | null): string {
if (consistency === null || consistency === undefined) return '0%';
return `${Math.round(consistency)}%`;
}
/**
* Format percentile
* Input: 15.5
* Output: "Top 16%"
*/
export function formatPercentile(percentile: number | null): string {
if (percentile === null || percentile === undefined) return 'Top -%';
return `Top ${Math.round(percentile)}%`;
}
// ============================================================================
// TEAM ROLE DISPLAY
// ============================================================================
export interface TeamRoleDisplayData {
text: string;
badgeClasses: string;
}
export const teamRoleDisplay: Record<string, TeamRoleDisplayData> = {
owner: {
text: 'Owner',
badgeClasses: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/30',
},
admin: {
text: 'Admin',
badgeClasses: 'bg-purple-500/10 text-purple-400 border-purple-500/30',
},
steward: {
text: 'Steward',
badgeClasses: 'bg-blue-500/10 text-blue-400 border-blue-500/30',
},
member: {
text: 'Member',
badgeClasses: 'bg-primary-blue/10 text-primary-blue border-primary-blue/30',
},
} as const;
export function getTeamRoleDisplay(role: string): TeamRoleDisplayData {
return teamRoleDisplay[role] || teamRoleDisplay.member;
}