Skip to content

settings: show the shard's price on trial and against every size button - #52

Merged
max-tet merged 1 commit into
mainfrom
settings-show-prices
Sep 2, 2026
Merged

max-tet merged 1 commit into
mainfrom
settings-show-prices

Conversation

@ClaydeCode

Copy link
Copy Markdown
Contributor

Closes #48 (the in-product half; the marketing side is FreeshardBase/landing-page#20).

What changed

All display changes are in src/views/Settings.vue; the one new helper is in src/lib/pricing.js.

Subscription card, trial branch. Below "Subscribe to keep your shard running." it now renders the same two lines the active branch renders, so the two states read as one product:

Plan: S — €21.78/month
Includes 30 GB disk, incl. 19% VAT (€3.48)

Size card. Each size button carries its monthly gross price on a second line, on trial and on an active subscription alike, with one muted line under the group saying the figures are per month at the shard's current disk and include VAT. The smaller sizes stay disabled — shards only grow — so priced-and-disabled is the intended look for those; nothing was re-enabled.

Where the numbers come from. mounted() fires GET /core/protected/management/api/shards/self/prices in the background, the same proxy style resizeShard already uses. The prices list from the response is rendered directly, so nothing a user sees is computed in this UI.

Self-check. ShardPricesResponse's docstring asks clients to recompute price_cents from components and assert the two agree. grossPriceCentsFromComponents in src/lib/pricing.js does exactly that, for every size in the response at the shard's current volume_size_gb. A mismatch produces a console.warn naming both figures; the server's figure is what stays on screen. It reproduces the controller's two load-bearing details from freeshard_controller/service/pricing.py: each component converted to euro before combining, and half-up Math.round, since toFixed and banker's rounding each diverge by 1 ct (S at 250 GB is 3749, not 3748).

Failure handling. Any failure of the fetch logs to console, leaves pricing at null, and every price block drops out of the template via its v-if. No toast, no blank card, no exception — the page renders as it does on main today.

Deploy dependency

The endpoint merged in FreeshardBase/freeshard-controller#440 but is not deployed yet — https://controller.freeshard.net/api/pricing returns 404 as of this writing. Until the controller ships, this branch is a no-op on a live shard: the fetch 404s, and the settings page looks exactly like main. Nothing here needs a shard_core change; the management catch-all proxy already covers the route.

Verified

Run in a fresh clone of main with this branch on top:

Command Result
npm ci clean install
npx vue-cli-service lint --no-fix DONE No lint errors found!
npx vue-cli-service test:unit 3 suites, 28 tests passed (19 on main, 9 added)
NODE_OPTIONS=--openssl-legacy-provider npx vue-cli-service build DONE Build complete.

The nine new tests cover grossPriceCentsFromComponents against a components block shaped like the controller's. Two of them are the correctness requirements, and both were proven to have teeth by breaking the code on purpose and reverting:

  • Replacing Math.round(grossEur * 100) with a toFixed(2)-based round made s + 250GB rounds half-up to 3749 cents fail with Expected: 3749, Received: 3748.
  • Switching the grouping to the cents-first form made m + 2180GB groups in euro and yields 19099 cents fail with Expected: 19099, Received: 19100.

Not verified, and why

  • No end-to-end test against a live shard. The endpoint is not deployed, so there is no shard that answers it. The 404 path is what I could observe, and it is also the degraded path this PR is written for.
  • No component test of the rendering. The repo has no view-test harness — @vue/test-utils is not a dependency and this PR does not add one, since that is a testing-infrastructure decision rather than part of this issue. So the template changes, the v-if guards that hide prices when the fetch fails, and the console.warn on mismatch are covered by reading, not by a test. The arithmetic underneath them is covered by tests.
  • Visual check. Not done — the dev server proxies to a real shard, which cannot serve the endpoint yet.

Judgement calls

  • Self-check runs over every size, not only the shard's current one. The issue text asks for the current size; every size is displayed, so every size is checked. Same loop, strictly more coverage.
  • The helper went into src/lib/pricing.js rather than the component. That is where the existing pricing helpers and their tests live, and it is what makes the arithmetic testable at all without a component harness. It is the reason the commit carries the lib scope alongside settings.
  • The trial block sits after the existing sentence, not in place of it. "Subscribe to keep your shard running." and the delete-date warning both survive untouched.
  • The Size card's VAT note sits below the resize confirmation block, not directly under the buttons: the confirmation block is d-inline-block beside the button group, and a block-level element between them would push it onto its own line.
  • Nothing else was restructured. FreeshardBase/web-terminal#50 (merging the Subscription/Size/Disk cards) is untouched — no card merged, moved or restyled.
  • Wording of the disk line. The trial card says "Includes 30 GB disk" and the Size card says "at your current 30 GB disk", both drawn from the response's volume_size_gb rather than from the Disk Space card's live usage figure, so the number always matches the one the price was computed from.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AjFFVt8evyWbgygj9BYPAR

…e button

A trial user could not find out what subscribing costs without leaving the
product: the trial branch of the Subscription card rendered one sentence and
the PayPal button, and the Size card's buttons were bare letters in every
state, so even an active subscriber first saw a resize price on PayPal's
approval sheet.

Both now read GET /api/shards/self/prices through the management proxy, which
the controller added in FreeshardBase/freeshard-controller#440. Its `prices`
list is rendered directly, so the UI does no pricing arithmetic of its own for
anything a user sees.

`grossPriceCentsFromComponents` exists only for the self-check the response
model asks clients to run: it recomputes each size from the published
`components` at the shard's current disk and warns to the console when the
result disagrees with the server, which stays the displayed figure. It
reproduces the controller's grouping (each component to euro before combining)
and its half-up rounding, both of which diverge by 1 ct from the obvious
alternatives.

The endpoint is not deployed yet, so the fetch 404s today. That is the same
path as any other failure: log to console, leave `pricing` null, and every
price block disappears from the template, leaving the page as it renders on
main.

Closes #48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trial shards show no price anywhere: a user deciding whether to subscribe cannot find out what it costs

2 participants