Files
AgendaPro/.superpowers/sdd/task-3-report.md
T
AgendaPro DevandClaude Opus 5 4c19244df9 feat: public landing page with magic login, brand palette and demo build flag
Adds a public funnel landing at `/` and moves the login to `/login`, rebuilt
around the panel's own colors instead of an invented palette.

Landing (`src/components/landing/`, one section per file, no props):
- Seven funnel sections composed by `LandingPage`. The hero's eight swatches are
  literally `PIE_COLORS` from `DashboardPage`, the same hex values the seed hands
  to avatars and services, so "your colors become your numbers" is literal.
- `BrandMark` becomes the single source for the logo, replicating
  `public/favicon.svg`. Blue is the action surface, orange only ever marks.
- `NotebookVisual` is the one deliberate exception to the palette: it is what the
  product replaces.
- Copy drops all system vocabulary; motion comes from `src/lib/motion.ts` with a
  single easing, and reduced-motion resolves `initial` to the final state so a
  never-firing `whileInView` cannot leave a section invisible forever.

Routing and bundle:
- `homePathFor` is the single definition of each role's destination.
- Landing, login, dashboard and calendar load lazily. Eager, the login dragged
  framer-motion (~40 KB gz) into every panel load and the landing downloaded
  recharts + FullCalendar (~187 KB gz) without charting anything. `clsx` is
  pinned to the `react` chunk because Rollup otherwise assigns it to `charts`,
  making the entry import 111 KB gz for a 200-byte utility.

Responsiveness (iPhone/iPad), verified with `npm run audit:responsive`:
- No touch form field below 16px, `dvh` height utilities, safe-area insets, and
  40px touch targets keyed off `pointer: coarse` rather than `sm:`.
- New `.ld-gutter`: `.safe-x` lives outside `@layer` and beats Tailwind's `px-*`,
  so it left the login's side padding at 0 on anything but an iPhone in
  landscape. No overflow check could see it — there was no overflow, just zero
  margin. The audit now guards it with a `gutter` check.
- `shell-height` no longer fires on pages that legitimately scroll; the static
  `raw-viewport-unit` scan covers those instead.

Production build:
- The Dockerfile now sets `VITE_DEMO_UI=1` as a build arg. `DEMO` is a build-time
  constant, so without it Vite eliminated the magic login and the "Ver como…"
  switcher: the deployed landing promised "no registration" and led to an empty
  form. Verified by building both ways and diffing the bundle.
- Service worker cache bumped to v2 so the orphaned pre-landing chunks get purged
  from returning visitors' caches.

Verified: typecheck clean; unit 39/39, e2e 33/33, admin 17/17, booking 12/12,
landing 13/13 (WebKit), PWA passed against the real production build.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-28 14:25:58 -06:00

146 lines
5.1 KiB
Markdown

# Task 3 Report: Add Install Detection and UI
## Status
Implemented Task 3 only. The install hook detects standalone mode, Chromium's deferred install event, and iOS Safari instructions. The reusable prompt is rendered once in each required shell surface and uses the existing modal/button/icon patterns.
## Changed files
- `src/lib/useInstallPrompt.ts`: added the browser-only install state hook, local deferred-event type, event listener lifecycle, install action, and dismissal behavior.
- `src/components/InstallAppPrompt.tsx`: added the reusable Chromium/iOS install action and iOS instructions modal.
- `src/index.css`: added `.safe-area-bottom` using `env(safe-area-inset-bottom)`.
- `src/pages/LoginPage.tsx`: added one compact install action below the login form.
- `src/components/AppShell.tsx`: added one install action to the mobile header.
- `src/components/AdminShell.tsx`: added one install action to the mobile header.
## Verification
### Typecheck
Command:
```text
npm.cmd run typecheck
```
Output:
```text
> [email protected] typecheck
> tsc -b --noEmit
```
Exit code: `0`.
### Production build
Command:
```text
npm.cmd run build
```
Output summary:
```text
Preflight OK: Node v26.4.0.
vite v5.4.21 building for production...
✓ 2465 modules transformed.
✓ built in 4.08s
```
Exit code: `0`.
### Diff validation
Command:
```text
git diff --check
```
Result: no whitespace errors. Git emitted only existing LF/CRLF conversion notices for worktree files.
## Self-review
- The hook uses a local `BeforeInstallPromptEvent` interface and does not add an unsafe global declaration.
- `beforeinstallprompt` and `appinstalled` listeners are removed on unmount.
- Standalone detection suppresses the CTA, and unsupported browsers remain unchanged.
- iOS instructions are limited to Safari and the existing `Modal` owns backdrop/Escape close behavior.
- The safe-area utility is applied only inside the iOS modal content.
- Each shell renders exactly one prompt instance outside `SidebarContent`, avoiding duplicate listeners from the desktop/mobile sidebar duplication.
- The accessible action text remains `Instalar AgendaMax` in every supported prompt.
- No Task 4 tests or documentation changes were added.
## Concerns
- No browser-level visual/manual check was run; verification was limited to the required typecheck, production build, and diff inspection.
- `git diff --check` reports line-ending notices from the existing Windows worktree configuration, not whitespace failures.
## Preserved worktree changes
Existing `.superpowers/sdd/*` changes and untracked `graphify-out/*` artifacts were not reverted or staged.
## Review Fixes
- `src/lib/useInstallPrompt.ts`: added a ref-based in-flight guard so rapid clicks can invoke the native prompt only once. The deferred event is restored when `prompt()` or `userChoice` fails, the error is contained, and the state returns to `available` for retry. `appinstalled` now clears dismissal before setting `installed`.
- `src/components/Modal.tsx`: added `aria-label="Cerrar"` to the shared icon-only close button.
- `src/components/InstallAppPrompt.tsx`: added an explicit `compact` presentation. Header instances use a fixed icon button with a screen-reader label and tooltip, while Login keeps the full visible action label.
- `src/components/AppShell.tsx` and `src/components/AdminShell.tsx`: enabled the compact prompt presentation in the narrow mobile headers.
## Review Fix Verification
### Typecheck
Command:
```text
npm.cmd run typecheck
```
Output:
```text
> [email protected] typecheck
> tsc -b --noEmit
```
Exit code: `0`.
### Production build
Command:
```text
npm.cmd run build
```
Output summary:
```text
Preflight OK: Node v26.4.0.
vite v5.4.21 building for production...
✓ 2465 modules transformed.
✓ built in 3.95s
```
Exit code: `0`.
### Diff inspection
`git diff --check` reported no whitespace errors. The only messages were existing Windows LF/CRLF conversion notices. The implementation diff contains only the hook, shared prompt, shared modal accessibility, and two mobile header call-site fixes described above.
## Review Fix Self-Review
- A second click while `installInFlight.current` is true returns immediately, including clicks occurring after React has scheduled the deferred-event state update.
- Failed native prompt or choice promises no longer produce an unhandled rejection and restore the captured event for a retryable `available` state.
- A later `appinstalled` event overrides any prior dismissal and returns the hook state as `installed`.
- Both modal close mechanisms remain intact, and the icon-only close control now has an accessible name.
- Header install controls are compact without hiding the action from assistive technology; the full accessible name remains `Instalar AgendaMax`.
- No unrelated worktree files were modified or staged, and no Task 4 tests or documentation were added.
## Remaining Concerns
- No browser-level visual/manual check was run; verification is limited to typecheck, production build, and source diff inspection.
- The existing Windows worktree continues to emit LF/CRLF conversion notices during Git operations.