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]>
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-bottomusingenv(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
BeforeInstallPromptEventinterface and does not add an unsafe global declaration. beforeinstallpromptandappinstalledlisteners are removed on unmount.- Standalone detection suppresses the CTA, and unsupported browsers remain unchanged.
- iOS instructions are limited to Safari and the existing
Modalowns 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 AgendaMaxin 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 --checkreports 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 whenprompt()oruserChoicefails, the error is contained, and the state returns toavailablefor retry.appinstallednow clears dismissal before settinginstalled.src/components/Modal.tsx: addedaria-label="Cerrar"to the shared icon-only close button.src/components/InstallAppPrompt.tsx: added an explicitcompactpresentation. 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.tsxandsrc/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.currentis 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
availablestate. - A later
appinstalledevent overrides any prior dismissal and returns the hook state asinstalled. - 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.