Public Access
feat(bf): Server Location selector beside the deal Buy, carried into both buy forms
Move the homepage's Server Location lockup (label + select) out of section#pricing
and into the HOST27 deal card's CTA row, next to the Buy button. There is still
exactly ONE element with id pricingServerLocation on the page, so buyNow() — which
resolves the selector by id — and every pricing button keep working untouched.
The pricing block keeps only the Odoo Version selector and reads the same global
location selection.
- markup: new .bf-cta-row in the deal form (selector left, brass Buy right);
location lockup removed from .pricing-server-row
- CSS (page-scoped): .bf-cta-row / .bf-cta-location, and the light-on-ivory twin
of .pricing-server-select (style.css carries neither; the homepage styles it
inline for its dark section). Wraps at 375px with no horizontal overflow; the
Buy stays the one dominant action.
- static/black-friday.js: on submit of the native post_basket forms, write the
chosen city + version into the basket line's `info`, exactly as buyNow() does.
Additive only — with JavaScript off the POST is unchanged (no info) and the
selector is empty because its options are built here (pre-existing behaviour).
Untouched: the hidden redirect=/coupon/HOST27, the single plausible-event-name=Buy
mark, the price, the countdown, #trialForm, the booking link and the consent gate.
Verified: verify-html.py PROBLEMS 0; competitor QC exit 0 on the page (74
pre-existing site-wide, 0 added); node --check OK; impeccable detect 35 findings,
identical set to HEAD (0 introduced); live browser: fastest server auto-selected,
wire body basket=[{"product_id":4,"qty":1,"info":"boston|20.0"}] and it tracks the
selectors on repeat submits; 375px no overflow.
This commit is contained in:
1 parent
0f8ae45e59
commit
9e38e05aad
2 files changed
+93
-3
No files matched your search
@@ -274,6 +274,52 @@
|
|||||||
outline: 3px solid var(--midnight);
|
outline: 3px solid var(--midnight);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
/* The deal's CTA row (2026-11-02): the brass/large Buy keeps its weight and stays the ONE
|
||||||
|
dominant action; the Server Location selector sits beside it as a compact, quiet form
|
||||||
|
control, never a second button. The row wraps on a narrow screen — the selector holds a
|
||||||
|
~190px basis and the Buy takes the line below — so nothing overflows at 375px. */
|
||||||
|
.bf-cta-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 0.6rem 0.9rem;
|
||||||
|
}
|
||||||
|
.bf-cta-location {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.35rem;
|
||||||
|
flex: 0 1 190px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.bf-cta-location label {
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--brass-deep);
|
||||||
|
}
|
||||||
|
/* The homepage's .pricing-server-select is styled for its dark section and lives only in
|
||||||
|
index.html's inline CSS — style.css carries neither. On this ivory card the selector
|
||||||
|
needs the light twin, so it is defined page-scoped here. */
|
||||||
|
.bf-cta-location .pricing-server-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: 1px solid var(--line-brass);
|
||||||
|
background: #fff;
|
||||||
|
color: var(--ink);
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.bf-cta-location .pricing-server-select:focus-visible {
|
||||||
|
outline: 3px solid var(--midnight);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.bf-cta-row .btn {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
.bf-cta-secondary {
|
.bf-cta-secondary {
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -769,7 +815,19 @@
|
|||||||
<input type="hidden" name="basket" value='[{"product_id":4,"qty":1}]' />
|
<input type="hidden" name="basket" value='[{"product_id":4,"qty":1}]' />
|
||||||
<input type="hidden" name="clear" value="1" />
|
<input type="hidden" name="clear" value="1" />
|
||||||
<input type="hidden" name="redirect" value="/coupon/HOST27?r=/shop/checkout" />
|
<input type="hidden" name="redirect" value="/coupon/HOST27?r=/shop/checkout" />
|
||||||
<button type="submit" class="btn btn-brass btn-lg">Buy On the Rise — $295 first year</button>
|
<!-- Server Location, MOVED here from the pricing block below (2026-11-02): this is
|
||||||
|
the ONE element with id pricingServerLocation on the page, so buyNow() — which
|
||||||
|
reads it by id — and both native buy forms keep working untouched. Its options
|
||||||
|
are built and its fastest server auto-selected by /static/black-friday.js. It
|
||||||
|
is a quiet form control (no `name`, so it is not itself submitted), never a
|
||||||
|
second button; the brass Buy beside it stays the one dominant action. -->
|
||||||
|
<div class="bf-cta-row">
|
||||||
|
<div class="bf-cta-location">
|
||||||
|
<label for="pricingServerLocation">Server Location</label>
|
||||||
|
<select id="pricingServerLocation" class="pricing-server-select"></select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-brass btn-lg">Buy On the Rise — $295 first year</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p class="bf-cta-secondary"><a href="/odoo-hosting-offers/">See everything a year of hosting includes</a></p>
|
<p class="bf-cta-secondary"><a href="/odoo-hosting-offers/">See everything a year of hosting includes</a></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -955,8 +1013,10 @@
|
|||||||
<p class="section-kicker">Order & Pricing</p>
|
<p class="section-kicker">Order & Pricing</p>
|
||||||
<h2 class="section-title">Your Plan, Your Timing: Upgrade Anytime</h2>
|
<h2 class="section-title">Your Plan, Your Timing: Upgrade Anytime</h2>
|
||||||
<div class="pricing-server-row">
|
<div class="pricing-server-row">
|
||||||
<label for="pricingServerLocation">Server Location</label>
|
<!-- The Server Location selector was moved into the Black Friday deal card (#offer):
|
||||||
<select id="pricingServerLocation" class="pricing-server-select"></select>
|
there must be exactly ONE element with id pricingServerLocation, and the deal card
|
||||||
|
is where the buyer commits. The pricing buttons read that same global selection by
|
||||||
|
id via buyNow(). Only the version selector stays here. -->
|
||||||
<label for="pricingOdooVersion">Odoo Version</label>
|
<label for="pricingOdooVersion">Odoo Version</label>
|
||||||
<select id="pricingOdooVersion" class="pricing-server-select">
|
<select id="pricingOdooVersion" class="pricing-server-select">
|
||||||
<option value="19.0">19.0 — Tons of OCA supported modules</option>
|
<option value="19.0">19.0 — Tons of OCA supported modules</option>
|
||||||
|
|||||||
@@ -307,6 +307,36 @@
|
|||||||
document.body.removeChild(form);
|
document.body.removeChild(form);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ---- Carry the chosen Server Location + Odoo version into the native buy forms ----
|
||||||
|
* The Black Friday buy buttons are plain form POSTs to /shop/post_basket, so they work with
|
||||||
|
* JavaScript off. buyNow() builds `info` for the JS buttons; this does exactly the same for
|
||||||
|
* the native forms, on submit, from the one global selector pair (#pricingServerLocation +
|
||||||
|
* #pricingOdooVersion). Additive only: with JavaScript off the basket posts exactly as before
|
||||||
|
* (no info) and the selector is empty anyway because its options are built here. The POST is
|
||||||
|
* untouched — only the form's own hidden `basket` input is rewritten. */
|
||||||
|
(function () {
|
||||||
|
var forms = document.querySelectorAll('form[action*="/shop/post_basket"]');
|
||||||
|
forms.forEach(function (form) {
|
||||||
|
form.addEventListener('submit', function () {
|
||||||
|
var basket = form.querySelector('input[name="basket"]');
|
||||||
|
if (!basket) return;
|
||||||
|
var locSelect = document.getElementById('pricingServerLocation');
|
||||||
|
var verSelect = document.getElementById('pricingOdooVersion');
|
||||||
|
var location = locSelect ? locSelect.value : '';
|
||||||
|
var version = verSelect ? verSelect.value : '';
|
||||||
|
var info = location ? location.replace('.odoo4projects.com', '') : '';
|
||||||
|
if (version) { info += (info ? '|' : '') + version; }
|
||||||
|
if (!info) return;
|
||||||
|
try {
|
||||||
|
var items = JSON.parse(basket.value);
|
||||||
|
if (!Array.isArray(items)) return;
|
||||||
|
items.forEach(function (it) { if (it) { it.info = info; } });
|
||||||
|
basket.value = JSON.stringify(items);
|
||||||
|
} catch (e) { /* leave the basket untouched rather than break the purchase */ }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
/* ---- Buy buttons + Custom Quote modal ---- */
|
/* ---- Buy buttons + Custom Quote modal ---- */
|
||||||
(function () {
|
(function () {
|
||||||
document.querySelectorAll('.btn-buy').forEach(function (btn) {
|
document.querySelectorAll('.btn-buy').forEach(function (btn) {
|
||||||
|
|||||||
Reference in new issue
Block a user