v0.2.0:39 — Buy page: render tier card for single-public-policy products
Previously the tier picker gated on `policies.len() < 2` and returned an empty string when a product had only one public policy. Buyers saw just the price card + form — none of the entitlements, marketing bullets, or description the operator had carefully authored on that tier. Reported against the Recap product, which has 3 policies but only Pro public; Pro's bullets were invisible to buyers. Fixed: - render_tier_picker gate flipped from `< 2` to `is_empty()`. A single public policy now renders a single tier card. - New `.tiers-1` grid class: one centered column at ~480px max-width. Keeps the single card from stretching to the full 1040px container. - `n` computation extends to handle 1 in the existing match arm. The price card below the picker still renders unchanged for the single-policy case — acts as the buy-confirmation summary. Operators keeping most tiers private and only exposing one to buyers now get the same rich tier-card render that multi-tier products always had. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -305,6 +305,10 @@ h1 {{
|
||||
1fr /* row 7: features (fills) */
|
||||
auto; /* row 8: button */
|
||||
}}
|
||||
/* Single-policy products: one centered card at a comfortable width.
|
||||
Wide enough to read entitlements + marketing bullets clearly without
|
||||
stretching across the full container. */
|
||||
.tiers-1 {{ grid-template-columns:minmax(0, 480px); justify-content:center; }}
|
||||
.tiers-2 {{ grid-template-columns:repeat(2, 1fr); }}
|
||||
.tiers-3 {{ grid-template-columns:repeat(3, 1fr); }}
|
||||
.tiers-4 {{ grid-template-columns:repeat(2, 1fr); }}
|
||||
@@ -1093,19 +1097,23 @@ footer.kfooter a:hover {{ color:var(--navy-900); }}
|
||||
}
|
||||
|
||||
/// Build the server-rendered tier-picker HTML. Returns an empty string
|
||||
/// when the product has fewer than 2 public policies (i.e., the existing
|
||||
/// single-price view is sufficient).
|
||||
/// only when the product has zero public policies (the bare price-card +
|
||||
/// form fallback covers that case). For one public policy, we still
|
||||
/// render a single tier card so the operator-configured entitlements
|
||||
/// and marketing bullets surface — without this, single-tier products
|
||||
/// showed only price + form, eating the operator's tier copy.
|
||||
fn render_tier_picker(
|
||||
policies: &[crate::models::Policy],
|
||||
initial: &Option<crate::models::Policy>,
|
||||
product: &crate::models::Product,
|
||||
featured_by_policy: &std::collections::HashMap<String, crate::models::DiscountCode>,
|
||||
) -> String {
|
||||
if policies.len() < 2 {
|
||||
if policies.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
let n = policies.len().min(4);
|
||||
let class_n = match n {
|
||||
1 => "tiers-1",
|
||||
2 => "tiers-2",
|
||||
3 => "tiers-3",
|
||||
_ => "tiers-4",
|
||||
|
||||
@@ -58,6 +58,8 @@ const RELEASE_NOTES = [
|
||||
// in RELEASE_NOTES above (the milestone). Subsequent revisions
|
||||
// append here.
|
||||
const ROUTINE_NOTES = [
|
||||
'0.2.0:39 — **Buy page now renders a tier card for single-public-policy products.** Previously the tier picker only rendered when a product had two or more public policies; single-public-policy products fell back to a bare price card + form, swallowing all the operator-configured entitlements, marketing bullets, and tier descriptions. Fixed: render a single centered tier card (new `.tiers-1` grid class, ~480px max-width) whenever there\'s at least one public policy. Operators who keep most tiers private and only expose one (e.g. "Pro" public, "Core" and "Max" admin-only) now see the same rich tier-card render that multi-tier products get. The price card below still renders unchanged as the buy-confirmation summary.',
|
||||
'',
|
||||
'0.2.0:38 — **Admin UI: Create-product Cancel button + modal-overflow fix across all dialogs.** Two operator-reported bugs.',
|
||||
'',
|
||||
'**Create product: Cancel button.** The "Create a new product" disclosure had a Create button but no way to back out without scrolling up to the chevron. Added a secondary Cancel button alongside Create — collapses the disclosure (returns to the products list) without clearing typed input, so re-expanding picks up where the operator left off.',
|
||||
@@ -505,7 +507,7 @@ const ROUTINE_NOTES = [
|
||||
].join('\n\n')
|
||||
|
||||
export const v0_2_0 = VersionInfo.of({
|
||||
version: '0.2.0:38',
|
||||
version: '0.2.0:39',
|
||||
releaseNotes: { en_US: ROUTINE_NOTES },
|
||||
// No on-disk transformation needed — v0.2.0:0 is a label change.
|
||||
// SQLite-level migrations live separately under
|
||||
|
||||
Reference in New Issue
Block a user