v0.2.0:36 — Launch-special remaining: "N remaining", drop the total
Buy-page tier card's "Limited: N of M remaining" line now reads just "Limited: N remaining". The total cap (M) is operator-private — there's no upside to exposing initial launch volume to buyers, and it can make a tier feel smaller than the operator intends. Symmetric landing-page change (index.html dynamic tier-card JS) ships alongside in a separate commit on the keysat-xyz-landing repo. Cosmetic; no API or schema change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1166,7 +1166,7 @@ fn render_tier_picker(
|
|||||||
// the launch-meta div as an in-flow element — that coupling
|
// the launch-meta div as an in-flow element — that coupling
|
||||||
// made cross-card row alignment impossible).
|
// made cross-card row alignment impossible).
|
||||||
// - featured_ribbon: absolutely-positioned diagonal banner
|
// - featured_ribbon: absolutely-positioned diagonal banner
|
||||||
// - launch_meta_html: in-flow "Limited: X of Y remaining"
|
// - launch_meta_html: in-flow "Limited: N remaining"
|
||||||
// - original_price_html: in-flow struck-through original
|
// - original_price_html: in-flow struck-through original
|
||||||
let (featured_ribbon, launch_meta_html, original_price_html) = if let Some(code) = featured {
|
let (featured_ribbon, launch_meta_html, original_price_html) = if let Some(code) = featured {
|
||||||
let tagline = if code.kind == "percent" {
|
let tagline = if code.kind == "percent" {
|
||||||
@@ -1179,11 +1179,13 @@ fn render_tier_picker(
|
|||||||
"LAUNCH SPECIAL".to_string()
|
"LAUNCH SPECIAL".to_string()
|
||||||
};
|
};
|
||||||
let remaining = code.max_uses.map(|m| (m - code.used_count).max(0)).unwrap_or(-1);
|
let remaining = code.max_uses.map(|m| (m - code.used_count).max(0)).unwrap_or(-1);
|
||||||
|
// Display: "Limited: N remaining" (not "N of M remaining").
|
||||||
|
// The total cap is operator-private — buyers don't need to
|
||||||
|
// infer launch volume from the M.
|
||||||
let launch_meta = if remaining > 0 {
|
let launch_meta = if remaining > 0 {
|
||||||
format!(
|
format!(
|
||||||
"<div class=\"tier-launch-meta\">Limited: {} of {} remaining</div>",
|
"<div class=\"tier-launch-meta\">Limited: {} remaining</div>",
|
||||||
remaining,
|
remaining,
|
||||||
code.max_uses.unwrap_or(0)
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ const RELEASE_NOTES = [
|
|||||||
// in RELEASE_NOTES above (the milestone). Subsequent revisions
|
// in RELEASE_NOTES above (the milestone). Subsequent revisions
|
||||||
// append here.
|
// append here.
|
||||||
const ROUTINE_NOTES = [
|
const ROUTINE_NOTES = [
|
||||||
|
'0.2.0:36 — **Launch-special remaining count drops the total.** The buy-page tier card\'s "Limited: N of M remaining" line now reads just "Limited: N remaining". The total cap (M) is operator-private — there\'s no upside to exposing initial volume to buyers, and it can make a tier look smaller than the operator wants to signal. Symmetric change in the landing-page dynamic tier-card render. Cosmetic; no API or schema change.',
|
||||||
|
'',
|
||||||
'0.2.0:35 — **Free tiers render as "Free" on the buy page tier card.** Previously the server rendered a 0-priced tier as `0 sats` (or `0.00 USD`) in the tier-card price headline, even though the price card BELOW the picker already swapped to `FREE` via the JS path. Inconsistency fixed at the server-render layer: when post-discount price is 0, the tier card renders the headline as `Free` with no unit suffix and no cadence (`Free /yr` would be incoherent). Recurring-meta line ("Renews annually") still surfaces beneath for recurring tiers that happen to be free, so the cadence is still visible — just not stuffed into the headline. Cosmetic; no API or schema change.',
|
'0.2.0:35 — **Free tiers render as "Free" on the buy page tier card.** Previously the server rendered a 0-priced tier as `0 sats` (or `0.00 USD`) in the tier-card price headline, even though the price card BELOW the picker already swapped to `FREE` via the JS path. Inconsistency fixed at the server-render layer: when post-discount price is 0, the tier card renders the headline as `Free` with no unit suffix and no cadence (`Free /yr` would be incoherent). Recurring-meta line ("Renews annually") still surfaces beneath for recurring tiers that happen to be free, so the cadence is still visible — just not stuffed into the headline. Cosmetic; no API or schema change.',
|
||||||
'',
|
'',
|
||||||
'0.2.0:34 — **Buy page: featured discount pre-populates the code field and shows "Launch special applied" on load.** Previously the launch-special discount auto-applied silently at payment time but the discount-code input was empty, leaving buyers unsure whether they needed to type anything. Now: when a tier has an active featured code, the input renders pre-filled with the code (e.g. `LAUNCH`) and the green "Launch special applied" status badge shows on load. The price card has always rendered the slashed-original + discounted-current price; this change just makes the form match. Tier switches clear an auto-populated code so a code that doesn\'t apply to the new tier doesn\'t linger; buyer-typed codes are untouched. UI / JS only; no API or schema change.',
|
'0.2.0:34 — **Buy page: featured discount pre-populates the code field and shows "Launch special applied" on load.** Previously the launch-special discount auto-applied silently at payment time but the discount-code input was empty, leaving buyers unsure whether they needed to type anything. Now: when a tier has an active featured code, the input renders pre-filled with the code (e.g. `LAUNCH`) and the green "Launch special applied" status badge shows on load. The price card has always rendered the slashed-original + discounted-current price; this change just makes the form match. Tier switches clear an auto-populated code so a code that doesn\'t apply to the new tier doesn\'t linger; buyer-typed codes are untouched. UI / JS only; no API or schema change.',
|
||||||
@@ -495,7 +497,7 @@ const ROUTINE_NOTES = [
|
|||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
|
|
||||||
export const v0_2_0 = VersionInfo.of({
|
export const v0_2_0 = VersionInfo.of({
|
||||||
version: '0.2.0:35',
|
version: '0.2.0:36',
|
||||||
releaseNotes: { en_US: ROUTINE_NOTES },
|
releaseNotes: { en_US: ROUTINE_NOTES },
|
||||||
// No on-disk transformation needed — v0.2.0:0 is a label change.
|
// No on-disk transformation needed — v0.2.0:0 is a label change.
|
||||||
// SQLite-level migrations live separately under
|
// SQLite-level migrations live separately under
|
||||||
|
|||||||
Reference in New Issue
Block a user