Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-ui-web/src/pages/test_buy.rs

//! **THE TEST BUTTON** (owner 2026-10-11) — `/test-buy`: each product's declared
//! test actions as buttons, and the runs they made with their setup lines.
//! Every word is a cell of [`crate::test_buy`], tested on the host; this only
//! lays them out. Thin: it calls the console's server and renders.

use leptos::prelude::*;

use crate::api::Fail;
use crate::test_buy::{
    button_words, buys, by_product, line_cells, pay_link, press, refusal_words, run_cells,
    start_body, state_tone, ActionJson, RunJson, TestPurchaseJson, LINE_COLUMNS, LIST_API,
    RUN_COLUMNS, START_API,
};
use crate::ui::{resolved, Badge, Empty, PageHeader, Panel};

/// A refusal in plain words: no status, no wire code.
#[component]
fn Refused(fail: Fail) -> impl IntoView {
    let (head, detail) = refusal_words(&fail);
    view! {
        <div class="failure" role="alert">
            <h3>{head}</h3>
            {(!detail.is_empty()).then(|| view! { <p class="detail">{detail}</p> })}
        </div>
    }
}

#[component]
pub fn TestBuy() -> impl IntoView {
    crate::api::set_estate(None);
    let feed = crate::feed::use_feed();
    let reload = RwSignal::new(0u32);
    let list = LocalResource::new(move || {
        feed.reread.get();
        reload.get();
        async { crate::api::get::<TestPurchaseJson>(LIST_API).await }
    });
    let busy = RwSignal::new(false);
    let failed = RwSignal::new(None::<Fail>);
    let shown = RwSignal::new(None::<String>);
    let is_admin = move || {
        crate::app::session()
            .get()
            .map(|s| s.is_operator())
            .unwrap_or(false)
    };
    let start = move |a: ActionJson| {
        if busy.get_untracked() {
            return;
        }
        busy.set(true);
        failed.set(None);
        leptos::task::spawn_local(async move {
            match crate::api::post::<_, RunJson>(START_API, &start_body(&a)).await {
                Ok(run) => {
                    shown.set(Some(run.id));
                    reload.update(|n| *n += 1);
                }
                Err(f) => failed.set(Some(f)),
            }
            busy.set(false);
        });
    };
    view! {
        <main class="page" id="main">
            <PageHeader title="Test buy" subtitle="Buy as a customer would. A real account (and machines, for a private instance) is made. Each button says what it costs."/>
            {move || failed.get().map(|f| view! { <Refused fail=f/> })}
            {move || match list.get() {
                Some(Err(f)) if crate::fmt::login_redirect_for(f.status, "/test-buy").is_none() => view! { <div class="panel-body"><Refused fail=f/></div> }.into_any(),
                got => resolved(got, None, move |j: TestPurchaseJson| {
                let now = crate::pages::now_ms();
                let admin = is_admin();
                let open = shown.get().or_else(|| j.runs.first().map(|r| r.id.clone()));
                let open_run = j.runs.iter().find(|r| Some(&r.id) == open.as_ref()).cloned();
                view! {
                    <div class="stack">
                        {if j.actions.is_empty() {
                            view! { <Empty title="No test buttons" detail="No test buttons yet."/> }.into_any()
                        } else {
                            by_product(&j.actions).into_iter().map(|(product, actions)| view! {
                                <Panel title=product.clone()>
                                    <div class="actions" role="group" aria-label=format!("{product} test buttons")>
                                        {actions.into_iter().map(|a| {
                                            let (ok, why) = press(&a, admin);
                                            let pressed = a.clone();
                                            let class = if a.live { "btn danger" } else { "btn" };
                                            view! {
                                                <div class="test-action">
                                                    <button class=class title=why.clone() disabled=move || busy.get() || !ok on:click=move |_| start(pressed.clone())>{button_words(&a)}</button>
                                                    <span class="hint">{if a.words.is_empty() { buys(&a) } else { a.words.clone() }}</span>
                                                    {(!why.is_empty()).then(|| view! { <span class="hint">{format!(" — {why}")}</span> })}
                                                </div>
                                            }
                                        }).collect_view()}
                                    </div>
                                </Panel>
                            }).collect_view().into_any()
                        }}
                        <Panel title="Runs">
                            {if j.runs.is_empty() {
                                view! { <p class="hint">"No test buy yet."</p> }.into_any()
                            } else {
                                view! {
                                    <table class="test-runs" aria-label="Runs">
                                        <thead><tr>{RUN_COLUMNS.iter().map(|c| view! { <th scope="col">{*c}</th> }).collect_view()}</tr></thead>
                                        <tbody>
                                            {j.runs.iter().map(|r| {
                                                let [started, product, action, account, instance, state, words] = run_cells(r, now);
                                                let tone = state_tone(&r.state);
                                                let id = r.id.clone();
                                                view! {
                                                    <tr title=r.id.clone() on:click=move |_| shown.set(Some(id.clone()))>
                                                        <td>{started}</td>
                                                        <td class="mono">{product}</td>
                                                        <td>{action}</td>
                                                        <td class="mono">{account}</td>
                                                        <td class="mono">{instance}</td>
                                                        <td><Badge tone=tone label=state/></td>
                                                        <td>{words}</td>
                                                    </tr>
                                                }
                                            }).collect_view()}
                                        </tbody>
                                    </table>
                                }.into_any()
                            }}
                        </Panel>
                        {open_run.map(|r| view! {
                            <Panel title=format!("{} · {} · {}", r.id, r.label, r.state)>
                                {(!r.mailbox.is_empty()).then(|| view! { <p class="hint">{format!("mail goes to {}", r.mailbox)}</p> })}
                                {pay_link(&r).map(|(words, url)| view! { <p><a class="btn danger" href=url target="_blank" rel="noopener noreferrer">{words}</a></p> })}
                                <table class="test-lines" aria-label="Setup lines">
                                    <thead><tr>{LINE_COLUMNS.iter().map(|c| view! { <th scope="col">{*c}</th> }).collect_view()}</tr></thead>
                                    <tbody>
                                        {r.lines.iter().map(|l| view! { <tr>{line_cells(l, now).into_iter().map(|x| view! { <td>{x}</td> }).collect_view()}</tr> }).collect_view()}
                                    </tbody>
                                </table>
                            </Panel>
                        })}
                    </div>
                }
            }),
            }}
        </main>
    }
}