The sales we got came at $79 + a 24hr coupon; cutting MCS-150 to $39 flat removed urgency and conversions did NOT improve (a permanent low price sets a new anchor and lets people defer). Restore the higher anchor and let an expiring discount create the now-or-lose-it decision. - Restore MCS-150 anchor $39 -> $79 (catalog single source + regenerated). - build_trucking_campaigns.py: mint ONE random 5-letter coupon per send-day (40% off, valid through 23:59:59 ET that day) into the existing discount_codes table; inject coupon_code/pct/expires + a ?code= LP link into every email. Idempotent per day; service-fee-only scope (gov/pass-through fees never cut). - Listmonk MCS-150 (186) + Inactive USDOT (188) templates: lead with the struck-through anchor + sale price + code + 'expires tonight', and point the primary CTA at the order page (with code) instead of the 'free check' tool. - OrderPriceBanner: validates ?code= via /api/v1/discount and shows was/now + expiry; Wizard forwards the code to order creation. - Verified: code gen, expiry math, scope enforcement, discount API (40% off $79 = $47.40), site+api builds clean.
134 lines
4.1 KiB
Text
134 lines
4.1 KiB
Text
---
|
|
import { formatUSD } from "../lib/intake_manifest";
|
|
|
|
export interface Props {
|
|
priceCents?: number;
|
|
govFeeLabel?: string;
|
|
note?: string;
|
|
serviceSlug?: string;
|
|
}
|
|
|
|
const { priceCents, govFeeLabel, note, serviceSlug } = Astro.props;
|
|
const hasPrice = typeof priceCents === "number";
|
|
const API = import.meta.env.PUBLIC_API_URL || "";
|
|
---
|
|
|
|
{hasPrice && (
|
|
<aside
|
|
class="pw-price-banner"
|
|
aria-label="Service price"
|
|
data-price-cents={priceCents}
|
|
data-service-slug={serviceSlug || ""}
|
|
data-api={API}
|
|
>
|
|
<div>
|
|
<p class="pw-price-label">Service fee</p>
|
|
<p class="pw-price-value">
|
|
<span class="pw-price-anchor" hidden></span>
|
|
<span class="pw-price-now">{formatUSD(priceCents!)}</span>
|
|
</p>
|
|
<p class="pw-price-deal" hidden></p>
|
|
</div>
|
|
<div class="pw-price-copy">
|
|
{govFeeLabel ? (
|
|
<p><strong>Pass-through fees:</strong>{" "}{govFeeLabel}. Billed separately at cost.</p>
|
|
) : (
|
|
<p>No hidden service fee. You will review the total before payment.</p>
|
|
)}
|
|
{note && <p>{note}</p>}
|
|
</div>
|
|
</aside>
|
|
)}
|
|
|
|
<script>
|
|
// Same-day campaign coupon: if the visitor arrived via ?code=XXXXX, validate
|
|
// it and surface the deal (struck-through anchor + discounted price + expiry).
|
|
// The discount applies to the service fee only; pass-through fees are unaffected.
|
|
(async () => {
|
|
const banner = document.querySelector<HTMLElement>(".pw-price-banner");
|
|
if (!banner) return;
|
|
const code = (new URLSearchParams(location.search).get("code") || "").toUpperCase().trim();
|
|
if (!code) return;
|
|
|
|
const priceCents = parseInt(banner.dataset.priceCents || "0", 10);
|
|
const slug = banner.dataset.serviceSlug || "";
|
|
const api = banner.dataset.api || "";
|
|
if (!priceCents) return;
|
|
|
|
try {
|
|
const url = `${api}/api/v1/discount/${encodeURIComponent(code)}`
|
|
+ `?amount=${priceCents}${slug ? `&service=${encodeURIComponent(slug)}` : ""}`;
|
|
const r = await fetch(url);
|
|
if (!r.ok) return;
|
|
const d = await r.json();
|
|
if (!d || !d.valid || !d.discount_cents) return;
|
|
|
|
const now = priceCents - d.discount_cents;
|
|
const fmt = (c: number) => `$${(c / 100).toFixed(c % 100 === 0 ? 0 : 2)}`;
|
|
|
|
const anchor = banner.querySelector<HTMLElement>(".pw-price-anchor");
|
|
const nowEl = banner.querySelector<HTMLElement>(".pw-price-now");
|
|
const deal = banner.querySelector<HTMLElement>(".pw-price-deal");
|
|
if (anchor) { anchor.textContent = fmt(priceCents); anchor.hidden = false; }
|
|
if (nowEl) nowEl.textContent = fmt(now);
|
|
if (deal) {
|
|
const pct = d.discount_type === "percent" ? `${d.discount_value}% off` : `${fmt(d.discount_cents)} off`;
|
|
deal.innerHTML = `<strong>${pct}</strong> with code <strong>${code}</strong> · expires 11:59 PM ET tonight`;
|
|
deal.hidden = false;
|
|
}
|
|
} catch { /* no-op: fall back to full price */ }
|
|
})();
|
|
</script>
|
|
|
|
<style>
|
|
.pw-price-banner {
|
|
display: flex;
|
|
gap: 1.25rem;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 1rem 1.15rem;
|
|
margin: 1rem 0 1.5rem;
|
|
border: 2px solid #fed7aa;
|
|
border-radius: 14px;
|
|
background: linear-gradient(135deg, #fff7ed, #ffffff);
|
|
box-shadow: 0 10px 28px rgba(249, 115, 22, 0.10);
|
|
}
|
|
.pw-price-label {
|
|
margin: 0 0 0.15rem;
|
|
color: #9a3412;
|
|
font-size: 0.78rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
.pw-price-value {
|
|
margin: 0;
|
|
color: #ea580c;
|
|
font-size: clamp(2rem, 5vw, 3rem);
|
|
line-height: 1;
|
|
font-weight: 900;
|
|
}
|
|
.pw-price-anchor {
|
|
color: #9ca3af;
|
|
font-weight: 700;
|
|
text-decoration: line-through;
|
|
font-size: 0.55em;
|
|
margin-right: 0.4em;
|
|
}
|
|
.pw-price-deal {
|
|
margin: 0.45rem 0 0;
|
|
color: #b91c1c;
|
|
font-size: 0.85rem;
|
|
font-weight: 700;
|
|
}
|
|
.pw-price-copy {
|
|
max-width: 30rem;
|
|
color: #475569;
|
|
font-size: 0.95rem;
|
|
line-height: 1.45;
|
|
}
|
|
.pw-price-copy p { margin: 0.2rem 0; }
|
|
@media (max-width: 640px) {
|
|
.pw-price-banner { align-items: flex-start; flex-direction: column; }
|
|
}
|
|
</style>
|