import { Box, Center, Group, Loader, Paper, Popover, Select, SimpleGrid, Stack, Text, Title, UnstyledButton, } from "@mantine/core"; import { MonthPickerInput } from "@mantine/dates"; import { IconCheckbox, IconHourglass, IconPlus } from "@tabler/icons-react"; import dayjs from "dayjs"; import { useEffect, useMemo, useState } from "react"; import * as api from "../api"; import type { Card, Metrics, User } from "../types"; interface Props { users: User[]; cards: Card[]; onJumpToCard?: (cardId: string) => void; } const DAY_LABELS = ["Lun", "Mar", "Mie", "Jue", "Vie", "Sab", "Dom"]; export function CalendarView({ users, cards, onJumpToCard }: Props) { const [openDate, setOpenDate] = useState(null); const [month, setMonth] = useState(new Date()); const [assigneeId, setAssigneeId] = useState(null); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { let cancelled = false; setLoading(true); const start = dayjs(month).startOf("month").format("YYYY-MM-DD"); const end = dayjs(month).endOf("month").format("YYYY-MM-DD"); api .getMetrics({ from: start, to: end, assignee_id: assigneeId || undefined }) .then((m) => { if (!cancelled) setData(m); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [month, assigneeId]); const userOptions = useMemo( () => users.map((u) => ({ value: u.id, label: u.display_name || u.username })), [users] ); const dayMap = useMemo(() => { const m = new Map(); if (!data) return m; for (const d of data.created_daily) { const cur = m.get(d.date) ?? { created: 0, done: 0, deadlines: [] }; cur.created = d.count; m.set(d.date, cur); } for (const d of data.throughput_daily) { const cur = m.get(d.date) ?? { created: 0, done: 0, deadlines: [] }; cur.done = d.count; m.set(d.date, cur); } for (const c of cards) { if (!c.deadline || c.deleted_at) continue; const date = c.deadline.slice(0, 10); const cur = m.get(date) ?? { created: 0, done: 0, deadlines: [] }; cur.deadlines.push(c); m.set(date, cur); } return m; }, [data, cards]); // Build month grid (Mon-first). const grid = useMemo(() => { const start = dayjs(month).startOf("month"); const end = dayjs(month).endOf("month"); // Day-of-week, ISO Mon=1..Sun=7. We want first cell to be Mon. const firstDow = (start.day() + 6) % 7; // 0=Mon const cells: { date: string | null; inMonth: boolean }[] = []; for (let i = 0; i < firstDow; i++) cells.push({ date: null, inMonth: false }); for (let d = start; !d.isAfter(end); d = d.add(1, "day")) { cells.push({ date: d.format("YYYY-MM-DD"), inMonth: true }); } while (cells.length % 7 !== 0) cells.push({ date: null, inMonth: false }); return cells; }, [month]); const totalCreated = useMemo( () => Array.from(dayMap.values()).reduce((s, v) => s + v.created, 0), [dayMap] ); const totalDone = useMemo( () => Array.from(dayMap.values()).reduce((s, v) => s + v.done, 0), [dayMap] ); return ( Calendario v && setMonth(typeof v === "string" ? new Date(v) : v)} style={{ minWidth: 160 }} clearable={false} />