Repository navigation
settings: show the exact next-charge timestamp - #57
Merged
Merged
Conversation
The subscription card rendered next_billing_date through formatDateHumanize, so an active subscriber read "Next charge in a month." With a paid subscription the length of the current term and the date of the next charge have to be unambiguous; a humanised interval is the kind of statement a competitor can attack under the UWG. The card now reads "Next charge: 2026-07-01 13:30", which is also the wording the PayPal billing design spec asks for. next_billing_date is a timestamptz taken from PayPal's next_billing_time, so rendering a time next to the date reflects the data rather than padding it. formatDate was the only absolute-date precedent in the codebase and had no call sites. It formatted with hh, a 12-hour clock with no meridiem, which renders 13:30 as 01:30 and would have carried the ambiguity straight back into the billing line. Its body moves to src/lib/dates.js on HH, where the unit suite can reach it, following the same extraction as lib/pricing.js.
…ct date on hover Max' call on the first version: for the next charge only the day matters, not the hour. formatDate loses the time component, which also settles the hh-versus-HH question the previous commit had to solve, since there is no clock left to be ambiguous about. The billing line reads "Next charge: 2026-07-01." The card states seven other dates as intervals, from "in a month" to "3 days ago". None of them is a charge falling due, so none needed the same treatment, but an interval is still the wrong thing to read when the question is which day something happens. Hovering now shows the calendar date. bootstrap-vue is already a dependency and b-tooltip is already used in the navbar, so this is the v-b-tooltip directive plus an optional prop on TextField, whose three About-overlay fields take their content as a string and had nowhere to hang a title.
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 #56.
The Subscription card's active state rendered
subscription.next_billing_datethrough theformatDateHumanizefilter, so a paying subscriber read "Next charge in a month." It now reads "Next charge: 2026-07-01."Per the comment on the issue, this is not a polish item: with a paid subscription the length of the current term and the date of the next charge have to be unambiguous, and a humanised interval is what creates the UWG exposure. The
Next charge:wording is whatfreeshard-controller/docs/superpowers/specs/2026-04-24-paypal-billing-design.mdspecifies for the Active state.What changed
src/views/Settings.vue— the next-charge line usesformatDateinstead offormatDateHumanize.src/lib/dates.js(new) —formatDate(value), the body of the existingformatDatefilter, extracted so the unit suite can reach it. Same extraction pattern assrc/lib/pricing.js.src/main.js— theformatDatefilter is registered from that function.src/components/TextField.vue— one optionaltooltipprop, for the hover described below.tests/unit/dates.spec.js(new) — two cases on the formatter.The date carries no clock time
The first version of this PR rendered
2026-07-01 13:30. Only the day matters for a charge, so the time is gone. That also disposes of a trap in the filter being reused: it formatted withhh, a 12-hour clock with no meridiem, which renders 13:30 as01:30. With no clock component there is nothing left to be ambiguous about. The filter had no call sites before this PR, so nothing else on screen moved.The other seven dates now show the exact date on hover
The card states seven further dates as intervals:
delete_afterin the trial and grace states,last_payment_failed_at,ended, the backup passphrase access time, andtime_created/time_assigned/delete_afterin the About overlay. None of them is a charge falling due, so none carries the billing-transparency requirement that motivates the change above, and they keep reading as intervals, which is the right default for "3 days ago".But an interval is the wrong thing to read when the question is which day something happens, so hovering any of them now shows the calendar date.
bootstrap-vueis already a dependency andb-tooltipis already used inNavbar.vue, so this is thev-b-tooltipdirective and nothing new. The three About-overlay fields go throughTextField, which takes its content as a string and had nowhere to hang a title, so it gained one optionaltooltipprop; it is used only by this view and the prop is inert when absent.Test evidence
The suite is a pure-unit one (
tests/unit/, no@vue/test-utilsin the repo), so the tests cover the formatter, not the template. The formatter test was broken on purpose to check it has teeth: puttingHH:mmback into the format string fails both cases, 2 failed / 28 passed. Restored, 30 pass across 4 suites.npx vue-cli-service lintis clean, andnpm run buildcompletes, which is what compiles the templates the tooltips were added to. Locally the build needsNODE_OPTIONS=--openssl-legacy-provideron Node 24; theservescript already carries that flag andbuilddoes not, which is a pre-existing wrinkle this PR does not touch.Note on the issue body
The issue quotes
next_charge_atatSettings.vue:70. On currentmainthe field issubscription.next_billing_dateand the line is 92 — the card was reworked after the issue was filed. The bug the issue describes is the same one; only the field name moved.