makepad-ports/insurepay_assessment.md
andodeki 7ddd75fb70 Makepad 2.0 ports: rider, koboyo, insurance (+ HTML sources, assessments)
Three single-file HTML apps ported to Makepad 2.0 (dev b41e740) script_mod! DSL:
- rider (ride.html): 21 unit + 9 integration + 9 headless UI tests
- koboyo (koboyo.html): 29 + 14 + 8
- insurance (insurance.html): 28 + 14 + 8
All 140 tests green. Includes headless-Linux platform patch, full README,
setup script, and three brutal codebase assessments with execution plans.
2026-08-04 05:07:18 +00:00

21 KiB
Raw Permalink Blame History

InsurePay ("nigig-pay" / insurance.html) — Brutal Professional Assessment

Artifact reviewed: /home/user/insurance.html — 998 lines, 56.8 KB, single file containing HTML + 700 lines of bespoke CSS + ~150 lines of inline JS. Reviewer verdict: a visually polished design prototype that is unshippable as a product in its current form. Roughly a third of the UI is inert decoration, the interaction layer has real correctness bugs, there is no test of any kind, and it embeds realistic PII in public markup.

Scorecard (010, strictly as production code):

Axis Score One-liner
Architecture 3.5 Single global script, DOM-as-state, stringly-typed routing
Code quality 4.0 Readable but duplicated, dead code, zero tests/tooling
Bugs / correctness 4.0 Gesture math, dead affordances, unreachable code paths
Performance 6.0 Self-contained & light, but backdrop-filter stacking and filter animation are GPU traps
Design / UX 6.5 Genuinely pretty; confusing IA, 40% interactive-looking elements do nothing
Security / privacy 3.0 No auth model, realistic PII hardcoded, no CSP; currently low attack surface only because nothing is wired
Accessibility 2.5 user-scalable=no, 10px labels, no focus management, no aria-live, no keyboard sheet control
Overall 4.2 Demo-grade. Not production-grade.

Credit where due (one paragraph, then we get harsh): the file is fully self-contained with zero dependencies and zero network cost; icons are inline SVG (no icon-font garbage); the gesture code at least attempts real physics (velocity, clamps, tap vs. drag disambiguation); sectional CSS comments are disciplined; and hit targets are mostly ≥38 px. That ends the good news.


1. Architecture

A1 — DOM is the state machine. Screen visibility, chip selection, sheet mode, and toast message are all encoded as DOM classes (classList.toggle) with data-go/data-toast/data-stab strings as the only "API". The only actual state variable in 150 lines of JS is sheetMode (insurance.html:925). There is no model, no store, no events, no types. Every behavior mutation has to know which classes to flip. This is why the Makepad port had to rebuild the whole thing as model.rs — the original has no extractable logic.

A2 — Stringly-typed routing with silent failure. go(name) (:885) looks up scr-<name>; a typo in any data-go attribute (data-go="policy" instead of policies) fails silently — no console error, no fallback. There are 14 data-go sites and nothing validates them.

A3 — No history/routing integration. The four "screens" are div-switching. The browser Back button exits the app entirely; there are no URLs, no deep links, no refresh-safe state. For an app with auth and payments (renewals), this alone disqualifies it.

A4 — Content is hardcoded in markup. Agent names/roles, policy numbers, prices ($214/$112), claim amounts (+$234.00), support email — all baked into HTML. There is no data layer, not even a JSON block. Any business change requires editing markup; there is no seam where an API would attach.

A5 — Four duplicated component families. The bell button is copy-pasted 4× (identical 5-line SVG), the back chevron 3×, the "↗" arrow 5×, the renew pill 2×, avatar stacks 2×. No <symbol>/sprite, no template, no JS component. Change the bell → edit 4 places. This is copy-paste-driven development.

A6 — Global scope pollution. The script has no IIFE/module wrapper: screens, tabs, go, showToast, toast, sheetMode, dragging, vy, moved et al. are all globals. Any third-party script collides silently. No 'use strict', no JSDoc types, no lint config, no package.json, no build, no source control hygiene visible (no version, no license, no author).

