From 4d244db6d007676ebd641a4575d37b5592e3e726 Mon Sep 17 00:00:00 2001 From: wmj Date: Thu, 13 Aug 2026 18:14:28 +0800 Subject: [PATCH] Add (i) info button to every tab, explained for the average user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New InfoPanel.svelte: shared (i) button next to each module's h2, toggling a plain-language explainer with three sections — what this tab is, how to use it, what to enter in each field. One component so tone stays consistent across all 10 tabs instead of each screen inventing its own help pattern. Deliberately avoids fiqh/legal jargon in favor of concrete, everyday framing (e.g. Hibah explained as "a gift you give right now, while you're alive" rather than leading with the Arabic term) — written for someone with no prior estate-planning or Islamic finance background, consistent with the "convince a 100-year-old grandmother" usability bar already established for this product. Wired into: Coverage, Faraid, Assets, Wassiyah, Hibah, Family Waqf, Nominate, Trigger, Claims (H2), Settings. e2e-info.cjs: new suite verifying the info button appears, opens a non-trivial explanation, and closes again on every one of the 10 tabs. 31/31 passing. Re-verified all five prior suites (32/32, 16/16, 12/12, 10/10, 10/10) — 111/111 total, no regressions. --- e2e-info.cjs | 49 ++++++++++++++++++++++++ src/App.svelte | 12 +++++- src/lib/AssetRegistry.svelte | 19 ++++++++- src/lib/CoverageDashboard.svelte | 14 ++++++- src/lib/DeathTrigger.svelte | 18 ++++++++- src/lib/FamilyWaqfDesignator.svelte | 19 ++++++++- src/lib/FaraidCalculator.svelte | 19 ++++++++- src/lib/HibahTracker.svelte | 19 ++++++++- src/lib/InfoPanel.svelte | 55 +++++++++++++++++++++++++++ src/lib/NominationRegistry.svelte | 18 ++++++++- src/lib/WassiyahGenerator.svelte | 19 ++++++++- src/lib/horizon2/DigitalClaims.svelte | 18 ++++++++- 12 files changed, 260 insertions(+), 19 deletions(-) create mode 100644 e2e-info.cjs create mode 100644 src/lib/InfoPanel.svelte diff --git a/e2e-info.cjs b/e2e-info.cjs new file mode 100644 index 0000000..774b539 --- /dev/null +++ b/e2e-info.cjs @@ -0,0 +1,49 @@ +// Verifies every tab has a working (i) info button that opens plain-language help. +const { chromium } = require('playwright'); +const BASE = 'https://moslem04.falahos.my/'; +const results = []; +const consoleErrors = []; +function record(name, pass, detail = '') { results.push({ name, pass, detail }); console.log(`${pass ? 'PASS' : 'FAIL'} ${name}${detail ? ' — ' + detail : ''}`); } + +const TABS = ['Coverage', 'Faraid', 'Assets', 'Wassiyah', 'Hibah', 'Family Waqf', 'Nominate', 'Trigger', 'Claims (H2)', 'Settings']; + +async function main() { + const browser = await chromium.launch(); + const page = await browser.newPage({ viewport: { width: 390, height: 844 } }); + page.on('console', m => { if (m.type() === 'error') consoleErrors.push(m.text()); }); + page.on('pageerror', e => consoleErrors.push(e.message)); + + await page.goto(BASE, { waitUntil: 'networkidle' }); + + for (const label of TABS) { + await page.locator('nav button.tab', { hasText: label }).click(); + await page.waitForTimeout(200); + + const infoBtn = page.locator('.module-header .info-btn'); + const btnVisible = await infoBtn.isVisible(); + record(`"${label}": info button visible`, btnVisible); + if (!btnVisible) continue; + + await infoBtn.click(); + await page.waitForTimeout(150); + const panelVisible = await page.locator('.info-panel').isVisible(); + const whatText = await page.locator('.info-panel .info-section p').first().textContent().catch(() => ''); + record(`"${label}": info panel opens with non-trivial explanation`, panelVisible && whatText.length > 40, `${whatText.length} chars`); + + // toggle closed + await infoBtn.click(); + await page.waitForTimeout(150); + const panelClosed = await page.locator('.info-panel').isVisible().catch(() => false); + record(`"${label}": info panel closes on second click`, !panelClosed); + } + + record('No uncaught JS console errors across all info panels', consoleErrors.length === 0, consoleErrors.join(' || ')); + + await browser.close(); + const passCount = results.filter(r => r.pass).length; + const failCount = results.length - passCount; + console.log(`\n${passCount} passed, ${failCount} failed, ${results.length} total`); + if (failCount > 0) results.filter(r => !r.pass).forEach(r => console.log(` - ${r.name}: ${r.detail}`)); + process.exit(failCount > 0 ? 1 : 0); +} +main().catch(e => { console.error('SCRIPT ERROR:', e); process.exit(2); }); diff --git a/src/App.svelte b/src/App.svelte index 156fa8b..2b04df9 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -10,6 +10,7 @@ import DigitalClaims from './lib/horizon2/DigitalClaims.svelte'; import { exportAll, deleteAll } from './lib/storage.js'; import { lang, setLang } from './lib/i18n.js'; + import InfoPanel from './lib/InfoPanel.svelte'; let currentLang = $state('en'); lang.subscribe(v => currentLang = v); @@ -73,7 +74,15 @@ {:else if activeTab === 8} {:else if activeTab === 9}
-

