You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
42 lines
1.4 KiB
TypeScript
42 lines
1.4 KiB
TypeScript
/** Pure math for the relationship-pulse ribbon (Client 360 overview). */
|
|
|
|
export interface PulseEvent {
|
|
id: string
|
|
lane: number
|
|
date: string // ISO yyyy-mm-dd
|
|
label: string
|
|
tone: 'accent' | 'ok' | 'warn' | 'err'
|
|
}
|
|
|
|
export const LANES = ['Documents', 'Payments', 'Interactions', 'AMC & renewals'] as const
|
|
|
|
/** n month keys ('YYYY-MM') ending at todayIso's month, oldest first. */
|
|
export function lastMonths(todayIso: string, n = 12): string[] {
|
|
const year = Number(todayIso.slice(0, 4))
|
|
const month = Number(todayIso.slice(5, 7)) // 1-based
|
|
const out: string[] = []
|
|
for (let i = n - 1; i >= 0; i--) {
|
|
const total = year * 12 + (month - 1) - i
|
|
const y = Math.floor(total / 12)
|
|
const m = total % 12 + 1
|
|
out.push(`${y}-${String(m).padStart(2, '0')}`)
|
|
}
|
|
return out
|
|
}
|
|
|
|
/** Bucket events into `${lane}:${monthIndex}` cells; out-of-window events are dropped. */
|
|
export function groupPulse(events: PulseEvent[], months: string[]): Map<string, PulseEvent[]> {
|
|
const index = new Map<string, number>()
|
|
months.forEach((m, i) => index.set(m, i))
|
|
const out = new Map<string, PulseEvent[]>()
|
|
for (const ev of events) {
|
|
const mi = index.get(ev.date.slice(0, 7))
|
|
if (mi === undefined) continue
|
|
const key = `${ev.lane}:${mi}`
|
|
const cell = out.get(key)
|
|
if (cell !== undefined) cell.push(ev)
|
|
else out.set(key, [ev])
|
|
}
|
|
return out
|
|
}
|