Skip to main content

max / makenotwork

server: a refund core, and the Sales panel described Closes b25dd957. The money path now exists once: payments::refund::refund holds every check and the atomic completed -> refunding claim, and both the API route and the described panel call it. refund_transaction is a thin axum wrapper; the route stays registered for API consumers. The Sales panel is a fill on a writes-only nest, because its read address is parameterized and a nest mounts at a fixed prefix. Its Refund button was doing a row swap and then a data-after whole-panel refresh, so the row came back stale (the refund.created webhook is what marks it) and the refresh replaced the panel underneath. A described answer replaces the panel once, and both the flicker and the stale row stop being a thing. The hand-written hx-confirm seal was two behind before this: the tier-1 batch deleted project_members.html and user_projects.html, each carrying one, and lowered nothing. That batch ran lib tests only and this seal is an integration-directory test, so it went unread until the suite ran on astra. 46 -> 43, with the Refund confirm included.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-27 00:07 UTC
Signed with PGP, not checked
Commit: 36e4926cc749881d3e889e9cb9c911cefdc939bd
Parent: 8ed9ad7
13 files changed, +573 insertions, -190 deletions
@@ -10980,13 +10980,6 @@
10980 10980 margin-top: var(--gap-section);
10981 10981 }
10982 10982
10983 - .item-sales-tab-header {
10984 - display: flex;
10985 - justify-content: space-between;
10986 - align-items: center;
10987 - margin-bottom: var(--gap-section);
10988 - }
10989 - .item-sales-tab-header h2 { margin: 0; }
10990 10983 .item-overview-embed {
10991 10984 margin-top: var(--gap-pane);
10992 10985 }
@@ -66,7 +66,20 @@
66 66 /// progress: the seal counts template source, so deduplicating two templates
67 67 /// lowers it without describing anything. The surviving site is still blocked
68 68 /// on the `closest .link-row` target above.
69 - const HIGH_WATER: usize = 46;
69 + /// 44 on 2026-08-26, and this one was late: the six-panel tier-1 batch
70 + /// (`16d8cac4`..`14254786`) deleted `tabs/project_members.html` and
71 + /// `tabs/user_projects.html`, each carrying one, and nobody lowered the number.
72 + /// The batch ran lib tests only, and this seal is an integration-directory test,
73 + /// so it went unread until the suite ran on astra. Same shape as the globals
74 + /// seal in `8ed9ad79`, and the same lesson: a ratchet nobody runs is not a
75 + /// ratchet. Both are now `Act::confirm` -- "Remove {name} from this project?" in
76 + /// `crate::quasi::project_members` and the delete confirm in
77 + /// `crate::quasi::user_projects`.
78 + ///
79 + /// 43 on 2026-08-26, `b25dd957`: `tabs/item_sales.html` is described as
80 + /// `crate::quasi::item_sales`, and its Refund button's "Issue a full refund for
81 + /// {amount}? This cannot be undone." is `Act::confirm` with `Tone::Danger`.
82 + const HIGH_WATER: usize = 43;
70 83
71 84 /// Every template, recursively. Walked rather than listed for the reason the
72 85 /// frontend-globals seal walks `frontend/src`: a fence that has to be told about
@@ -20,6 +20,7 @@
20 20 pub mod fan_ops;
21 21 /// The MNW event vocabulary the webhook dispatcher reasons in.
22 22 pub mod mnw_event;
23 + pub mod refund;
23 24 pub mod synckit_app_pricing;
24 25 pub mod synckit_billing;
25 26 mod webhooks;
@@ -43,6 +43,7 @@
43 43 pub mod embeds;
44 44 pub mod export_act;
45 45 pub mod forum_memberships;
46 + pub mod item_sales;
46 47 pub mod item_tabs;
47 48 pub mod library_contacts;
48 49 pub mod library_tabs;
@@ -324,6 +325,13 @@
324 325 project_members::NEST,
325 326 writes_only(app, project_members::WRITES, project_members::renderer),
326 327 ),
328 + // The item dashboard's Sales panel: a parameterized read address, so
329 + // the read stays on Askama and only the Refund write is described.
330 + // `b25dd957`; see `writes_only`.
331 + (
332 + item_sales::NEST,
333 + writes_only(app, item_sales::WRITES, item_sales::renderer),
334 + ),
327 335 (
328 336 forum_memberships::SETTINGS_PATH,
329 337 mount(
@@ -271,7 +271,6 @@
271 271 ItemDetailsTabTemplate,
272 272 ItemPricingTabTemplate,
273 273 ItemFilesTabTemplate,
274 - ItemSalesTabTemplate,
275 274 ItemEmbedTabTemplate,
276 275 // Onboarding checklist
277 276 OnboardingChecklistPartialTemplate,
@@ -9,8 +9,8 @@
9 9 AdminAppealRow, AdminHeldUploadRow, AdminReportRow, AdminUserRow, AdminWaitlistRow,
10 10 BlogPostDashboardRow, ChartBar, Collection, ContentItem, DiscoverItem, DiscoverProject,
11 11 InviteCodeDisplay, Item, ItemSection, LicenseKeyRow, Project, ProjectCard, ProjectMemberRow,
12 - PromoCodeRow, SaleRow, SidebarView, StatCard, SubscriptionTier, SyncAppRow, TipReceived,
13 - Transaction, User, UserSubscription, Version, WaitlistEntry, WaveStats,
12 + PromoCodeRow, SidebarView, StatCard, SubscriptionTier, SyncAppRow, TipReceived, Transaction,
13 + User, UserSubscription, Version, WaitlistEntry, WaveStats,
14 14 };
15 15
16 16 use super::CsrfTokenOption;
@@ -914,18 +914,6 @@
914 914 pub versions: Vec<Version>,
915 915 }
916 916
917 - /// Item sales tab: transaction history with refund actions.
918 - #[derive(Template)]
919 - #[template(path = "partials/tabs/item_sales.html")]
920 - pub struct ItemSalesTabTemplate {
921 - pub item: Item,
922 - pub sales: Vec<SaleRow>,
923 - /// Where the described Export control posts. Built by the handler rather
924 - /// than composed in the template: `export_act::html` takes a `&str` and
925 - /// Askama cannot `format!` one in an expression.
926 - pub export_route: String,
927 - }
928 -
929 917 /// Item embed tab: copy-paste embed codes for this item.
930 918 #[derive(Template)]
931 919 #[template(path = "partials/tabs/item_embed.html")]