Settings

+
+

Settings

+ +

Your data is stored locally on this device. Nothing is sent to a third party.

@@ -132,6 +141,7 @@ :global(.btn-danger) { width: 100%; padding: 12px; border-radius: 8px; border: 1px solid rgba(239,68,68,0.4); background: rgba(239,68,68,0.1); color: #EF4444; font-weight: 600; cursor: pointer; margin-top: 12px; } :global(.btn-secondary) { width: 100%; padding: 12px; border-radius: 8px; border: none; background: rgba(255,255,255,0.08); color: #E8E4DC; font-weight: 600; cursor: pointer; } + .module-header { display: flex; align-items: center; margin-bottom: 4px; } .lang-switch { margin-bottom: 16px; } .lang-label { display: block; font-size: 12px; color: #B8B2A6; margin-bottom: 8px; } .lang-buttons { display: flex; gap: 8px; } diff --git a/src/lib/AssetRegistry.svelte b/src/lib/AssetRegistry.svelte index bf96cc8..1975912 100644 --- a/src/lib/AssetRegistry.svelte +++ b/src/lib/AssetRegistry.svelte @@ -1,6 +1,7 @@
-

Asset Registry

+
+

Asset Registry

+ +

Log what you own — feeds the Faraid Calculator, Wassiyah one-third meter, and Waqf corpus selector.

@@ -136,7 +150,8 @@ diff --git a/src/lib/NominationRegistry.svelte b/src/lib/NominationRegistry.svelte index eb2ad80..74c194f 100644 --- a/src/lib/NominationRegistry.svelte +++ b/src/lib/NominationRegistry.svelte @@ -12,6 +12,7 @@ import { load, save } from './storage.js'; import { suggestedChannel } from './nonprobate.js'; import Disclaimer from './Disclaimer.svelte'; + import InfoPanel from './InfoPanel.svelte'; const assets = load('assets', []); @@ -165,7 +166,19 @@
-

Nomination Registry

+
+

Nomination Registry

+ +
Third fast-path channel. EPF and Takaful/insurance nominations pay the nominee directly by law — no Faraid/probate involvement at all. Bank mandates and trust/nominee holdings need setup now, while alive, but also bypass the court queue once in place.

Log which non-probate channel covers each asset that can't be fully gifted (Hibah) or dedicated (Waqf).

@@ -276,7 +289,8 @@