/** * Number and date formatting utilities */ /** * Format CO₂ emissions with appropriate unit * @param {number} kg - emissions in kg * @returns {string} */ export const formatEmission = (kg) => { if (kg === null || kg === undefined) return '—'; if (kg >= 1000) { return `${(kg / 1000).toFixed(2)} tonnes`; } return `${Math.round(kg)} kg`; }; /** * Format CO₂ emissions always in tonnes * @param {number} kg * @returns {string} */ export const formatEmissionTonnes = (kg) => { if (kg === null || kg === undefined) return '—'; return `${(kg / 1000).toFixed(2)} t CO₂e`; }; /** * Format a number with commas * @param {number} n * @returns {string} */ export const formatNumber = (n) => { if (n === null || n === undefined) return '—'; return new Intl.NumberFormat('en-US').format(Math.round(n)); }; /** * Format percentage * @param {number} n - value 0-100 * @returns {string} */ export const formatPercent = (n) => { if (n === null || n === undefined) return '—'; return `${Math.round(n)}%`; }; /** * Format a date to readable string * @param {string|Date} date * @returns {string} */ export const formatDate = (date) => { if (!date) return '—'; return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', }).format(new Date(date)); }; /** * Format a date to short format * @param {string|Date} date * @returns {string} */ export const formatDateShort = (date) => { if (!date) return '—'; return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric', }).format(new Date(date)); }; /** * Format relative time (e.g. "2 days ago") * @param {string|Date} date * @returns {string} */ export const formatRelativeTime = (date) => { if (!date) return '—'; const now = new Date(); const d = new Date(date); const diffMs = now - d; const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); if (diffDays === 0) return 'Today'; if (diffDays === 1) return 'Yesterday'; if (diffDays < 7) return `${diffDays} days ago`; if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`; if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`; return `${Math.floor(diffDays / 365)} years ago`; }; /** * Calculate percentage change between two values * @param {number} original * @param {number} updated * @returns {number} - positive = increase, negative = decrease */ export const percentChange = (original, updated) => { if (!original || original === 0) return 0; return ((updated - original) / original) * 100; }; /** * Get score label and color * @param {number} score - 0-100 * @returns {{ label: string, color: string, bgClass: string, textClass: string }} */ export const getScoreInfo = (score) => { if (score >= 90) return { label: 'Excellent', color: '#10b981', bgClass: 'bg-emerald-500/20', textClass: 'text-emerald-400', borderClass: 'border-emerald-500/30', }; if (score >= 70) return { label: 'Good', color: '#06b6d4', bgClass: 'bg-cyan-500/20', textClass: 'text-cyan-400', borderClass: 'border-cyan-500/30', }; if (score >= 50) return { label: 'Moderate', color: '#f59e0b', bgClass: 'bg-amber-500/20', textClass: 'text-amber-400', borderClass: 'border-amber-500/30', }; return { label: 'Needs Improvement', color: '#ef4444', bgClass: 'bg-rose-500/20', textClass: 'text-rose-400', borderClass: 'border-rose-500/30', }; }; /** * Get priority badge classes * @param {string} priority - HIGH | MEDIUM | LOW */ export const getPriorityInfo = (priority) => { switch (priority?.toUpperCase()) { case 'HIGH': return { label: 'High Priority', bgClass: 'bg-rose-500/20', textClass: 'text-rose-400', borderClass: 'border-rose-500/30', }; case 'MEDIUM': return { label: 'Medium Priority', bgClass: 'bg-amber-500/20', textClass: 'text-amber-400', borderClass: 'border-amber-500/30', }; case 'LOW': return { label: 'Low Priority', bgClass: 'bg-sky-500/20', textClass: 'text-sky-400', borderClass: 'border-sky-500/30', }; default: return { label: priority, bgClass: 'bg-slate-500/20', textClass: 'text-slate-400', borderClass: 'border-slate-500/30', }; } };