A7 — Error handling: zero. grab.setPointerCapture(e.pointerId) (:951) can throw NotFoundError in synthetic/edge environments; no try/catch anywhere in the file. One thrown pointer error bricks the sheet gesture for the session.

A8 — Decision ambiguity encoded as three same-named sections. "Quick Actions" exists on Home (2 rows), on Policies (2 policy cards), and in the FAB sheet (4 tiles) — three different datasets under one label. "File a Claim" appears 3 ways with three different outcomes (+$234.00 reward row, "Claim registration opened ✓", "Claim form opened…"). Same action, different behavior depending on where you tap — an IA defect masquerading as richness.

2. Code quality

  • Q1. Dead CSS and JS. .hide{display:none!important} (:36) is defined and never used. The FAB click toggle (:937: closeSheet() branch) is unreachable code (see B1). The dots pager (.dots) purports to page a carousel that doesn't exist.
  • Q2. ~40% of interactive-style elements are inert. Concrete list: bell ×4, filter button, hero-arrow, both bare "View All" links (policies/buy), mini-handle, avatar, portrait, both QA rows, the agents duo-card, the search input (accepts typing, filters nothing), all four .sec-links on the subpages. Users will tap things that look tappable and get nothing. Either wire them or delete them — currently it's a mock wearing a product's clothes.
  • Q3. Magic values everywhere. px-only sizing (no rem/em — ignores user font preferences), un-tokenized spacing/radii, z-index 60/70/80/90/95 held together by hope, gesture thresholds (26, 12, 36, 18, 0.2, 0.28, 140, 8) inline with no names.
  • Q4. transition: all anti-pattern on chips and action tiles (.chip, .act) — animates every property including ones you didn't mean to.
  • Q5. Cryptic class names: .tb, .dc, .qa, .idh, .bc, .stk, .ag-row, .sup-row — maintenance tax on every future edit.
  • Q6. No tests. Nothing. Not a gesture unit test, not a snapshot. The gesture thresholds are tunable constants whose regression would be invisible until a user complains.
  • Q7. Localization-hostile. Hardcoded English, 10/02/25 ambiguous date, $ hardcoded, white-space:nowrap toasts that will overflow in German, physical (non-logical) CSS properties block RTL.

3. Bugs (ranked by severity)

  • B1 (High) — FAB can never close the sheet; half its code is dead. fabBtn lives inside .tabbar (:777-797). Opening the sheet adds .ghost to the tabbar → pointer-events:none (:283). So sheetMode==='closed' ? openSheet() : closeSheet() (:937) can only ever take the open branch; the close branch is unreachable. The FAB pretends to be a toggle and isn't one.
  • B2 (High) — Flick detection windows are wrong. Velocity uses only the last two pointermove samples (vy = (cy-ly)/max(1, now-lt), :959). A fast drag that decelerates at release registers vy≈0 and snaps back; a slow drag with an accidental final twitch counts as a fling. Industry standard is a trailing ~100 ms sample window. Users will experience random sheet snaps.
  • B3 (Medium) — Sub-tab state is silently reset on every expansion. setMode('expanded') forces switchStab('actions') (:932). User on "Agents" collapses to peek (tab hidden but state kept), re-expands → yanked back to "Actions". Collapse keeps state, expand discards it — inconsistent.
  • B4 (Medium) — Chips filter nothing. The benefits chips only toggle .on (:905-911). Featured/Health/Motor switch while the entire screen (drive card, health tip) stays identical. It is a fake control.
  • B5 (Medium) — user-scalable=no, maximum-scale=1 (:5). WCAG 1.4.4 / 1.4.10 violation; low-vision users cannot zoom. This is disqualifying for an insurance app that will be used by older demographics.
  • B6 (Medium) — Fake QR code. The ID card "QR" (<div class="qr">) is a static rect pattern encoding nothing. A user scanning their own insurance ID and getting "no data" is a trust incident. At minimum an insurance ID card must encode policy deep-link or signed token; as-is it's a real-looking credential that does nothing — worse than no QR.
  • B7 (Medium) — Toast is not announced. No role="status"/aria-live on #toast; screen-reader users never hear "Policy renewal started ✓". Also hardcoded white-space:nowrap will overflow the pill on 320 px devices or longer localized strings.
  • B8 (Low) — Sheet keyboard/escape handling absent. No Esc-to-close, no focus trap, focus stays behind the backdrop; keyboard users tab into a blurred background. .backdrop is a <div> with a click listener — not keyboard-activatable.
  • B9 (Low) — Drag threshold tuned for mouse, not thumb. 2636 px on a ~0.35 mm/px phone screen is ~9 mm — double the comfort threshold for flick gestures; combined with B2 users get missed or accidental expansions.
  • B10 (Low) — Hero renew pill says "Renew Now" and the toast confirms a renewal with no confirmation step, no amount, no terms. A one-tap financial commitment with zero friction text. Fine for a mock; a lawsuit in production.
  • B11 (Low) — Screens preserve scroll position across navigation. go() never resets scrollTop, so returning to a long screen restores a scrolled view with no status indication. Minor but sloppy.
  • B12 (Low) — Time is hardcoded "9:41" and the bell red dot has no unread-state backing. Cosmetic, but it signals the app's data plumbing is imaginary.

