<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Dental Scheduling Widget</title> <style> *{box-sizing:border-box;font-family:Arial,Helvetica,sans-serif} body{margin:0;padding:0;background:#f7f7f7} #widget-container{max-width:420px;margin:20px auto;padding:20px;background:#fff;border-radius:12px;box-shadow:0 4px 14px rgba(0,0,0,.08)} h2{margin-top:0;text-align:center} form,.step{display:flex;flex-direction:column;gap:12px} label{display:flex;flex-direction:column;font-size:.9rem} input[type="text"],input[type="tel"],input[type="date"]{padding:8px 10px;border:1px solid #ccc;border-radius:6px;font-size:.95rem} input[type="checkbox"]{margin-right:6px} .btn{padding:10px 14px;border:none;border-radius:6px;font-size:.95rem;cursor:pointer;background:#007bff;color:#fff;transition:opacity .2s} .btn:hover{opacity:.85} .calendar-scroll{max-height:240px;overflow-y:auto;border:1px solid #e2e2e2;border-radius:8px;padding:10px;display:flex;flex-direction:column;gap:8px} .date-item{font-weight:600;margin-bottom:4px} .time-grid{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px} .time-btn{padding:6px 10px;border:1px solid #007bff;background:#fff;border-radius:5px;font-size:.85rem;cursor:pointer;transition:all .2s} .time-btn:hover{background:#007bff;color:#fff} .link{font-size:.8rem;text-align:center;margin-top:6px} pre.debug{background:#fafafa;padding:8px;border:1px dashed #ccc;border-radius:8px;font-size:.75rem;white-space:pre-wrap;margin-top:12px;overflow-x:auto} </style></head><body> <div id="widget-container"></div> <script> /* ----------------- CONFIG ----------------- */ const SCHEDULING_PORTAL_BASE = "https://example.com/schedule?phone="; // fallback link // Static dummy availability const dummySlots = { existing: { "2025-07-01": ["09:00-09:15", "11:00-11:15", "15:00-15:15"], "2025-07-02": ["09:30-09:45", "13:00-13:15", "16:00-16:15"], "2025-07-03": ["10:00-10:15", "12:30-12:45", "14:00-14:15", "16:30-16:45"] }, new: { "2025-07-01": ["10:00-10:30", "14:00-14:30"], "2025-07-02": ["11:00-11:30", "15:30-16:00"], "2025-07-03": ["09:00-09:30", "13:30-14:00"] }, emergency: { "2025-07-01": ["10:30-11:00", "12:00-12:30"], "2025-07-02": ["09:00-09:30", "14:30-15:00"], "2025-07-03": ["11:30-12:00", "15:00-15:30"] } }; /* ----------------- STATE ----------------- */ let state = { step: 1, firstName: "", lastName: "", dob: "", phone: "", isNew: false, isEmergency: false, selectedDate: "", selectedTime: "" }; const container = document.getElementById("widget-container"); /* ----------------- HELPERS ----------------- */ const createEl = (tag, props = {}, children = []) => { const el = document.createElement(tag); Object.assign(el, props); children.forEach(c => el.appendChild(typeof c === "string" ? document.createTextNode(c) : c)); return el; }; const addDebug = (parent) => { parent.appendChild(createEl("pre", {className:"debug", innerText: JSON.stringify(state,null,2)})); }; const render = () => { container.innerHTML = ""; if (state.step === 1) renderForm(); else if (state.step === 2) renderCalendar(); else if (state.step === 3) renderReview(); else if (state.step === 4) renderConfirmation(); }; // Mock APIs const apiCreatePatient = (payload) => new Promise(res => setTimeout(() => res({patientId: Date.now()}), 800)); const apiBookAppointment = (payload) => new Promise(res => setTimeout(() => res({appointmentId: Date.now()}), 800)); /* ----------------- STEP 1: FORM ----------------- */ const renderForm = () => { const form = createEl("form"); form.appendChild(createEl("h2", {innerText: "Book a Dental Appointment"})); form.appendChild(createEl("label", {}, ["First Name", createEl("input", {type:"text",required:true,oninput:e=>state.firstName=e.target.value})])); form.appendChild(createEl("label", {}, ["Last Name", createEl("input", {type:"text",required:true,oninput:e=>state.lastName=e.target.value})])); form.appendChild(createEl("label", {}, ["Date of Birth", createEl("input", {type:"date",required:true,oninput:e=>state.dob=e.target.value})])); form.appendChild(createEl("label", {}, ["Phone Number", createEl("input", {type:"tel",required:true,placeholder:"e.g. 555-123-4567",oninput:e=>state.phone=e.target.value})])); const cbNew = createEl("label", {style:"flex-direction:row;align-items:center"}, [createEl("input", {type:"checkbox",onchange:e=>state.isNew=e.target.checked}), " New Patient"]); const cbEmerg = createEl("label", {style:"flex-direction:row;align-items:center"}, [createEl("input", {type:"checkbox",onchange:e=>state.isEmergency=e.target.checked}), " Emergency"]); form.appendChild(cbNew); form.appendChild(cbEmerg); const btn = createEl("button", {className:"btn", innerText:"Continue"}); btn.addEventListener("click", async (e) => { e.preventDefault(); if (!form.checkValidity()) { form.reportValidity(); return; } btn.disabled=true;btn.innerText="Saving..."; try { if (state.isNew) { await apiCreatePatient({firstName: state.firstName, lastName: state.lastName, dob: state.dob, phone: state.phone}); } state.step = 2; render(); } finally {btn.disabled=false;btn.innerText="Continue";} }); form.appendChild(btn); form.appendChild(createEl("div", {className:"link"}, [createEl("a", {href:SCHEDULING_PORTAL_BASE + encodeURIComponent(state.phone||""), target:"_blank", innerText:"Prefer a different experience? Click here."})])); addDebug(form); container.appendChild(form); }; /* ----------------- STEP 2: CALENDAR ----------------- */ const renderCalendar = () => { const step = createEl("div", {className:"step"}); step.appendChild(createEl("h2", {innerText:"Select a Time"})); const availabilityType = state.isEmergency ? "emergency" : (state.isNew ? "new" : "existing"); const dates = Object.keys(dummySlots[availabilityType]); const calWrap = createEl("div", {className:"calendar-scroll"}); dates.forEach(dateStr => { calWrap.appendChild(createEl("div", {className:"date-item"}, [new Date(dateStr).toDateString()])); const grid = createEl("div", {className:"time-grid"}); dummySlots[availabilityType][dateStr].forEach(slot => { const btn = createEl("button", {className:"time-btn", innerText:slot}); btn.addEventListener("click", () => { state.selectedDate = dateStr; state.selectedTime = slot; state.step = 3; render(); }); grid.appendChild(btn); }); calWrap.appendChild(grid); }); step.appendChild(calWrap); addDebug(step); container.appendChild(step); }; /* ----------------- STEP 3: REVIEW ----------------- */ const renderReview = () => { const step = createEl("div", {className:"step"}); step.appendChild(createEl("h2", {innerText:"Review & Book"})); const summary = `\n${state.firstName} ${state.lastName}\nDOB: ${state.dob}\nPhone: ${state.phone}\nNew Patient: ${state.isNew ? "Yes" : "No"}\nEmergency: ${state.isEmergency ? "Yes" : "No"}\nAppointment: ${new Date(state.selectedDate).toDateString()} ${state.selectedTime}`; step.appendChild(createEl("pre", {innerText:summary, style:"background:#f1f1f1;padding:10px;border-radius:8px;font-size:.85rem"})); const bookBtn = createEl("button", {className:"btn", innerText:"Book Appointment"}); bookBtn.addEventListener("click", async () => { bookBtn.disabled=true;bookBtn.innerText="Booking..."; await apiBookAppointment({...state}); state.step = 4; render(); }); step.appendChild(bookBtn); addDebug(step); container.appendChild(step); }; /* ----------------- STEP 4: CONFIRMATION ----------------- */ const renderConfirmation = () => { const step = createEl("div", {className:"step",style:"align-items:center"}); step.appendChild(createEl("h2", {innerText:"All Set!"})); const msg = `Hi ${state.firstName}! Your appointment is booked for ${new Date(state.selectedDate).toDateString()} at ${state.selectedTime.split("-")[0]}. See you soon!`; step.appendChild(createEl("p", {innerText: msg, style:"text-align:center;font-size:.95rem"})); step.appendChild(createEl("p", {innerText:"A confirmation SMS has been sent to " + state.phone + ".", style:"font-size:.85rem;color:#555;text-align:center"})); addDebug(step); container.appendChild(step); }; // Initial render render(); </script></body></html>