/** * — public booking page. * * Phase A: stub that fetches public settings + availability and shows a * placeholder date picker. Phase B ports the full UI from * schedule-jeffemmett/src/app/page.tsx (month calendar, slot list, timezone * toggle, world map, booking form). */ interface PublicSettings { displayName: string; bookingMessage: string; slotDurationMin: number; maxAdvanceDays: number; minNoticeHours: number; timezone: string; } class FolkScheduleBooking extends HTMLElement { private shadow: ShadowRoot; private space = ""; private settings: PublicSettings | null = null; constructor() { super(); this.shadow = this.attachShadow({ mode: "open" }); } connectedCallback() { this.space = this.getAttribute("space") || "demo"; void this.load(); } private async load() { try { const base = this.apiBase(); const res = await fetch(`${base}/settings/public`); this.settings = res.ok ? await res.json() : null; } catch { this.settings = null; } this.render(); } private apiBase(): string { const path = window.location.pathname; const match = path.match(/^(\/[^/]+)?\/rschedule/); return `${match?.[0] || "/rschedule"}/api`; } private render() { const s = this.settings; const name = s?.displayName || this.space; const msg = s?.bookingMessage || "Book a time to chat."; const duration = s?.slotDurationMin ?? 30; this.shadow.innerHTML = `

Book with ${escapeHtml(name)}

⏱ ${duration} min ${s?.timezone ? `🌐 ${escapeHtml(s.timezone)}` : ""}

${escapeHtml(msg)}

Date picker and slot list coming in Phase B.
Admin: configure availability
`; } } function escapeHtml(s: string): string { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } customElements.define("folk-schedule-booking", FolkScheduleBooking); export {};