4. Performance

  • P1 — backdrop-filter stacking. Five separate backdrop-filter regions (hero-ic blur 4, batt-tag blur 6, dc-glass blur 6, tabbar blur 14, sheet backdrop blur 4) sit over scrollable content. On mid/low Android this is the single largest GPU cost; scroll jank is guaranteed when the sheet is open over blurred content.
  • P2 — Animating filter: blur(3px) on .tabbar.ghost (:283) — filter animation is paint-every-frame, not composited; on a full-width bar this is a visible stutter. The same effect is free as pre-rendered opacity+translate.
  • P3 — Massive shadows. 0 34px 90px, 0 22px 46px, multi-layer card shadows repaint on any transform of their elements. Real risk during the sheet's transform animation on low-end devices (verified pattern on mobile WebKit).
  • P4 — Redundant gradients. Hero, drive card, ID card, FAB each stack 23 radial + linear gradients as backgrounds. Beautiful; also 4 large background-size rasterizations. Cacheable but adds first-paint cost on weak GPUs.
  • P5 — No will-change/containment hints on .sheet or .screens; the .screen switch depends on display toggling which forces full layout+paint of 1000-node pages on every tab switch. For 4 pages it's tolerable; at 8 it won't be.
  • P6 — Positives: zero external requests, no fonts, no images, ~10 KB gzipped. First paint is fast. This is why it feels snappy in a demo.
  • P7 — Pointermove handler does performance.now() + style writes per event — fine — but no requestAnimationFrame throttle; at 120 Hz screens style writes run 2× frame rate. Trivial fix.

5. Security & privacy

  • S1 — Realistic PII in public markup. Full names (Rahul Sharma, Alex Mandes, Sarah Kimani, David Otieno, Grace Wanjiru), DOB (24 Feb 2001), ID number 326547624, policy CA326547624, plate MP04CY9999, residence. If any of these map to real humans, this file is a data-protection incident (Kenya DPA 2019 / GDPR-class). Even if synthetic, nothing in the file says so.
  • S2 — No auth/session concept. An app that displays ID cards, policy numbers, and takes "renewal" actions has zero authentication surface. As a mock: understandable. As a product blueprint: where is it?
  • S3 — No CSP/SRI/referrer-meta. Currently self-hosted so risk is latent, but the day someone hot-links a script or style, there's no defense in depth. textContent for toasts is XSS-safe today (:897) — the classic regression is someone "improving" it to innerHTML for bold text.
  • S4 — Sensitive actions lack confirmation & audit trail. Renewal, claim registration, ID download: one tap, toast, done. No step-up auth, no idempotency, no server request at all (obviously) — meaning the file currently teaches the wrong interaction contract to anyone building the backend from it.
  • S5 — Secrets/privacy in URLs to be designed later — but note the QR (B6) will likely tempt someone to encode raw policy IDs client-side. Needs a signed, short-lived token design from the start.
  • S6 — Input handling is unstructured. The search input has no validation/sanitization conventions; when wired to an API this becomes the injection vector of choice. Establish encoding-at-boundary now.

