/// Month-grid helpers for the transactions heatmap. library; DateTime dateOnly(DateTime date) => DateTime(date.year, date.month, date.day); DateTime monthStart(DateTime date) => DateTime(date.year, date.month); bool isSameDay(DateTime a, DateTime b) => a.year == b.year && a.month == b.month && a.day == b.day; bool isSameMonth(DateTime a, DateTime b) => a.year == b.year && a.month == b.month; DateTime addMonths(DateTime month, int delta) => DateTime(month.year, month.month + delta); /// Monday-first calendar cells for [month], padded with nulls to full weeks. List monthGrid(DateTime month) { final start = DateTime(month.year, month.month, 1); final daysInMonth = DateTime(month.year, month.month + 1, 0).day; final leading = start.weekday - DateTime.monday; final cells = [ ...List.filled(leading, null), for (var day = 1; day <= daysInMonth; day++) DateTime(month.year, month.month, day), ]; while (cells.length % 7 != 0) { cells.add(null); } return cells; } /// How many transactions landed on each calendar day. Map txCountsByDay(Iterable dates) { final counts = {}; for (final date in dates) { final key = dateOnly(date); counts[key] = (counts[key] ?? 0) + 1; } return counts; } /// GitHub-style 0–4 intensity from a transaction count. int heatmapLevel(int count) { if (count <= 0) return 0; if (count >= 4) return 4; return count; }