Rebuild around the real mechanism: bypass Faraid/probate entirely
Product direction clarified: Faraid/probate court adjudication is what takes ~2 years. Assets already moved out of the estate before death (via completed Hibah, dedicated Waqf, or a legally-direct nomination channel) are never in that queue — there's nothing for a court to adjudicate. The app's job is to maximize what's covered by those instruments and make the death-triggered payout fast for whatever is covered. New: - nonprobate.js: coverage model — classifies each Asset Registry entry by fast-path channel (hibah/waqf/nomination) vs exposed (faraid/probate). - CoverageDashboard.svelte: the single number that matters — % of estate that bypasses Faraid entirely, with per-asset next-step suggestions. - NominationRegistry.svelte: third fast-path channel for assets that can't be fully gifted/waqf'd — EPF (EPF Act 1991 s.51), Takaful/insurance (Insurance Act 1996 s.166), bank death-mandate, trust/nominee holding. - DeathTrigger.svelte: the execution layer. Multi-attestor threshold + death certificate reference fires the trigger; generates a ready-to-file execution packet per covered asset, pre-filled from Hibah/Waqf/Nomination records. Explicitly scoped: this app cannot itself move money or transfer title, but removes missing-paperwork/ambiguity as a source of delay so institutions can act in days instead of stacking behind a probate queue. Changed: - HibahTracker/FamilyWaqfDesignator: now link to a specific Asset Registry entry so coverage can be computed; fast-path explanation added to both. - WassiyahGenerator: explicit warning that a wassiyah does NOT bypass probate — it's a post-death instrument, only appropriate for the discretionary one-third, not a fast-track vehicle. Users were previously not told this distinction. - FaraidCalculator: reframed as informational-only, showing what applies to whatever remains uncovered — not itself a mechanism. e2e-fastpath.cjs: new Playwright suite covering the full mechanism end-to-end (coverage 0%->100%, nomination, multi-attestor trigger threshold, execution packet generation) — 16/16 passing. Original e2e-uat.cjs suite re-verified at 32/32 with no regressions.
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
<script>
|
||||
// The whole point of this app, made visible as one number: what fraction of the
|
||||
// estate is already arranged to bypass Faraid/probate entirely, versus what will
|
||||
// still fall into the slow court queue because no fast-path instrument covers it.
|
||||
import { computeCoverage, suggestedChannel } from './nonprobate.js';
|
||||
import Disclaimer from './Disclaimer.svelte';
|
||||
|
||||
const coverage = computeCoverage();
|
||||
|
||||
const CHANNEL_LABELS = {
|
||||
hibah: 'Hibah — lifetime gift, completed',
|
||||
waqf: 'Waqf — dedicated corpus',
|
||||
epf: 'EPF nomination',
|
||||
takaful: 'Takaful / insurance nomination',
|
||||
'bank-mandate': 'Bank death mandate',
|
||||
trust: 'Trust / nominee holding',
|
||||
'hibah-flagged-capped': 'Hibah flagged (marad al-mawt) — capped, partially exposed',
|
||||
none: 'Not covered — exposed to Faraid/probate'
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="module">
|
||||
<h2>Coverage Dashboard</h2>
|
||||
<p class="sub">What fraction of your estate is already arranged to skip Faraid/probate entirely.</p>
|
||||
|
||||
<div class="big-number-card" class:full={coverage.fastPercent === 100} class:partial={coverage.fastPercent > 0 && coverage.fastPercent < 100} class:none={coverage.fastPercent === 0}>
|
||||
<div class="big-percent">{coverage.fastPercent}%</div>
|
||||
<div class="big-label">of your estate bypasses the Faraid/probate queue</div>
|
||||
</div>
|
||||
|
||||
<div class="split-row">
|
||||
<div class="split-card fast">
|
||||
<span class="split-label">Fast path</span>
|
||||
<strong>{coverage.fastTotal.toLocaleString()}</strong>
|
||||
</div>
|
||||
<div class="split-card slow">
|
||||
<span class="split-label">Exposed to Faraid/probate</span>
|
||||
<strong>{coverage.exposedTotal.toLocaleString()}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if coverage.fastPercent < 100 && coverage.total > 0}
|
||||
<div class="warning-box">
|
||||
<strong>{coverage.exposedTotal.toLocaleString()} is still exposed.</strong> Anything not covered by Hibah, Waqf, or a Nomination channel goes into the conventional Faraid/probate process at death — the ~2-year route this app exists to avoid. Cover the remaining assets below.
|
||||
</div>
|
||||
{:else if coverage.total > 0}
|
||||
<div class="success-box">
|
||||
<strong>Full coverage.</strong> Every logged asset has a fast-path exit. Nothing here is queued for Faraid/probate adjudication.
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="asset-list">
|
||||
{#each coverage.rows as r (r.asset.id)}
|
||||
{@const suggestion = !r.fast ? suggestedChannel(r.asset.type) : null}
|
||||
<div class="asset-row" class:fast={r.fast} class:slow={!r.fast}>
|
||||
<div class="asset-info">
|
||||
<strong>{r.asset.description}</strong>
|
||||
<span class="muted">{r.ownedValue.toLocaleString()} · {CHANNEL_LABELS[r.channel]}</span>
|
||||
{#if suggestion}
|
||||
<span class="suggestion-inline">Next step: {suggestion.label} — go to {suggestion.channel === 'trust' ? 'a legal advisor for trust setup' : 'Nomination Registry'} or {r.asset.type?.toLowerCase().includes('property') ? 'Hibah/Waqf' : 'Hibah'}.</span>
|
||||
{/if}
|
||||
</div>
|
||||
<span class="status-dot" class:on={r.fast}></span>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="empty">No assets logged yet — start in Asset Registry.</p>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<Disclaimer text="Coverage shown here reflects what you've logged in this app, not a legal audit. EPF/Takaful nominations, bank mandates, and trust structures only take effect once actually filed with the institution — this dashboard tracks intent, not confirmed legal status." />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.module { padding: 4px 0 40px; }
|
||||
h2 { font-family: 'DM Serif Display', serif; font-size: 24px; color: #E8E4DC; margin-bottom: 4px; }
|
||||
.sub { font-size: 13px; color: #8A8478; margin-bottom: 18px; }
|
||||
|
||||
.big-number-card { text-align: center; border-radius: 16px; padding: 24px; margin-bottom: 16px; border: 1px solid rgba(255,255,255,0.08); }
|
||||
.big-number-card.full { background: rgba(46,204,113,0.1); border-color: rgba(46,204,113,0.3); }
|
||||
.big-number-card.partial { background: rgba(201,168,76,0.08); border-color: rgba(201,168,76,0.3); }
|
||||
.big-number-card.none { background: rgba(239,68,68,0.08); border-color: rgba(239,68,68,0.3); }
|
||||
.big-percent { font-family: 'DM Serif Display', serif; font-size: 48px; color: #E8E4DC; }
|
||||
.big-number-card.full .big-percent { color: #2ECC71; }
|
||||
.big-number-card.none .big-percent { color: #EF4444; }
|
||||
.big-label { font-size: 12px; color: #B8B2A6; margin-top: 4px; }
|
||||
|
||||
.split-row { display: flex; gap: 10px; margin-bottom: 16px; }
|
||||
.split-card { flex: 1; background: rgba(255,255,255,0.03); border-radius: 10px; padding: 12px; }
|
||||
.split-card.fast { border-left: 3px solid #2ECC71; }
|
||||
.split-card.slow { border-left: 3px solid #EF4444; }
|
||||
.split-label { display: block; font-size: 11px; color: #8A8478; margin-bottom: 4px; }
|
||||
.split-card strong { font-size: 15px; color: #E8E4DC; }
|
||||
|
||||
.warning-box { background: rgba(239,68,68,0.08); border: 1px solid rgba(239,68,68,0.25); border-radius: 10px; padding: 12px; font-size: 12px; color: #E8E4DC; line-height: 1.5; margin-bottom: 16px; }
|
||||
.success-box { background: rgba(46,204,113,0.08); border: 1px solid rgba(46,204,113,0.25); border-radius: 10px; padding: 12px; font-size: 12px; color: #E8E4DC; line-height: 1.5; margin-bottom: 16px; }
|
||||
|
||||
.asset-row { display: flex; justify-content: space-between; align-items: flex-start; padding: 12px; border-radius: 10px; margin-bottom: 8px; }
|
||||
.asset-row.fast { background: rgba(46,204,113,0.05); }
|
||||
.asset-row.slow { background: rgba(239,68,68,0.05); }
|
||||
.asset-info { display: flex; flex-direction: column; gap: 3px; font-size: 13px; color: #E8E4DC; }
|
||||
.muted { color: #8A8478; font-size: 11px; }
|
||||
.suggestion-inline { color: #C9A84C; font-size: 11px; }
|
||||
.status-dot { width: 10px; height: 10px; border-radius: 50%; background: #EF4444; margin-top: 4px; flex-shrink: 0; }
|
||||
.status-dot.on { background: #2ECC71; }
|
||||
.empty { font-size: 13px; color: #8A8478; text-align: center; padding: 20px 0; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user