Repository navigation
settings: show the shard's price on trial and against every size button - #52
Merged
Merged
Conversation
…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
This was referenced Sep 5, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 insrc/lib/pricing.js.Subscription card,
trialbranch. Below "Subscribe to keep your shard running." it now renders the same two lines theactivebranch renders, so the two states read as one product: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()firesGET /core/protected/management/api/shards/self/pricesin the background, the same proxy styleresizeShardalready uses. Thepriceslist from the response is rendered directly, so nothing a user sees is computed in this UI.Self-check.
ShardPricesResponse's docstring asks clients to recomputeprice_centsfromcomponentsand assert the two agree.grossPriceCentsFromComponentsinsrc/lib/pricing.jsdoes exactly that, for every size in the response at the shard's currentvolume_size_gb. A mismatch produces aconsole.warnnaming both figures; the server's figure is what stays on screen. It reproduces the controller's two load-bearing details fromfreeshard_controller/service/pricing.py: each component converted to euro before combining, and half-upMath.round, sincetoFixedand 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
pricingatnull, and every price block drops out of the template via itsv-if. No toast, no blank card, no exception — the page renders as it does onmaintoday.Deploy dependency
The endpoint merged in
FreeshardBase/freeshard-controller#440but is not deployed yet —https://controller.freeshard.net/api/pricingreturns 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 likemain. Nothing here needs ashard_corechange; the management catch-all proxy already covers the route.Verified
Run in a fresh clone of
mainwith this branch on top:npm cinpx vue-cli-service lint --no-fixDONE No lint errors found!npx vue-cli-service test:unitmain, 9 added)NODE_OPTIONS=--openssl-legacy-provider npx vue-cli-service buildDONE Build complete.The nine new tests cover
grossPriceCentsFromComponentsagainst acomponentsblock 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:Math.round(grossEur * 100)with atoFixed(2)-based round mades + 250GB rounds half-up to 3749 centsfail withExpected: 3749, Received: 3748.m + 2180GB groups in euro and yields 19099 centsfail withExpected: 19099, Received: 19100.Not verified, and why
@vue/test-utilsis 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, thev-ifguards that hide prices when the fetch fails, and theconsole.warnon mismatch are covered by reading, not by a test. The arithmetic underneath them is covered by tests.Judgement calls
src/lib/pricing.jsrather 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 thelibscope alongsidesettings.d-inline-blockbeside the button group, and a block-level element between them would push it onto its own line.FreeshardBase/web-terminal#50(merging the Subscription/Size/Disk cards) is untouched — no card merged, moved or restyled.volume_size_gbrather 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