/** period_index is 1-based (Sem 1 … Sem N). */ export function ganttSpan( activeIndexes: number[], periodCount: number, ): { leftPct: number; widthPct: number; first: number; last: number } | null { const n = Number(periodCount) || 0; const idxs = activeIndexes.filter((i) => Number.isFinite(i) && i >= 1 && (n <= 0 || i <= n)); if (!idxs.length || n <= 0) return null; const first = Math.min(...idxs); const last = Math.max(...idxs); return { first, last, leftPct: ((first - 1) / n) * 100, widthPct: ((last - first + 1) / n) * 100, }; } export function periodHasAmount( periods: { period_id?: number; period_index?: number; amount?: number }[], week: { id: number; period_index: number }, ): number { const slot = periods.find((x) => x.period_index === week.period_index || x.period_id === week.id); return Number(slot?.amount || 0); } export function periodHasConceptBar( periods: { period_index?: number; start_pct?: number; end_pct?: number }[], week: { period_index: number }, ): { start_pct: number; end_pct: number } | null { const slot = periods.find((x) => x.period_index === week.period_index); if (!slot) return null; const start = Number(slot.start_pct || 0); const end = Number(slot.end_pct || 0); if (start === 0 && end === 0) return null; return { start_pct: start, end_pct: Math.max(start, end) }; }