- 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)
55 lines
4.1 KiB
Markdown
55 lines
4.1 KiB
Markdown
# 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.
|