import { type ReactNode } from "react"; import { Box, Group, Paper, SimpleGrid, Stack, Text } from "@mantine/core"; import { IconCheckbox, IconPlus } from "@tabler/icons-react"; import { monthGrid } from "../core/month_grid"; export interface MonthHeatmapCell { primary?: number; secondary?: number; } export interface MonthHeatmapProps { /** Mes a mostrar (se usan year y month, el dia se ignora). */ month: Date; /** Datos por dia, key "YYYY-MM-DD". */ cells: Map; /** Icono junto al contador primary. Default: IconPlus. */ primaryIcon?: ReactNode; /** Icono junto al contador secondary. Default: IconCheckbox. */ secondaryIcon?: ReactNode; /** Color Mantine del counter primary. Default: "blue". */ primaryColor?: string; /** Color Mantine del counter secondary. Default: "green". */ secondaryColor?: string; /** Labels de cabecera, 7 elementos empezando por lunes. */ dayLabels?: string[]; /** Altura minima de cada celda en px. Default: 72. */ cellMinHeight?: number; } const DEFAULT_DAY_LABELS = ["Lun", "Mar", "Mie", "Jue", "Vie", "Sab", "Dom"]; /** * Grid mensual de calor (heatmap) con inicio en lunes. * * Renderiza 7 columnas con header de dias de la semana. * Cada celda muestra el numero del dia y hasta dos contadores opcionales * (primary / secondary) con icono. El dia de hoy recibe borde azul y * numero en negrita. El fondo de la celda se tinta segun los valores: * secondary > 0 → verdoso, primary > 0 → azulado. * Las celdas de relleno (fuera del mes) se renderizan vacias. */ export function MonthHeatmap({ month, cells, primaryIcon, secondaryIcon, primaryColor = "blue", secondaryColor = "green", dayLabels = DEFAULT_DAY_LABELS, cellMinHeight = 72, }: MonthHeatmapProps) { const year = month.getFullYear(); const monthNum = month.getMonth() + 1; // getMonth() es 0-based const grid = monthGrid(year, monthNum); // Hoy como "YYYY-MM-DD" usando Date nativo const now = new Date(); const todayStr = [ now.getFullYear(), String(now.getMonth() + 1).padStart(2, "0"), String(now.getDate()).padStart(2, "0"), ].join("-"); const resolvedPrimaryIcon = primaryIcon ?? ; const resolvedSecondaryIcon = secondaryIcon ?? ; return ( {/* Header dias de la semana */} {dayLabels.map((label) => ( {label} ))} {/* Celdas del mes */} {grid.map((cell, i) => { if (!cell.date) { return ; } const data = cells.get(cell.date) ?? {}; const primary = data.primary ?? 0; const secondary = data.secondary ?? 0; const dayNum = parseInt(cell.date.slice(8, 10), 10); const isToday = cell.date === todayStr; const background = secondary > 0 ? "rgba(81, 207, 102, 0.08)" : primary > 0 ? "rgba(34, 139, 230, 0.06)" : undefined; return ( {dayNum} {primary > 0 && ( {resolvedPrimaryIcon} {primary} )} {secondary > 0 && ( {resolvedSecondaryIcon} {secondary} )} ); })} ); }