unrip/src/operator-dashboard/static/lib/format.js
philipp 2d329c9b2f
All checks were successful
deploy / deploy (push) Successful in 57s
Add quote lifecycle retention rollups
Proof: DB-backed quote lifecycle retention policy, rollup-before-prune maintenance, dashboard storage visibility, and regression tests for preservation and fail-closed behavior.
Assumptions: Completed quote outcome attribution remains the current durable successful-trade evidence; venue-native terminal fill ids and fee-complete PnL remain unavailable.
Still fake: Historical non-success detail already pruned cannot be recovered; rollups are aggregate analytics, not per-quote settled trade proof.
2026-06-06 15:50:29 +02:00

91 lines
2.8 KiB
JavaScript

export function formatBoolean(value) {
if (value == null) return 'Unknown';
return value ? 'Yes' : 'No';
}
export function formatTimestamp(value) {
if (!value) return 'Unavailable';
const date = new Date(value);
if (Number.isNaN(date.getTime())) return 'Unavailable';
return date.toLocaleString();
}
export function formatAge(value) {
if (value == null) return 'Unavailable';
const numeric = Number(value);
if (!Number.isFinite(numeric)) return 'Unavailable';
const ageMs = Math.max(0, Math.floor(numeric));
if (ageMs < 1000) return `${ageMs} ms`;
const seconds = Math.floor(ageMs / 1000);
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
if (minutes < 60) {
return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
}
const hours = Math.floor(minutes / 60);
const remainingMinutes = minutes % 60;
if (hours < 24) {
return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
}
const days = Math.floor(hours / 24);
const remainingHours = hours % 24;
return remainingHours > 0 ? `${days}d ${remainingHours}h` : `${days}d`;
}
export function formatAgeFromTimestamp(value, now = Date.now()) {
if (!value) return 'Unavailable';
const timestamp = new Date(value).getTime();
if (Number.isNaN(timestamp)) return 'Unavailable';
return formatAge(now - timestamp);
}
export function formatBytes(value) {
const numeric = Number(value);
if (!Number.isFinite(numeric)) return 'Unavailable';
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
let amount = Math.max(0, numeric);
let unitIndex = 0;
while (amount >= 1024 && unitIndex < units.length - 1) {
amount /= 1024;
unitIndex += 1;
}
const digits = amount >= 100 || unitIndex === 0 ? 0 : amount >= 10 ? 1 : 2;
return `${amount.toFixed(digits)} ${units[unitIndex]}`;
}
export function formatEur(value) {
if (value == null || value === '') return 'Unavailable';
const numeric = Number(value);
if (!Number.isFinite(numeric)) return String(value);
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR',
maximumFractionDigits: 2,
}).format(numeric);
}
export function signedClass(value) {
const numeric = Number(value);
if (!Number.isFinite(numeric)) return '';
if (numeric > 0) return 'value-positive';
if (numeric < 0) return 'value-negative';
return '';
}
export function truncateMiddle(value, maxLength = 40) {
const text = String(value || '');
if (!text || text.length <= maxLength) return text;
const visible = Math.max(8, maxLength - 1);
const startLength = Math.ceil(visible / 2);
const endLength = Math.floor(visible / 2);
return `${text.slice(0, startLength)}${text.slice(-endLength)}`;
}
export function stringifyJson(value) {
return JSON.stringify(value, null, 2);
}