first commit

This commit is contained in:
Alberto-Audrix committed 2026-09-08 15:21:23 +07:00
commit b54624be96
226 files changed
+108840

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
export function formatNumber(
value: number,
maxDecimals = 2,
minDecimals = 0,
truncate = false
): string {
let display = value;
if (truncate) {
const factor = 10 ** maxDecimals;
display = Math.trunc(display * factor) / factor;
}
return display.toLocaleString('id-ID', {
maximumFractionDigits: maxDecimals,
minimumFractionDigits: minDecimals,
});
}
export function formatRatio(value: number, decimals = 3): string {
const factor = 10 ** decimals;
const truncated = Math.trunc(value * factor) / factor;
return truncated.toFixed(decimals);
}
export function formatDateId(iso: string | null | undefined): string {
if (!iso) return '—';
const date = new Date(`${iso}T00:00:00`);
if (Number.isNaN(date.getTime())) return iso;
return date.toLocaleDateString('id-ID', { day: 'numeric', month: 'short', year: 'numeric' });
}
export function formatDateRange(startIso: string, endIso?: string | null): string {
if (!endIso) return formatDateId(startIso);
return `${formatDateId(startIso)} – ${formatDateId(endIso)}`;
}
export function todayIso(): string {
const now = new Date();
const pad = (n: number) => String(n).padStart(2, '0');
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
}
export function addDaysIso(iso: string, days: number): string {
const date = new Date(`${iso}T00:00:00`);
date.setDate(date.getDate() + days);
const pad = (n: number) => String(n).padStart(2, '0');
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
}
export function cycleDayForDate(startDate: string, isoDate: string): number {
const start = new Date(`${startDate}T00:00:00`);
const current = new Date(`${isoDate}T00:00:00`);
const diff = Math.round((current.getTime() - start.getTime()) / 86400000);
return Math.max(0, diff);
}
export function inclusiveDayCount(startIso: string, endIso: string): number {
return Math.max(1, cycleDayForDate(startIso, endIso) + 1);
}
export type CycleStatusUi = 'active' | 'pending_close' | 'closed';
export function resolveCycleStatus(cycle: {
status?: string;
end_date?: string | null;
visible_through_date?: string;
}): CycleStatusUi {
if (cycle.status === 'pending_close' || cycle.status === 'closed' || cycle.status === 'active') {
return cycle.status;
}
if (cycle.end_date && cycle.visible_through_date && cycle.visible_through_date >= cycle.end_date) {
return 'closed';
}
return 'active';
}