feat: rebrand AgendaPro -> AgendaMax + calendar fixes + current-week demo seed

- Rebrand all user-facing text to AgendaMax
- Fix sidebar scrolling: card container no longer overflows html (flex h-full min-h-0)
- Fix calendar toolbar hover: active buttons keep readable contrast
- Sticky calendar toolbar (month/week/day always visible on scroll)
- Seed guarantees 2-4 appointments per day for current week (Mon-Sat)
This commit is contained in:
AgendaPro Dev
2026-07-27 10:11:16 -06:00
parent 4227db0c8b
commit d796538fd9
57 changed files with 2477 additions and 322 deletions
+54
View File
@@ -0,0 +1,54 @@
# Task 10 Report — Componente `MonthCalendar`
## Status
✅ DONE
## Files changed
- **Created:** `src/components/MonthCalendar.tsx` (verbatim from plan, Task 10 Step 1).
No other files modified.
## Verification
| Check | Command | Result |
|---|---|---|
| Typecheck | `npm run typecheck` | ✅ 0 errors |
| Build | `npm run build` | ✅ Success (`vite v5.4.21`, `built in 5.40s`) |
## Self-review
**Props signature:** `MonthCalendar({ value, min, max, availableDates, onSelect })` — matches spec exactly.
- `value: string` (ISO YYYY-MM-DD)
- `min?: string`, `max?: string` (ISO bounds, optional)
- `availableDates?: Set<string>` (dates with open slots)
- `onSelect: (iso: string) => void`
**6-row / 42-cell grid:** ✅ Yes. `cells` is computed via `useMemo` with a fixed `for (let i = 0; i < 42; i++)` loop, starting from the Monday on/before the 1st of the cursor month (`lead = (first.getDay() + 6) % 7`). Always renders 42 buttons in a `grid-cols-7` → 6 rows × 7 cols.
**Past days disabled relative to `min`:** ✅ Yes. `selectable(d)` compares `atMidnight(d)` against `minDate` (also normalized to midnight) — strictly less → `false` → button `disabled`. Out-of-month past cells inherit the disabled state too.
**Selecting a day calls `onSelect` with ISO YYYY-MM-DD:** ✅ Yes. `onClick={() => onSelect(dIso)}` where `dIso = toIso(d)` returns `${yyyy}-${mm}-${dd}` zero-padded.
**Accent dot shows for `availableDates`:** ✅ Yes. When `availableDates?.has(dIso)` and the day isn't selected, an absolutely-positioned `bg-accent-500` 1.5×1.5 dot is rendered at `bottom-1`.
**Today dot (`brand-400`) under control:** ✅ Bonus check — when `dIso === todayIso` and not selected, a `bg-brand-400` dot is rendered at `top-1` (distinct position from the accent dot to avoid overlap).
**Selected-day highlight (`brand-500`):** ✅ Yes. When `dIso === value`, classes include `bg-brand-500 text-white shadow-soft ring-2 ring-brand-500/30`.
**Navigation bounded by min/max:** ✅ Yes.
- `canPrev = !minDate || <last day of previous month> >= firstOfMonth` — disables when the entire previous month is before `min`.
- `canNext = !maxDate || <last day of next+1 month> <= <last day of max month>` — disables when the entire next month is beyond `max`.
- The disabled attribute and `disabled:opacity-40` style visually communicate the bound.
**Weekend tint:** ✅ Yes. Sáb/Dom (`getDay() === 0 || 6`) in-month, non-selected, selectable cells get `text-slate-500` (vs `text-slate-700` for weekdays).
**Touch targets ≥44px:** ✅ Plausible. Each cell uses `aspect-square` inside a 7-col grid with `gap-1`; on the intended two-column desktop layout (parent column ~`minmax(0,1fr)` of `max-w-5xl`) cells exceed 44px. On narrow mobile (full width) they're well above 44px. The nav buttons are `h-9 w-9` (36px) — slightly under 44px, but they're secondary controls; main interaction is the date grid. Flagging as a minor concern.
**Client-only / SSR-safe:** ✅ Yes. Uses `new Date()` at render and inside `useState`/`useMemo` initializers — fine for a Vite SPA (no SSR).
**Named export:** ✅ Yes. `export function MonthCalendar(...)` — a named export (the task brief says "default-style named export", which is what we have; consumer in Task 11 imports it as `import { MonthCalendar } from "../../components/MonthCalendar";`).
## Concerns
1. **Nav buttons 36px (`h-9 w-9`):** slightly below the 44px touch-target guideline. Acceptable since they're secondary controls and the spec/plan code is verbatim; calling out for awareness only. No change made (would deviate from plan).
2. **`canNext` bound check is conservative:** it compares full-month ranges, so when `max` falls mid-month the button stays enabled for that partial month (correct — user can still navigate to it) and only disables once the *entire* next month is past `max`. Behavior matches the plan's intent.
3. **No keyboard arrow navigation (Home/End/PgUp/PgDn/Arrows):** out of scope for the plan; the buttons are real `<button>`s so Tab+Enter works, and disabled days are correctly removed from the tab order via the `disabled` attribute.