export function formatDate(value: string, seconds = false): string { const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', ...(seconds ? { second: '2-digit' } : {}), }; return new Date(value).toLocaleString('it-IT', options); } export const capitalize = (str: string) => str .toLowerCase() .split(' ') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); export type SplitMode = 'underscore' | 'camel' | 'auto'; /** Title-case a string while preserving short all-caps acronyms (e.g., XML) */ const toTitleCase = (s: string) => s .trim() .toLowerCase() .split(/\s+/) .map((w) => /^[A-Z]{2,4}$/.test(w) ? w : w.charAt(0).toUpperCase() + w.slice(1).toLowerCase(), ) .join(' '); const splitUnderscoreHyphen = (s: string) => s.replaceAll(/[_-]+/g, ' '); /** Insert spaces at camelCase boundaries and around digit/letter edges */ const splitCamel = (s: string) => s // fooBar -> foo Bar ; foo2D -> foo 2D .replaceAll(/([a-z0-9])([A-Z])/g, '$1 $2') // XMLHttp -> XML Http (acronym + word) .replaceAll(/([A-Z])([A-Z][a-z])/g, '$1 $2') // letter<->digit boundaries .replaceAll(/([a-zA-Z])([0-9])/g, '$1 $2') .replaceAll(/([0-9])([a-zA-Z])/g, '$1 $2'); /** * Split and capitalize either by underscores/hyphens or camelCase. * mode: * - "underscore": split on _ or - * - "camel": split on camelCase boundaries * - "auto": pick underscore if present, otherwise camel */ export function splitAndCapitalize(str?: string, mode: SplitMode = 'auto'): string { if (!str) return ''; // normalize underscores/hyphens first for auto decision const hasUnderscoreLike = /[_-]/.test(str); const chosen: SplitMode = mode === 'auto' ? (hasUnderscoreLike ? 'underscore' : 'camel') : mode; const spaced = chosen === 'underscore' ? splitUnderscoreHyphen(str) : splitCamel(str); // collapse extra spaces, then title-case return toTitleCase(spaced.replaceAll(/\s+/g, ' ').trim()); }