feat: version 4.8.0 -- Agenda medica en vista de calendario (mes y semana)

Vista Mes tipo calendario (componente reutilizable MonthGrid) y vista Semana con
las citas de todos los medicos filtrados, coloreadas por medico; leyenda de
colores y vista elegida persistida.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-10-28 13:34:23 -05:00
parent 1e25022630
commit 91e15e8318
12 changed files with 106 additions and 28 deletions
+30
View File
@@ -0,0 +1,30 @@
// Cuadricula mensual (lunes a domingo) con las clases .calendar-* de app.css. El contenido de cada
// dia lo decide quien la usa (renderDay); clic en el dia -> onDayClick(YYYY-MM-DD).
const pad = (n) => String(n).padStart(2, '0');
const key = (d) => `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
// Primer lunes visible y los 35/42 dias que cubren el mes de `day` (YYYY-MM-DD).
export function monthGridDays(day) {
const [y, m] = day.split('-').map(Number);
const first = new Date(Date.UTC(y, m - 1, 1));
const start = new Date(first.getTime() - ((first.getUTCDay() + 6) % 7) * 86400000);
const daysInMonth = new Date(Date.UTC(y, m, 0)).getUTCDate();
const cells = Math.ceil((((first.getUTCDay() + 6) % 7) + daysInMonth) / 7) * 7;
return Array.from({ length: cells }, (_, i) => key(new Date(start.getTime() + i * 86400000)));
}
export function MonthGrid({ day, weekLabels, todayKey, renderDay, onDayClick }) {
const month = day.slice(0, 7);
return (
<div className="calendar-grid">
{weekLabels.map((label) => <div key={label} className="calendar-dow">{label}</div>)}
{monthGridDays(day).map((d) => (
<div key={d} className={`calendar-cell ${d.slice(0, 7) !== month ? 'muted' : ''} ${d === todayKey ? 'today' : ''}`}
onClick={() => onDayClick?.(d)} style={{ cursor: onDayClick ? 'pointer' : undefined, minWidth: 0 }}>
<div className="calendar-day-num">{Number(d.slice(8, 10))}</div>
{renderDay(d)}
</div>
))}
</div>
);
}