Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions data/seeds/mint-on-the-avenue.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
],
"priceRange": "$$-$$$",
"bookingPlatform": "Phorest",
"bookingUrl": "TO_CONFIRM — 'Book Now via Phorest' target URL not captured from static HTML",
"bookingUrl": "https://www.phorest.com/salon/mintontheavenue",
"$bookingNote": "Confirmed by Mark 2026-07-07 — the canonical Phorest booking page. mintontheavenue.com/request-appointment is being phased out.",
"gbp": {
"listedName": "Mint On The Avenue Aveda Salon",
"placeId": "ChIJQfwznhBw54gRZHRHaFdWJxA",
Expand Down Expand Up @@ -229,7 +230,6 @@
],

"toConfirmWithMark": [
"Exact Phorest booking URL (Book Now target)",
"Social links (Instagram/Facebook) — not present in crawled static HTML",
"Canonical text number (833 vs 830 — 833 presumed, matches homepage + blog)",
"Canonical price source (service pages vs homepage widget)",
Expand Down
9 changes: 6 additions & 3 deletions src/server/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface SalonDoc {
cityId?: string | null;
entity?: SalonEntityLike & {
textLine?: string;
bookingUrl?: string;
positioning?: { tagline?: string; heroLine?: string; philosophy?: string };
serviceInclusions?: string;
stylistLevels?: string;
Expand All @@ -60,15 +61,17 @@ function track(db: NedbClient, event: Record<string, unknown>): void {
});
}

/** Tracked CTA row — every conversion path goes through /go. */
/** Tracked CTA row — every conversion path goes through /go. The booking
* URL is DATA (entity.bookingUrl from the seed) — a salon without one
* simply doesn't get the button; call/text remain. */
function ctaRow(salon: SalonDoc, origin: string): string {
const e = salon.entity ?? {};
const tel = telHref(e.nap?.phone);
const sms = smsHref(e.textLine);
const book = "https://mintontheavenue.com/request-appointment/";
const book = typeof e.bookingUrl === "string" && /^https:\/\//.test(e.bookingUrl) ? e.bookingUrl : null;
const go = (kind: string, to: string) => `/go/${kind}?to=${encodeURIComponent(to)}`;
const parts: string[] = [];
parts.push(`<a class="btn solid" href="${esc(go("booking_click", book))}">Book an appointment</a>`);
if (book) parts.push(`<a class="btn solid" href="${esc(go("booking_click", book))}">Book an appointment</a>`);
if (sms) parts.push(`<a class="btn ghost" href="${esc(go("text_click", sms))}">Text ${esc(e.textLine ?? "")}</a>`);
if (tel) parts.push(`<a class="btn ghost" href="${esc(go("call_click", tel))}">Call ${esc(e.nap?.phone ?? "")}</a>`);
if (e.gbp?.mapsUrl) parts.push(`<a class="chip" href="${esc(go("direction_click", e.gbp.mapsUrl))}">Directions</a>`);
Expand Down
4 changes: 4 additions & 0 deletions test/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ test("orlando surfaces: server-rendered pages from real engine data", async () =
assert.ok(!homeHtml.includes("aggregateRating"), "no self-marked-up Google rating");
assert.ok(homeHtml.includes(`<meta name="description"`), "meta description present");
assert.ok(homeHtml.includes("/go/booking_click?to="), "booking CTA is click-tracked");
assert.ok(
homeHtml.includes(encodeURIComponent("https://www.phorest.com/salon/mintontheavenue")),
"booking CTA targets the canonical Phorest page (data-driven from the seed)",
);
assert.ok(homeHtml.includes("Sonia Taylor"), "canonical roster renders");

// service category page — real price table
Expand Down
Loading