rspace-online/modules/rschedule/components/folk-schedule-booking.ts

96 lines
3.0 KiB
TypeScript

/**
* <folk-schedule-booking> — 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 = `
<style>
:host { display: block; color: #e2e8f0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; min-height: 100vh; background: #0b1120; }
.wrap { max-width: 980px; margin: 0 auto; padding: 48px 24px; }
.card { background: #111827; border: 1px solid rgba(148,163,184,0.12); border-radius: 16px; padding: 32px; }
h1 { margin: 0 0 8px; font-size: 1.6rem; background: linear-gradient(to right, #06b6d4, #8b5cf6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.meta { display: flex; gap: 16px; color: #94a3b8; font-size: 0.9rem; margin-bottom: 16px; }
.msg { color: #cbd5e1; margin: 0 0 24px; white-space: pre-wrap; }
.placeholder { padding: 40px; text-align: center; border: 1px dashed rgba(148,163,184,0.25); border-radius: 10px; color: #94a3b8; }
</style>
<div class="wrap">
<div class="card">
<h1>Book with ${escapeHtml(name)}</h1>
<div class="meta">
<span>⏱ ${duration} min</span>
${s?.timezone ? `<span>🌐 ${escapeHtml(s.timezone)}</span>` : ""}
</div>
<p class="msg">${escapeHtml(msg)}</p>
<div class="placeholder">
Date picker and slot list coming in Phase B.<br>
Admin: <a href="admin" style="color:#06b6d4">configure availability</a>
</div>
</div>
</div>
`;
}
}
function escapeHtml(s: string): string {
return String(s)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
customElements.define("folk-schedule-booking", FolkScheduleBooking);
export {};