6. Design / UX (and a11y)

  • D1 — Visual craft is real. Consistent radius scale (13→30), one strong brand color, disciplined soft-shadow language, coherent purple/navy/light hierarchy. It looks expensive. (This is what's fooling stakeholders into thinking it's nearly-done.)
  • D2 — Identity confusion. Hero says Car Insurance; sections push Health & Wellness, Quick Actions reward "+$234.00" for claims, Benefits pushes driving score + heart rate. The app doesn't know what it is: car insurance, health insurance, or a wellness-rewards program. Pick one north star per surface.
  • D3 — FAB mega-menu buries support two gestures deep (FAB → expand → Support stab) while "Message Now" exists on Policies — two competing support entries with different states.
  • D4 — Emoji-as-iconography. 🧔🏻👩🏼📄🔔💬🛡 render inconsistently across platforms (tofu on some Androids, inconsistent skin tones). For a financial app this reads as unprofessional; replace with a single icon font/SVG set.
  • D5 — Contrast failures. .lbl rgba(255,255,255,.62) on purple (~2.9:1), #a6a7b3 placeholder, #9a9ba8 10.5 px tab labels, #cfcfd9 dots — multiple sub-4.5:1 ratios at 1012 px sizes. Insurance users skew older; this is an exclusion shipped as a style.
  • D6 — Home indicator / island are fake device chrome painted by the app (.island, .home-ind). Inside a real device in-app WebView these double up with the OS chrome. Kill them outside the demo frame.
  • D7 — No loading/empty/error states anywhere. The file only knows the happy path. First product question that will be asked: "what does this screen show while policies load?" — there's no answer in this code.
  • D8 — Motion is un-governed: no prefers-reduced-motion query; the fadeUp + sheet + blur + toast stack all animate regardless of vestibular settings.

7. Execution plan — "finally improve InsurePay"

Rough effort for 1 senior FE + 0.5 QA, assuming it must become a real maintainable web app. If any phase's goal shifts (e.g., the product stays a prototype), that's a scope decision — write it down explicitly instead of letting it happen by accident.

Phase 0 — Foundations & hygiene (≈34 days) — do not skip

# Task Acceptance criteria
0.1 Split the monolith: index.html + styles.css (+ tokens.css) + app.js as ES module; add package.json (Vite or no-build ESM), lint (ESLint+stylelint), formatting (Prettier), git baseline npm run dev/build/lint green; zero globals (verify via window enumeration in CI)
0.2 Extract design tokens → CSS custom properties for all spacing/radii/font sizes; px→rem at 16 px base; z-index scale z.base/z.overlay/z.sheet/z.toast No magic numbers outside tokens; rem audit passes
0.3 SVG sprite: one <symbol> per icon, <use> everywhere (SVG currently duplicated 14×); delete emoji icons Single bell definition; zero emoji in chrome
0.4 Kill dead code: .hide, dots pager, fake .island/.home-ind, inert controls → wire or delete each; decision table for all 22 data-toast and 14 data-go sites Interactive-inventory doc: every tappable element has a named behavior
0.5 Fix viewport meta (drop user-scalable=no/maximum-scale); add prefers-reduced-motion gate for fadeUp/sheet/toast animations axe-core: 0 critical
0.6 Toast: role="status" aria-live="polite", max-width wrap, queue (2s) instead of replace SR announces; no clipping at 320 px

Phase 1 — Behavior correctness (≈45 days)

# Task Acceptance criteria
1.1 State store: one module owning {screen, sheetMode, sheetTab, chip, toast}; DOM becomes pure render. Keep the exact gesture constants as named, unit-tested exports — the Makepad port's model.rs is the reference implementation; port it back to JS/TS 1:1 Store drives all class flips; go('typo') throws
1.2 Rewrite the gesture engine: trailing 100 ms velocity window, rAF-throttled moves, guarded setPointerCapture, touch-comfort thresholds (~48 px / velocity-weighted), tap tolerance on both axes 95% of scripted gesture traces resolve as intended (test corpus)
1.3 Fix B1: move FAB out of the ghosted tabbar (or un-ghost it); restore real toggle; add Esc-to-close, backdrop focus trap, focus return Keyboard-only walkthrough passes
1.4 Fix B3: preserve sub-tab across collapse/expand, or make the reset an explicit product decision (note in spec) Behavior documented + tested
1.5 Fix B4: chips actually filter a benefits dataset (extract benefits rows from markup into data/benefits.json); search bar filters catalog Typing "health" narrows visible rows
1.6 Routing: #/policies-style hash router with browser back support + scroll restore reset (B11) Refresh on #/buy renders Buy; back works
1.7 One canonical Claims journey (merge the 3 divergent entries, IA decision per D2/D3) One "File a Claim" path with one outcome

