ui phase 13

This commit is contained in:
hamid
2026-07-20 01:35:15 +03:30
parent d33568bf31
commit 12ce7fa7de
21 changed files with 868 additions and 80 deletions
+22
View File
@@ -38,6 +38,28 @@
<li><strong>MVP:</strong> weekly batches; EVV + dispute-window gating; per-session accrual for engagements; <code>nurse_clawbacks</code> with next-batch netting and write-off; unique booking↔payout link; <code>iranian_holidays</code>-aware scheduling; verified-IBAN payouts with reconciliation references.</li>
<li><strong>DEFERRED:</strong> on-demand / instant nurse withdrawal; per-nurse configurable payout frequency; automated clawback recovery beyond netting.</li>
</ul>
<h2 id="d1-rules-confirmed-in-build-backend-b13">(d1) Rules confirmed in build (backend b13) <a class="anchor" href="#d1-rules-confirmed-in-build-backend-b13" aria-hidden="true">#</a></h2>
<p>These fill gaps the requirements left open; the payout engine was built to them:</p>
<ul>
<li>**Clawback netting recovers <em>whole</em> clawbacks up to a batch's earnings.** A <code>nurse_clawbacks</code> row is atomic —</li>
</ul>
<p> it is recovered in full or not at all — so a batch nets the largest set of whole pending clawbacks (oldest first) that fits within the nurse's earnings that week; <code>net_amount = gross_earnings clawback_applied ≥ 0</code> (never negative). A single clawback <strong>larger than a batch's earnings</strong> stays fully <code>pending</code> and recovers from a later, larger batch (it is not partially recovered). The recovery is a real ledger movement (<code>DEBIT nurse_payable / CREDIT nurse_clawback_receivable</code>), not just a status flag, so the derived balances reconcile.</p>
<ul>
<li><strong>A booking with an active (non-failed/-rejected) refund is held out of payout batches</strong> — its money was (partly)</li>
</ul>
<p> reversed, so paying its frozen <code>nurse_payout_amount</code> would overpay. It is excluded until resolved (this is the operational reading of "no open dispute", since there is no separate dispute table yet).</p>
<ul>
<li><strong>PAYA vs SATNA</strong> is chosen per payout by the <code>payout_satna_threshold_irr</code> config (SATNA for net amounts at/above</li>
</ul>
<p> the threshold, else PAYA).</p>
<ul>
<li><strong>Optional BNPL settlement gate:</strong> <code>require_bnpl_settlement_for_payout</code> (config, <strong>default off</strong>) — when on, a</li>
</ul>
<p> BNPL-paid booking is payout-eligible only once its provider settlement (<code>settled_at</code>) is received.</p>
<ul>
<li>The weekly <strong>cron trigger is DEFERRED</strong> — batches are admin-triggered; the cadence lives in</li>
</ul>
<p> <code>nurse_payout_interval_days</code> (default 7) for the future scheduler.</p>
<h2 id="d-supporting-database-entities">(d) Supporting database entities <a class="anchor" href="#d-supporting-database-entities" aria-hidden="true">#</a></h2>
<p><code>nurse_payout_batches</code>, <code>nurse_payouts</code> (with <code>gross_earnings_irr</code>, <code>clawback_applied_irr</code>, <code>net_amount_irr</code>, <code>iban_snapshot</code>), <code>nurse_payout_booking_links</code> (unique per booking), <strong><code>nurse_clawbacks</code></strong>, <code>ledger_entries</code>, <strong><code>iranian_holidays</code></strong>, <code>bookings.dispute_window_ends_at</code>, <code>nurse_bank_accounts</code>.</p>
<blockquote><p><strong>Related:</strong> Data model — <a href="../data-model/07-payouts.html">Payouts</a>.</p>
+27
View File
@@ -48,6 +48,33 @@
<p> <strong>(Under BNPL, who pays the nurse?)</strong> → Resolved. <strong>Balinyaar</strong> pays the nurse, on its own weekly schedule, from <code>gross balinyaar_commission</code> — identical to a card booking; the BNPL commission is a platform expense, never the nurse's. See <a href="../payments/cancellation-and-payout.html">Q2 — who pays the nurse &amp; when</a>.</p>
<hr>
<p>See also the launch-blocking items to confirm with counsel / providers: <a href="../research/go-to-market.html">research open questions</a> and <a href="../data-model/index.html">data-model open items</a>.</p>
<hr>
<h2 id="decided-public-guest-browse-depth-ui-phase-13-2026-07-19">Decided — public guest-browse depth (ui-phase-13, 2026-07-19) <a class="anchor" href="#decided-public-guest-browse-depth-ui-phase-13-2026-07-19" aria-hidden="true">#</a></h2>
<p><strong>Question:</strong> how deep should the anonymous (logged-out) web experience go, now that the entire public surface is a bare <code>/login</code>?</p>
<p>Three tiers were framed:</p>
<ul>
<li><strong>(a) Landing only</strong> — a marketing page at <code>/</code>; zero data, zero new endpoints.</li>
<li><strong>(b) Landing + static public pages</strong> — (a) plus category/how-it-works/trust content; still zero</li>
</ul>
<p> data (a static i18n category grid, not the live catalog).</p>
<ul>
<li><strong>(c) Guest search + public nurse profiles</strong> — read-only anonymous variants of the search-results</li>
</ul>
<p> and nurse-profile screens. Requires <strong>public read endpoints that don't exist today</strong> (search and profile reads sit behind the auth middleware + cookie-bearing <code>clientFetch</code>) plus a privacy review of which nurse fields may be shown logged-out (never phone, exact address, or document data).</p>
<p><strong>Decision: ship (a)+(b) now; tier (c) is deferred as a REQ-gated follow-up</strong>, not built against placeholder endpoints. Rationale:</p>
<ul>
<li>(a)+(b) already close the biggest gap — a family can see the brand, the trust story (identity/</li>
</ul>
<p> license/INO/bank verification), the escrow guarantee, and the service categories before creating an account, all with <strong>zero new backend surface</strong>.</p>
<ul>
<li>(c) is a materially bigger commitment: a new anonymous, rate-limited search read and a</li>
</ul>
<p> privacy-reviewed public nurse-profile shape are backend work with real abuse/PII exposure considerations (scraping, competitor intel, nurse safety) — the kind of decision that shouldn't be made by inferring it from a frontend phase.</p>
<ul>
<li>What (c) would need, if approved later: an anonymous <code>nurse_search</code> read (verified-only invariant</li>
</ul>
<p> preserved, rate-limited, no customer-context fields) and a privacy-reviewed public nurse-profile read (display name, photo, verified-badge state, rating aggregate, service/price rows only — never phone, exact coverage area, or document data). Filed as REQ-066/REQ-067 in <a href="../../dev/shared-working-context/frontend/requests/for-backend.html">for-backend.md</a>; guest-search routes and the guest→login handoff at the booking CTA stay unbuilt until those land.</p>
<p>Implemented as: a public landing rewritten in at <code>/</code> (middleware rewrite, not a redirect, so the URL/ canonical stays <code>/</code>) with hero, static category grid, how-it-works, a trust/verification explainer, a nurse-recruitment CTA, and a footer — see <a href="../../dev/shared-working-context/reports/ui-phase-13-report.html">ui-phase-13-report.md</a> for the full build record.</p>
<a class="back-to-top" href="#">↑ Back to top</a>
</div></main>
</div>
+38
View File
@@ -45,3 +45,41 @@ research and built into the model.
See also the launch-blocking items to confirm with counsel / providers:
[research open questions](../research/go-to-market.md) and
[data-model open items](../data-model/index.md).
---
## Decided — public guest-browse depth (ui-phase-13, 2026-07-19)
**Question:** how deep should the anonymous (logged-out) web experience go, now that the entire
public surface is a bare `/login`?
Three tiers were framed:
- **(a) Landing only** — a marketing page at `/`; zero data, zero new endpoints.
- **(b) Landing + static public pages** — (a) plus category/how-it-works/trust content; still zero
data (a static i18n category grid, not the live catalog).
- **(c) Guest search + public nurse profiles** — read-only anonymous variants of the search-results
and nurse-profile screens. Requires **public read endpoints that don't exist today** (search and
profile reads sit behind the auth middleware + cookie-bearing `clientFetch`) plus a privacy review
of which nurse fields may be shown logged-out (never phone, exact address, or document data).
**Decision: ship (a)+(b) now; tier (c) is deferred as a REQ-gated follow-up**, not built against
placeholder endpoints. Rationale:
- (a)+(b) already close the biggest gap — a family can see the brand, the trust story (identity/
license/INO/bank verification), the escrow guarantee, and the service categories before creating
an account, all with **zero new backend surface**.
- (c) is a materially bigger commitment: a new anonymous, rate-limited search read and a
privacy-reviewed public nurse-profile shape are backend work with real abuse/PII exposure
considerations (scraping, competitor intel, nurse safety) — the kind of decision that shouldn't be
made by inferring it from a frontend phase.
- What (c) would need, if approved later: an anonymous `nurse_search` read (verified-only invariant
preserved, rate-limited, no customer-context fields) and a privacy-reviewed public nurse-profile
read (display name, photo, verified-badge state, rating aggregate, service/price rows only — never
phone, exact coverage area, or document data). Filed as REQ-066/REQ-067 in
[for-backend.md](../../dev/shared-working-context/frontend/requests/for-backend.md); guest-search
routes and the guest→login handoff at the booking CTA stay unbuilt until those land.
Implemented as: a public landing rewritten in at `/` (middleware rewrite, not a redirect, so the URL/
canonical stays `/`) with hero, static category grid, how-it-works, a trust/verification explainer,
a nurse-recruitment CTA, and a footer — see
[ui-phase-13-report.md](../../dev/shared-working-context/reports/ui-phase-13-report.md) for the
full build record.