Skip to main content

max / makenotwork

Declare the payout summary card Two shapes. The heading level, the strip of two figures, the readiness the card shows when the balance did not load, the way out to Stripe and the banner a creator without payouts enabled sees are all what the tests asserted, and none of them moved. The figures become a supplier because mapping a balance into two of them is a closure, which the form refuses on purpose.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01MptwXZ8k65v19rFmdGAyki
Author: Max Johnson <me@maxj.phd> · 2026-09-03 17:40 UTC
Signed with PGP, not checked
Commit: 45aaf628f62a7e413ea4d80c8673f03d7499de95
Parent: 1e12d19
2 files changed, +53 insertions, -49 deletions
@@ -6674,7 +6674,7 @@
6674 6674
6675 6675 [[package]]
6676 6676 name = "quasi-router"
6677 - version = "0.101.2"
6677 + version = "0.101.3"
6678 6678 dependencies = [
6679 6679 "makeover-layout",
6680 6680 ]
@@ -10769,6 +10769,14 @@
10769 10769 "pkg-config",
10770 10770 ]
10771 10771
10772 + [[patch.unused]]
10773 + name = "synckit-client"
10774 + version = "0.10.0"
10775 +
10776 + [[patch.unused]]
10777 + name = "synckit-config"
10778 + version = "0.2.0"
10779 +
10772 10780 [[patch.unused]]
10773 10781 name = "kberg"
10774 10782 version = "0.1.0"
@@ -10796,11 +10804,3 @@
10796 10804 [[patch.unused]]
10797 10805 name = "quasi-tauri"
10798 10806 version = "0.101.1"
10799 -
10800 - [[patch.unused]]
10801 - name = "synckit-client"
10802 - version = "0.10.0"
10803 -
10804 - [[patch.unused]]
10805 - name = "synckit-config"
10806 - version = "0.2.0"
@@ -50,8 +50,9 @@
50 50 //! not this conversion's goal and is a consequence of saying the thing once.
51 51
52 52 use makeover_layout as layout;
53 + use quasi_declare::declare;
53 54 use quasi_router::screen::Figure;
54 - use quasi_router::{Action, Node, RegionKind, Request, Response, RouteError, Slot};
55 + use quasi_router::{Node, Request, Response, RouteError};
55 56 use quasi_webview::Webview;
56 57
57 58 use super::Viewer;
@@ -124,49 +125,52 @@
124 125 ))
125 126 }
126 127
127 - /// Everything inside the card.
128 - fn card(balance: Option<&BalanceView>, payouts_enabled: bool) -> Node {
129 - // `Subsection`, so the heading stays an `h3`: this card sits inside the
130 - // Payments tab's own sections and the template wrote `<h3 class="card-h">`.
131 - let slot = Slot::new(REGION, RegionKind::Pane)
132 - .with(Node::Heading {
133 - level: layout::Heading::Subsection,
134 - text: "Payout Summary".into(),
135 - })
136 - .with(stripe_link());
128 + declare! {
129 + /// Everything inside the card.
130 + ///
131 + /// `subsection`, so the heading stays an `h3`: this card sits inside the
132 + /// Payments tab's own sections and the template wrote `<h3 class="card-h">`.
133 + shape card(balance: Option<&BalanceView>, payouts_enabled: bool) -> Node;
137 134
138 - let Some(balance) = balance else {
139 - return Node::Region(slot.with(Node::failed(
140 - "Unable to load balance. Check your Stripe dashboard for details.",
141 - )));
142 - };
143 -
144 - let slot = slot
145 - .with(Node::text(
146 - "Payouts are processed automatically via Stripe Connect.",
147 - ))
148 - .with(Node::stats([
149 - Figure::new(balance.available.clone(), "Available Balance"),
150 - Figure::new(balance.pending.clone(), "Pending"),
151 - ]));
152 -
153 - Node::Region(if payouts_enabled {
154 - slot
155 - } else {
156 - slot.with(Node::banner(
157 - layout::Tone::Warning,
158 - "Payouts are not yet enabled. Complete your Stripe account setup above.",
159 - ))
160 - })
135 + region REGION as Pane {
136 + subsection "Payout Summary";
137 + include stripe_link();
138 + failed "Unable to load balance. Check your Stripe dashboard for details."
139 + unless balance.is_some();
140 + text "Payouts are processed automatically via Stripe Connect." when balance.is_some();
141 + stats figures(balance) when balance.is_some();
142 + banner layout::Tone::Warning
143 + "Payouts are not yet enabled. Complete your Stripe account setup above."
144 + when balance.is_some() and not payouts_enabled;
145 + }
161 146 }
162 147
163 - /// The way out to Stripe's own dashboard.
148 + /// The two figures, or none when the balance did not load.
164 149 ///
165 - /// [`Action::external`] rather than a route: this address is not one the server
166 - /// answers, and saying so is what lets a renderer that is not a browser decide
167 - /// what "leaving" means for it.
168 - fn stripe_link() -> Node {
169 - Node::act("View in Stripe", Action::external(STRIPE_PAYOUTS))
150 + /// A `-> Vec<Figure>` payload supplier, which is the remedy the deferred table
151 + /// names for a mapped payload. R9 is why it answers for the absent case rather
152 + /// than being skipped: a guard decides whether a member is placed, not whether
153 + /// its holes are evaluated, so this is asked even when the strip is not drawn.
154 + fn figures(balance: Option<&BalanceView>) -> Vec<Figure> {
155 + balance
156 + .map(|balance| {
157 + vec![
158 + Figure::new(balance.available.clone(), "Available Balance"),
159 + Figure::new(balance.pending.clone(), "Pending"),
160 + ]
161 + })
162 + .unwrap_or_default()
163 + }
164 +
165 + declare! {
166 + /// The way out to Stripe's own dashboard.
167 + ///
168 + /// `external` rather than a route: this address is not one the server
169 + /// answers, and saying so is what lets a renderer that is not a browser decide
170 + /// what "leaving" means for it.
171 + shape stripe_link() -> Node;
172 +
173 + act "View in Stripe" to external STRIPE_PAYOUTS;
170 174 }
171 175
172 176 /// The renderer this screen is drawn with.