Phase 2 — Tests & quality gates (≈34 days, overlaps Phase 1)

# Task Acceptance criteria
2.1 Unit tests (Vitest) for: router, store transitions, gesture math corpus (reuse the 28 test cases from makepad-ports/insurance/src/model.rs — they encode the same truth), chip filtering, toast queue ≥80% branch coverage on logic modules
2.2 E2E (Playwright): boot smoke, tab circuit, FAB open/expand/close (mouse drag + touch emulation), chip select, toast TTL All green in CI, trace/video on failure
2.3 a11y gate: axe-core in CI on all 4 screens; contrast sweep (fixes D5): raise .lbl/placeholder/tab-label to ≥4.5:1 or document exception axe 0 critical/serious
2.4 Visual regression: screenshot 4 screens + sheet states (peek/expanded × 3 tabs) against golden PNGs PR-blocker on diffs
2.5 Performance budget in CI: Lighthouse mobile ≥90; forbid new backdrop-filter & filter: animations without waiver (replaces P1P3 with stacked alpha/opacity pre-composites) Budgets enforced by CI

Phase 3 — Product hardening (≈26 weeks, domain-dependent)

# Task Notes
3.1 AuthN/Z gate (OIDC), step-up for renewals/claims/ID download (B10, S2, S4) Idempotent POSTs + confirmation sheets with amounts
3.2 Real QR: signed short-lived token → verification endpoint (B6, S5) Never raw policy IDs in the QR
3.3 PII audit & synthetic-data policy (S1): replace all names/IDs with clearly-marked fixtures (EXAMPLE-prefixed); provenance review for agent names Sign-off from whoever owns data compliance
3.4 i18n extraction (react-intl/paraglide), logical CSS properties for RTL, locale-safe date/currency en + sw (Swahili) as first locales given user base
3.5 Security headers: CSP (script-src 'self'), referrer-policy, permissions-policy; SRI for any future external asset Observed via scanner
3.6 API layer: typed contracts for policies/claims/agents/support; loading/empty/error states for every screen (D7) MSW-mocked contract tests
3.7 Decide the native story: the Makepad 2.0 port already exists and is fully tested (makepad-ports/insurance/ — 28 unit + 14 integration + 8 UI tests green). Either (a) keep web-only and treat model.rs as the executable spec to port back, or (b) adopt the port as the native shell and drive both from one JSON content contract The port's model.rs is currently the most correct description of this app's behavior that exists anywhere — use it

Phase 4 — Observability (post-launch guardrails, ≈2 days)

Analytics events on nav/toast/CTA (privacy-reviewed), RUM for gesture jank and tab-switch paint time, error reporting wired to CI releases, feature flags for the staged rollout.

Definition of done (product-level)

  1. Every interactive element either works or is gone (D2 inventory signs off).
  2. npm run build && npm test && axe && lhci green on every PR.
  3. No PII-looking strings in the repo outside tests/fixtures/.
  4. Gesture behavior documented + identical in web tests and the Makepad port's model tests.
  5. A real flow (renew or claim) completes against a mock API with loading, error, and confirmation states.

Bottom line

InsurePay's HTML is a beautifully-executed demo of ~20% of an app. It has no logic worth salvaging as code — its value is (a) the visual system and (b) the interaction vocabulary (sheet physics, tab model, toast idiom). Both are already captured in a testable form in the Makepad port. Treat the HTML as the spec, not the product: rebuild on Phase 01 foundations, gate with Phase 2, and only then let it call itself an insurance app.