website refactor

This commit is contained in:
2026-01-19 14:07:49 +01:00
parent 54f42bab9f
commit 6154d54435
88 changed files with 755 additions and 566 deletions

View File

@@ -0,0 +1,24 @@
/**
* DurationDisplay
*
* Deterministic formatting for time durations.
*/
export class DurationDisplay {
/**
* Formats milliseconds as "123.45ms".
*/
static formatMs(ms: number): string {
return `${ms.toFixed(2)}ms`;
}
/**
* Formats seconds as "M:SS.mmm".
* Example: 65.123 -> "1:05.123"
*/
static formatSeconds(seconds: number): string {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = (seconds % 60).toFixed(3);
return `${minutes}:${remainingSeconds.padStart(6, '0')}`;
}
}

View File

@@ -0,0 +1,24 @@
/**
* FinishDisplay
*
* Deterministic formatting for race finish positions.
*/
export class FinishDisplay {
/**
* Formats a finish position as "P1", "P2", etc.
*/
static format(position: number | null | undefined): string {
if (position === null || position === undefined) return '—';
return `P${position.toFixed(0)}`;
}
/**
* Formats an average finish position with one decimal place.
* Example: 5.4 -> "P5.4"
*/
static formatAverage(avg: number | null | undefined): string {
if (avg === null || avg === undefined) return '—';
return `P${avg.toFixed(1)}`;
}
}

View File

@@ -0,0 +1,21 @@
/**
* MemoryDisplay
*
* Deterministic formatting for memory usage.
*/
export class MemoryDisplay {
/**
* Formats bytes as "123.4MB".
*/
static formatMB(bytes: number): string {
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
}
/**
* Formats bytes as "123.4KB".
*/
static formatKB(bytes: number): string {
return `${(bytes / 1024).toFixed(1)}KB`;
}
}

View File

@@ -15,4 +15,17 @@ export class NumberDisplay {
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.');
}
/**
* Formats a number in compact form (e.g., 1.2k, 1.5M).
*/
static formatCompact(value: number): string {
if (value >= 1000000) {
return `${(value / 1000000).toFixed(1)}M`;
}
if (value >= 1000) {
return `${(value / 1000).toFixed(1)}k`;
}
return value.toString();
}
}

View File

@@ -0,0 +1,25 @@
/**
* PercentDisplay
*
* Deterministic formatting for percentages.
*/
export class PercentDisplay {
/**
* Formats a decimal value as a percentage string.
* Example: 0.1234 -> "12.3%"
*/
static format(value: number | null | undefined): string {
if (value === null || value === undefined) return '0.0%';
return `${(value * 100).toFixed(1)}%`;
}
/**
* Formats a whole number as a percentage string.
* Example: 85 -> "85%"
*/
static formatWhole(value: number | null | undefined): string {
if (value === null || value === undefined) return '0%';
return `${Math.round(value)}%`;
}
}

View File

@@ -0,0 +1,44 @@
/**
* StatusDisplay
*
* Deterministic mapping of status codes to human-readable labels.
*/
export class StatusDisplay {
/**
* Maps transaction status to label.
*/
static transactionStatus(status: string): string {
const map: Record<string, string> = {
paid: 'Paid',
pending: 'Pending',
overdue: 'Overdue',
failed: 'Failed',
};
return map[status] || status;
}
/**
* Maps race status to label.
*/
static raceStatus(status: string): string {
const map: Record<string, string> = {
scheduled: 'Scheduled',
running: 'Live',
completed: 'Completed',
};
return map[status] || status;
}
/**
* Maps protest status to label.
*/
static protestStatus(status: string): string {
const map: Record<string, string> = {
pending: 'Pending',
under_review: 'Under Review',
resolved: 'Resolved',
};
return map[status] || status;
}
}

View File

@@ -4,4 +4,9 @@ export class WinRateDisplay {
const rate = (wins / racesCompleted) * 100;
return rate.toFixed(1);
}
static format(rate: number | null | undefined): string {
if (rate === null || rate === undefined) return '0.0%';
return `${rate.toFixed(1)}%`;
}
}