Files
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

5.1 KiB

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:

npm.cmd run typecheck

Output:

> [email protected] typecheck
> tsc -b --noEmit

Exit code: 0.

Production build

Command:

npm.cmd run build

Output summary:

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:

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:

npm.cmd run typecheck

Output:

> [email protected] typecheck
> tsc -b --noEmit

Exit code: 0.

Production build

Command:

npm.cmd run build

Output summary:

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.