@tebuto/react-booking-widget
Version:
React Component for the Tebuto Booking Widget
3 lines (2 loc) • 31 kB
JavaScript
"use strict";var e=require("react/jsx-runtime"),t=require("react");function n({config:t}){const n=function(e){const t={"data-therapist-uuid":e.therapistUUID},n=e.backgroundColor??e.theme?.backgroundColor;return n&&(t["data-background-color"]=n),e.categories&&e.categories.length>0&&(t["data-categories"]=e.categories.join(",")),function(e,t){void 0!==t.border&&(e["data-border"]=t.border?"true":"false"),void 0!==t.includeSubusers&&(e["data-include-subusers"]=t.includeSubusers?"true":"false"),void 0!==t.showQuickFilters&&(e["data-show-quick-filters"]=t.showQuickFilters?"true":"false");const n=t.inheritFont??t.theme?.inheritFont;void 0!==n&&(e["data-inherit-font"]=n?"true":"false")}(t,e),e.theme&&function(e,t){t.primaryColor&&(e["data-primary-color"]=t.primaryColor),t.textPrimary&&(e["data-text-primary"]=t.textPrimary),t.textSecondary&&(e["data-text-secondary"]=t.textSecondary),t.borderColor&&(e["data-border-color"]=t.borderColor),t.fontFamily&&(e["data-font-family"]=t.fontFamily)}(t,e.theme),t}(t);return e.jsx("script",{src:"https://widget.tebuto.de/booking.js",...n,"data-testid":"tebuto-booking-widget-script"})}const o="tebuto-fingerprint",r=t.createContext(null);function i({therapistUUID:n,apiBaseUrl:i="https://api.tebuto.de",categories:a,includeSubusers:s,children:l}){const d=function(){const[e,n]=t.useState("");return t.useEffect(()=>{if(void 0===globalThis.window)return;const e=sessionStorage.getItem(o);if(e)n(e);else{const e=crypto.randomUUID();sessionStorage.setItem(o,e),n(e)}},[]),e}(),c=t.useMemo(()=>({therapistUUID:n,apiBaseUrl:i,categories:a,includeSubusers:s,buildUrl:e=>`${i}${e}`,fingerprint:d}),[n,i,a,s,d]);return e.jsx(r.Provider,{value:c,children:l})}function a(){const e=t.useContext(r);if(!e)throw new Error("useTebutoContext must be used within a TebutoProvider");return e}function s(){const{therapistUUID:e,buildUrl:n}=a(),[o,r]=t.useState({data:null,isLoading:!0,error:null}),i=t.useCallback(async()=>{r(e=>({...e,isLoading:!0,error:null}));try{const t=await fetch(n(`/therapists/uuid/${e}`));if(!t.ok)throw new Error(`Failed to fetch therapist: ${t.statusText}`);const o=await t.json();r({data:o,isLoading:!1,error:null})}catch(e){const t=e instanceof Error?e:new Error("Unknown error occurred");r({data:null,isLoading:!1,error:t})}},[e,n]);return t.useEffect(()=>{i().catch(()=>{})},[i]),{...o,refetch:i}}function l(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}function d(e){const t=new Date(e.start),n=new Date(e.end),o=new Date,r=new Date(o.getFullYear(),o.getMonth(),o.getDate()),i=new Date(t.getFullYear(),t.getMonth(),t.getDate()),a=Math.round((n.getTime()-t.getTime())/6e4);return{...e,dateKey:l(t),timeString:t.toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"}),durationMinutes:a,formattedPrice:new Intl.NumberFormat("de-DE",{style:"currency",currency:"EUR"}).format(Number.parseFloat(e.price)),isToday:i.getTime()===r.getTime(),isPast:t<o}}function c(e={}){const{autoFetch:n=!0,categories:o}=e,{therapistUUID:r,buildUrl:i,categories:s,includeSubusers:c,fingerprint:g}=a(),[u,b]=t.useState({data:null,isLoading:n,error:null}),p=o??s,m=p?.join(",")??"",h=t.useCallback(async()=>{if(g){b(e=>({...e,isLoading:!0,error:null}));try{const e=new URL(i(`/events/${r}`));e.searchParams.set("fingerprint",g),m&&e.searchParams.set("categories",m),c&&e.searchParams.set("includeSubusers","true");const t=await fetch(e.toString());if(!t.ok)throw new Error(`Failed to fetch slots: ${t.statusText}`);const n=await t.json(),o=Array.isArray(n)?n:n.events;b({data:o,isLoading:!1,error:null})}catch(e){const t=e instanceof Error?e:new Error("Unknown error occurred");b({data:null,isLoading:!1,error:t})}}},[r,i,m,c,g]);t.useEffect(()=>{n&&g&&h().catch(()=>{})},[n,h,g]);const x=t.useMemo(()=>{if(!u.data)return{};const e={},t=new Date;for(const n of u.data){const o=new Date(n.start);if(o<=t)continue;const r=l(o);e[r]||(e[r]=[]),e[r].push(n)}for(const t in e)e[t].sort((e,t)=>new Date(e.start).getTime()-new Date(t.start).getTime());return e},[u.data]),f=t.useMemo(()=>Object.keys(x).map(e=>{const[t,n,o]=e.split("-").map(Number);return new Date(t,n-1,o)}).sort((e,t)=>e.getTime()-t.getTime()),[x]),k=t.useCallback(e=>{const t=l(e);return(x[t]??[]).map(d)},[x]),y=t.useMemo(()=>{if(!u.data)return[];const e=new Map;for(const t of u.data)e.has(t.eventCategoryId)||e.set(t.eventCategoryId,{id:t.eventCategoryId,name:t.title,color:t.color});return Array.from(e.values())},[u.data]);return{...u,refetch:h,slotsByDate:x,availableDates:f,getSlotsForDate:k,categories:y,totalSlots:u.data?.length??0}}function g(){const{therapistUUID:e,buildUrl:n,fingerprint:o}=a(),[r,i]=t.useState({claimedSlot:null,claimResponse:null,isLoading:!1,error:null}),s=t.useRef(null),l=t.useCallback(async()=>{if(r.claimedSlot&&s.current)try{await fetch(n(`/events/${e}/unclaim`),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fingerprint:o,start:r.claimedSlot.start,end:r.claimedSlot.end,eventRuleId:r.claimedSlot.eventRuleId})})}catch{}finally{s.current=null,i(e=>({...e,claimedSlot:null,claimResponse:null}))}else i(e=>({...e,claimedSlot:null,claimResponse:null}))},[r.claimedSlot,e,n,o]),d=t.useCallback(async t=>{const a=`${t.start}-${t.eventRuleId}`;if(s.current===a&&r.claimResponse)return r.claimResponse;s.current&&s.current!==a&&await l(),i(e=>({...e,isLoading:!0,error:null}));try{const r=await fetch(n(`/events/${e}/claim`),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fingerprint:o,start:t.start,end:t.end,eventRuleId:t.eventRuleId})});if(!r.ok)throw new Error(`Failed to claim slot: ${r.statusText}`);const l=await r.json();return l.isAvailable?(s.current=a,i({claimedSlot:t,claimResponse:l,isLoading:!1,error:null}),l):(i(e=>({...e,isLoading:!1,error:new Error("This time slot is no longer available")})),null)}catch(e){const t=e instanceof Error?e:new Error("Unknown error occurred");return i(e=>({...e,isLoading:!1,error:t})),null}},[e,n,l,r.claimResponse,o]),c=t.useCallback(e=>!!r.claimedSlot&&(r.claimedSlot.start===e.start&&r.claimedSlot.eventRuleId===e.eventRuleId),[r.claimedSlot]),g=t.useCallback(()=>{i(e=>({...e,error:null}))},[]);return{...r,claim:d,unclaim:l,isClaimed:c,clearError:g}}function u(){const{therapistUUID:e,buildUrl:n,fingerprint:o}=a(),[r,i]=t.useState({isLoading:!1,error:null,isSuccess:!1});return{...r,book:t.useCallback(async t=>{const{slot:r,client:a,locationSelection:s}=t;i({isLoading:!0,error:null,isSuccess:!1});try{const t=await fetch(n(`/events/${e}/book`),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fingerprint:o,start:r.start,end:r.end,eventRuleId:r.eventRuleId,locationSelection:s??r.location,...a})});if(!t.ok)throw new Error(`Booking failed: ${t.statusText}`);return i({isLoading:!1,error:null,isSuccess:!0}),!0}catch(e){const t=e instanceof Error?e:new Error("Unknown error occurred");return i({isLoading:!1,error:t,isSuccess:!1}),!1}},[e,n,o]),reset:t.useCallback(()=>{i({isLoading:!1,error:null,isSuccess:!1})},[])}}function b(e={}){const{onBookingComplete:n,onError:o}=e,[r,i]=t.useState("loading"),[a,l]=t.useState(null),[d,b]=t.useState(null),[p,m]=t.useState(null),h=s(),x=c({categories:e.categories}),f=g(),k=u();t.useMemo(()=>{"loading"!==r||h.isLoading||x.isLoading||(h.error||x.error?i("error"):i("date-selection"))},[r,h.isLoading,x.isLoading,h.error,x.error]);const y=t.useMemo(()=>a?x.getSlotsForDate(a):[],[a,x]),v=t.useCallback(e=>{l(e),b(null),m(null),e&&i("time-selection")},[l,b,m,i]),j=t.useCallback(async e=>{if(!e)return await f.unclaim(),b(null),m(null),!0;return await f.claim(e)?(b(e),"not-fixed"!==e.location&&m(e.location),i("booking-form"),!0):(f.error&&o?.(f.error),!1)},[f,o]),S=t.useCallback(async e=>{if(!d)return!1;return await k.book({slot:d,client:e,locationSelection:p??d.location})?(n?.(),i("confirmation"),!0):(k.error&&o?.(k.error),!1)},[d,p,k,n,o]),w=t.useCallback(()=>{i("loading"),l(null),b(null),m(null),k.reset(),f.unclaim(),x.refetch()},[k,f,x]),D=t.useCallback(e=>{i(e)},[]),C=t.useCallback(e=>{m(e)},[]),I=h.isLoading||x.isLoading||f.isLoading||k.isLoading,T=h.error??x.error??f.error??k.error;return{step:r,goToStep:D,therapist:h,slots:x,selectedDate:a,selectDate:v,selectedDateSlots:y,selectedSlot:d,selectSlot:j,selectedLocation:p,setLocation:C,claim:f,booking:k,submitBooking:S,reset:w,isLoading:I,error:T}}const p={"--booking-bg":"#0f0f10","--booking-surface":"#18181b","--booking-surface-elevated":"#1f1f23","--booking-surface-hover":"#27272a","--booking-border":"#2d2d30","--booking-border-subtle":"#232326","--booking-text":"#fafafa","--booking-text-secondary":"#d4d4d8","--booking-text-muted":"#a1a1aa","--booking-text-subtle":"#71717a","--booking-accent":"#10b981","--booking-accent-hover":"#34d399","--booking-accent-muted":"rgba(16, 185, 129, 0.12)","--booking-accent-glow":"rgba(16, 185, 129, 0.25)","--booking-error":"#ef4444","--booking-error-bg":"rgba(239, 68, 68, 0.1)","--booking-radius":"10px","--booking-radius-sm":"6px","--booking-radius-lg":"14px","--booking-shadow":"0 4px 24px rgba(0, 0, 0, 0.4)","--booking-transition":"0.15s ease","--booking-font":'"Inter", -apple-system, BlinkMacSystemFont, sans-serif'};function m(){return e.jsx("div",{style:{width:32,height:32,border:"2px solid var(--booking-border)",borderTopColor:"var(--booking-accent)",borderRadius:"50%",animation:"spin 0.6s linear infinite"}})}function h(){return e.jsxs("div",{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:80,gap:16},children:[e.jsx(m,{}),e.jsx("span",{style:{color:"var(--booking-text-muted)",fontSize:14},children:"Verfügbare Termine werden geladen..."})]})}function x({current:t,total:n}){return e.jsx("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",gap:8,marginBottom:32},children:Array.from({length:n},(n,o)=>e.jsx("div",{style:{display:"flex",alignItems:"center",gap:8},children:e.jsx("div",{style:{width:o===t?28:10,height:10,borderRadius:5,background:o<=t?"var(--booking-accent)":"var(--booking-border)",transition:"all 0.2s ease",boxShadow:o===t?"0 0 12px var(--booking-accent-glow)":"none"}})},o))})}function f({availableDates:n,selectedDate:o,onSelectDate:r}){const[i,a]=t.useState(()=>n.length>0?new Date(n[0]):new Date),s=t.useMemo(()=>new Set(n.map(e=>e.toDateString())),[n]),{year:l,month:d,weeks:c}=t.useMemo(()=>{const e=i.getFullYear(),t=i.getMonth(),n=new Date(e,t,1),o=new Date(e,t+1,0).getDate(),r=(n.getDay()+6)%7,a=[];let s=[];for(let e=0;e<r;e++)s.push(null);for(let e=1;e<=o;e++)s.push(e),7===s.length&&(a.push(s),s=[]);if(s.length>0){for(;s.length<7;)s.push(null);a.push(s)}return{year:e,month:t,daysInMonth:o,firstDayOfWeek:r,weeks:a}},[i]),g=e=>{const t=new Date(l,d,e);return s.has(t.toDateString())},u=new Date,b=l>u.getFullYear()||l===u.getFullYear()&&d>u.getMonth();return e.jsxs("div",{children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:20,padding:"0 4px"},children:[e.jsx("button",{type:"button",onClick:()=>{a(new Date(l,d-1,1))},disabled:!b,style:{width:36,height:36,border:"1px solid var(--booking-border)",borderRadius:"var(--booking-radius-sm)",background:"var(--booking-surface)",color:b?"var(--booking-text)":"var(--booking-text-subtle)",cursor:b?"pointer":"not-allowed",display:"flex",alignItems:"center",justifyContent:"center",fontSize:16,transition:"var(--booking-transition)",opacity:b?1:.4},children:"‹"}),e.jsxs("span",{style:{fontSize:16,fontWeight:600,color:"var(--booking-text)"},children:[["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"][d]," ",l]}),e.jsx("button",{type:"button",onClick:()=>{a(new Date(l,d+1,1))},style:{width:36,height:36,border:"1px solid var(--booking-border)",borderRadius:"var(--booking-radius-sm)",background:"var(--booking-surface)",color:"var(--booking-text)",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",fontSize:16,transition:"var(--booking-transition)"},children:"›"})]}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(7, 1fr)",gap:4,marginBottom:8},children:["Mo","Di","Mi","Do","Fr","Sa","So"].map(t=>e.jsx("div",{style:{textAlign:"center",fontSize:12,fontWeight:500,color:"var(--booking-text-subtle)",padding:"8px 0",textTransform:"uppercase",letterSpacing:"0.05em"},children:t},t))}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:4},children:c.map(t=>{const n=t.find(e=>null!==e)??0,i=`${l}-${d}-w${n}`;return e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(7, 1fr)",gap:4},children:t.map((t,n)=>{if(null===t)return e.jsx("div",{style:{aspectRatio:"1",minHeight:44}},`${i}-empty-${n}`);const a=g(t),s=(e=>!!o&&new Date(l,d,e).toDateString()===o.toDateString())(t),c=(e=>{const t=new Date;return e===t.getDate()&&d===t.getMonth()&&l===t.getFullYear()})(t);let u="1px solid transparent";s?u="2px solid var(--booking-accent)":c&&(u="1px solid var(--booking-accent)");let b="transparent";s?b="var(--booking-accent)":a&&(b="var(--booking-surface-elevated)");let p="var(--booking-text-subtle)";s?p="#000":a&&(p="var(--booking-text)");let m=400;return s?m=600:a&&(m=500),e.jsxs("button",{type:"button",onClick:()=>(e=>{g(e)&&r(new Date(l,d,e))})(t),disabled:!a,style:{aspectRatio:"1",minHeight:44,border:u,borderRadius:"var(--booking-radius-sm)",background:b,color:p,cursor:a?"pointer":"default",fontSize:14,fontWeight:m,fontFamily:"inherit",display:"flex",alignItems:"center",justifyContent:"center",transition:"var(--booking-transition)",opacity:a?1:.35,position:"relative"},children:[t,a&&!s&&e.jsx("span",{style:{position:"absolute",bottom:6,width:4,height:4,borderRadius:"50%",background:"var(--booking-accent)"}})]},`${l}-${d}-${t}`)})},i)})}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:20,marginTop:20,paddingTop:16,borderTop:"1px solid var(--booking-border-subtle)",fontSize:12,color:"var(--booking-text-muted)"},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx("span",{style:{width:8,height:8,borderRadius:"50%",background:"var(--booking-accent)"}}),e.jsx("span",{children:"Termine verfügbar"})]}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx("span",{style:{width:8,height:8,borderRadius:"50%",background:"var(--booking-text-subtle)",opacity:.35}}),e.jsx("span",{children:"Keine Termine"})]})]})]})}function k({slots:t,selectedSlot:n,onSelectSlot:o,selectedDate:r,onBack:i}){const a=e=>{switch(e){case"virtual":return"🎥";case"onsite":return"📍";case"not-fixed":return"🔄"}},s=e=>{switch(e){case"virtual":return"Online";case"onsite":return"Vor Ort";case"not-fixed":return"Flexibel"}},l=t.reduce((e,t)=>{const n=t.title;return e[n]||(e[n]=[]),e[n].push(t),e},{});return e.jsxs("div",{children:[e.jsxs("div",{style:{marginBottom:24},children:[e.jsx("button",{type:"button",onClick:i,style:{display:"flex",alignItems:"center",gap:6,background:"none",border:"none",color:"var(--booking-text-muted)",fontSize:13,cursor:"pointer",padding:"4px 0",marginBottom:12,fontFamily:"inherit",transition:"var(--booking-transition)"},children:"← Zurück zum Kalender"}),e.jsxs("h3",{style:{fontSize:18,fontWeight:600,color:"var(--booking-text)",margin:0,display:"flex",alignItems:"center",gap:12},children:[e.jsx("span",{style:{width:40,height:40,borderRadius:"var(--booking-radius-sm)",background:"var(--booking-accent-muted)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:18},children:"🕐"}),(d=r,d.toLocaleDateString("de-DE",{weekday:"long",day:"numeric",month:"long",year:"numeric"}))]})]}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:28},children:Object.entries(l).map(([t,r])=>e.jsxs("div",{children:[e.jsxs("h4",{style:{fontSize:14,fontWeight:600,color:"var(--booking-text-secondary)",margin:"0 0 12px 0",display:"flex",alignItems:"center",gap:8},children:[e.jsx("span",{style:{width:3,height:14,borderRadius:2,background:r[0]?.color||"var(--booking-accent)"}}),t]}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(240px, 1fr))",gap:12},children:r.map(t=>{const r=n?.start===t.start&&n?.eventRuleId===t.eventRuleId;return e.jsxs("button",{type:"button",onClick:()=>o(t),style:{padding:"14px 16px",border:r?"2px solid var(--booking-accent)":"1px solid var(--booking-border)",borderRadius:"var(--booking-radius)",background:r?"var(--booking-accent-muted)":"var(--booking-surface-elevated)",cursor:"pointer",textAlign:"left",fontFamily:"inherit",transition:"var(--booking-transition)",display:"flex",alignItems:"center",justifyContent:"space-between",gap:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12},children:[e.jsx("span",{style:{fontSize:17,fontWeight:600,color:"var(--booking-text)",fontVariantNumeric:"tabular-nums"},children:t.timeString}),e.jsxs("span",{style:{fontSize:11,color:"var(--booking-text-subtle)",background:"var(--booking-surface)",padding:"4px 8px",borderRadius:4,display:"inline-flex",alignItems:"center",gap:4,whiteSpace:"nowrap",flexShrink:0},children:[a(t.location)," ",s(t.location)]})]}),e.jsx("span",{style:{fontSize:14,fontWeight:600,color:"var(--booking-accent)"},children:t.formattedPrice})]},`${t.start}-${t.eventRuleId}`)})})]},t))}),0===t.length&&e.jsx("div",{style:{textAlign:"center",padding:40,color:"var(--booking-text-muted)"},children:"Keine Termine an diesem Tag verfügbar."})]});var d}function y({slot:n,showLocationSelector:o,selectedLocation:r,onLocationChange:i,onSubmit:a,onBack:s,isLoading:l,error:d}){const[c,g]=t.useState({firstName:"",lastName:"",email:"",phone:"",notes:""}),u={width:"100%",padding:"14px 16px",border:"1px solid var(--booking-border)",borderRadius:"var(--booking-radius-sm)",background:"var(--booking-bg)",color:"var(--booking-text)",fontSize:15,fontFamily:"inherit",outline:"none",transition:"var(--booking-transition)",boxSizing:"border-box"},b={display:"block",fontSize:13,fontWeight:600,color:"var(--booking-text-secondary)",marginBottom:8,textTransform:"uppercase",letterSpacing:"0.03em"};return e.jsxs("div",{children:[e.jsx("button",{type:"button",onClick:s,style:{display:"inline-flex",alignItems:"center",gap:6,background:"var(--booking-surface-elevated)",border:"1px solid var(--booking-border)",borderRadius:"var(--booking-radius-sm)",color:"var(--booking-text-muted)",fontSize:13,cursor:"pointer",padding:"8px 14px",marginBottom:24,fontFamily:"inherit",transition:"var(--booking-transition)"},children:"← Zurück"}),e.jsxs("div",{style:{background:"linear-gradient(135deg, var(--booking-accent-muted) 0%, var(--booking-surface-elevated) 100%)",borderRadius:"var(--booking-radius-lg)",padding:24,marginBottom:28,border:"1px solid var(--booking-accent)",position:"relative",overflow:"hidden"},children:[e.jsx("div",{style:{position:"absolute",top:0,left:0,width:6,height:"100%",background:n.color||"var(--booking-accent)"}}),e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",flexWrap:"wrap",gap:16},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:20},children:[e.jsx("div",{style:{width:56,height:56,borderRadius:"var(--booking-radius)",background:"var(--booking-surface)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:24,border:"1px solid var(--booking-border)"},children:"📅"}),e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:18,fontWeight:700,color:"var(--booking-text)",marginBottom:6},children:n.title}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:16,fontSize:14,color:"var(--booking-text-muted)"},children:[e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:6},children:[e.jsx("span",{style:{opacity:.7},children:"📆"})," ",(p=n.start,new Date(p).toLocaleDateString("de-DE",{weekday:"short",day:"numeric",month:"short"}))]}),e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:6},children:[e.jsx("span",{style:{opacity:.7},children:"🕐"})," ",n.timeString," Uhr"]}),e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:6},children:[e.jsx("span",{style:{opacity:.7},children:"⏱"})," ",n.durationMinutes," Min."]})]})]})]}),e.jsxs("div",{style:{textAlign:"right"},children:[e.jsx("div",{style:{fontSize:12,color:"var(--booking-text-muted)",marginBottom:2},children:"Preis"}),e.jsx("div",{style:{fontSize:28,fontWeight:700,color:"var(--booking-accent)"},children:n.formattedPrice})]})]})]}),o&&e.jsxs("div",{style:{background:"var(--booking-surface-elevated)",borderRadius:"var(--booking-radius)",padding:20,marginBottom:28,border:"1px solid var(--booking-border)"},children:[e.jsx("div",{style:{fontSize:14,fontWeight:600,color:"var(--booking-text)",marginBottom:14},children:"Wo soll der Termin stattfinden?"}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12},children:["virtual","onsite"].map(t=>e.jsxs("button",{type:"button",onClick:()=>i(t),style:{padding:"16px 20px",border:r===t?"2px solid var(--booking-accent)":"1px solid var(--booking-border)",borderRadius:"var(--booking-radius)",background:r===t?"var(--booking-accent-muted)":"var(--booking-surface)",color:"var(--booking-text)",cursor:"pointer",fontFamily:"inherit",fontSize:15,fontWeight:600,display:"flex",alignItems:"center",justifyContent:"center",gap:10,transition:"var(--booking-transition)"},children:[e.jsx("span",{style:{fontSize:20},children:"virtual"===t?"🎥":"📍"}),"virtual"===t?"Online-Termin":"Vor Ort"]},t))})]}),e.jsxs("div",{style:{background:"var(--booking-surface-elevated)",borderRadius:"var(--booking-radius-lg)",padding:28,border:"1px solid var(--booking-border)"},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:14,marginBottom:24,paddingBottom:20,borderBottom:"1px solid var(--booking-border)"},children:[e.jsx("div",{style:{width:44,height:44,borderRadius:"var(--booking-radius-sm)",background:"var(--booking-accent-muted)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:20},children:"👤"}),e.jsxs("div",{children:[e.jsx("h3",{style:{fontSize:17,fontWeight:700,color:"var(--booking-text)",margin:0},children:"Ihre Kontaktdaten"}),e.jsx("p",{style:{fontSize:13,color:"var(--booking-text-muted)",margin:"4px 0 0 0"},children:"Wir benötigen diese Angaben für Ihre Terminbestätigung"})]})]}),d&&e.jsxs("div",{style:{background:"var(--booking-error-bg)",border:"1px solid var(--booking-error)",borderRadius:"var(--booking-radius-sm)",padding:"14px 16px",marginBottom:20,color:"var(--booking-error)",fontSize:14,display:"flex",alignItems:"center",gap:10},children:[e.jsx("span",{children:"⚠️"})," ",d.message]}),e.jsxs("form",{onSubmit:e=>{e.preventDefault(),a({firstName:c.firstName,lastName:c.lastName,email:c.email,phone:c.phone||void 0,notes:c.notes||void 0})},style:{display:"flex",flexDirection:"column",gap:20},children:[e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:16},children:[e.jsxs("div",{children:[e.jsx("label",{htmlFor:"booking-firstName",style:b,children:"Vorname"}),e.jsx("input",{id:"booking-firstName",type:"text",required:!0,value:c.firstName,onChange:e=>g(t=>({...t,firstName:e.target.value})),style:u,placeholder:"Max"})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"booking-lastName",style:b,children:"Nachname"}),e.jsx("input",{id:"booking-lastName",type:"text",required:!0,value:c.lastName,onChange:e=>g(t=>({...t,lastName:e.target.value})),style:u,placeholder:"Mustermann"})]})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"booking-email",style:b,children:"E-Mail-Adresse"}),e.jsx("input",{id:"booking-email",type:"email",required:!0,value:c.email,onChange:e=>g(t=>({...t,email:e.target.value})),style:u,placeholder:"max@beispiel.de"})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"booking-phone",style:{...b,color:"var(--booking-text-muted)"},children:["Telefonnummer ",e.jsx("span",{style:{fontWeight:400,textTransform:"none"},children:"(optional)"})]}),e.jsx("input",{id:"booking-phone",type:"tel",value:c.phone,onChange:e=>g(t=>({...t,phone:e.target.value})),style:u,placeholder:"+49 123 456789"})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"booking-notes",style:{...b,color:"var(--booking-text-muted)"},children:["Nachricht ",e.jsx("span",{style:{fontWeight:400,textTransform:"none"},children:"(optional)"})]}),e.jsx("textarea",{id:"booking-notes",value:c.notes,onChange:e=>g(t=>({...t,notes:e.target.value})),style:{...u,minHeight:100,resize:"vertical"},placeholder:"Gibt es etwas, das wir vorab wissen sollten?"})]}),e.jsx("button",{type:"submit",disabled:l||o&&!r,style:{marginTop:8,padding:"18px 32px",border:"none",borderRadius:"var(--booking-radius)",background:"var(--booking-accent)",color:"#000",fontSize:16,fontWeight:700,cursor:l||o&&!r?"not-allowed":"pointer",fontFamily:"inherit",transition:"var(--booking-transition)",opacity:l||o&&!r?.5:1,display:"flex",alignItems:"center",justifyContent:"center",gap:10,boxShadow:l||o&&!r?"none":"0 4px 14px var(--booking-accent-glow)"},children:l?e.jsxs(e.Fragment,{children:[e.jsx(m,{})," Wird gebucht..."]}):e.jsx(e.Fragment,{children:"Termin verbindlich buchen"})})]})]})]});var p}function v({slot:t,onReset:n}){return e.jsxs("div",{style:{textAlign:"center",padding:"48px 24px"},children:[e.jsx("div",{style:{width:72,height:72,borderRadius:"50%",background:"var(--booking-accent-muted)",display:"flex",alignItems:"center",justifyContent:"center",margin:"0 auto 24px",fontSize:32,border:"2px solid var(--booking-accent)"},children:"✓"}),e.jsx("h2",{style:{fontSize:24,fontWeight:700,color:"var(--booking-text)",margin:"0 0 12px 0"},children:"Terminanfrage eingegangen!"}),e.jsxs("p",{style:{fontSize:15,color:"var(--booking-text-muted)",maxWidth:400,margin:"0 auto 32px",lineHeight:1.6},children:["Ihre Anfrage für ",e.jsxs("strong",{style:{color:"var(--booking-text)"},children:['"',t.title,'"']})," am"," ",e.jsx("strong",{style:{color:"var(--booking-text)"},children:(o=t.start,new Date(o).toLocaleDateString("de-DE",{weekday:"long",day:"numeric",month:"long",year:"numeric"}))})," um"," ",e.jsxs("strong",{style:{color:"var(--booking-text)"},children:[t.timeString," Uhr"]})," ist eingegangen. Bitte bestätigen Sie den Termin über den Link in der E-Mail, die wir Ihnen gerade gesendet haben."]}),e.jsx("div",{style:{display:"flex",gap:12,justifyContent:"center",flexWrap:"wrap"},children:e.jsx("button",{type:"button",onClick:n,style:{padding:"12px 20px",border:"1px solid var(--booking-border)",borderRadius:"var(--booking-radius-sm)",background:"transparent",color:"var(--booking-text)",fontSize:14,fontWeight:500,cursor:"pointer",fontFamily:"inherit"},children:"Weiteren Termin buchen"})})]});var o}function j(){const{step:t,goToStep:n,therapist:o,slots:r,selectedDate:i,selectDate:a,selectedDateSlots:s,selectedSlot:l,selectSlot:d,selectedLocation:c,setLocation:g,submitBooking:u,reset:m,isLoading:j,error:S}=b(),w=l?{...l,dateKey:new Date(l.start).toISOString().split("T")[0],timeString:new Date(l.start).toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"}),durationMinutes:Math.round((new Date(l.end).getTime()-new Date(l.start).getTime())/6e4),formattedPrice:new Intl.NumberFormat("de-DE",{style:"currency",currency:"EUR"}).format(Number.parseFloat(l.price)),isToday:!1,isPast:!1}:null;return e.jsxs("div",{style:{...p,fontFamily:"var(--booking-font)",background:"var(--booking-bg)",color:"var(--booking-text)",minHeight:"100vh",padding:"40px 24px"},children:[e.jsx("style",{children:"\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\n@keyframes spin { to { transform: rotate(360deg); } }\n@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }\n"}),e.jsxs("div",{style:{maxWidth:880,margin:"0 auto"},children:[e.jsxs("header",{style:{textAlign:"center",marginBottom:40},children:[e.jsxs("h1",{style:{fontSize:28,fontWeight:700,margin:"0 0 8px 0",color:"var(--booking-text)"},children:["Termin buchen",o.data?` bei ${o.data.name}`:""]}),e.jsx("p",{style:{fontSize:15,color:"var(--booking-text-muted)",margin:0},children:"Wählen Sie einen passenden Termin für Ihr Anliegen"})]}),"loading"!==t&&"error"!==t&&"confirmation"!==t&&e.jsx(x,{current:(()=>{switch(t){case"date-selection":default:return 0;case"time-selection":return 1;case"booking-form":return 2;case"confirmation":return 3}})(),total:3}),e.jsx("div",{style:{background:"var(--booking-surface)",borderRadius:"var(--booking-radius-lg)",border:"1px solid var(--booking-border)",boxShadow:"var(--booking-shadow)",overflow:"hidden",animation:"fadeIn 0.3s ease"},children:e.jsxs("div",{style:{padding:28},children:["loading"===t&&e.jsx(h,{}),"error"===t&&e.jsxs("div",{style:{textAlign:"center",padding:40},children:[e.jsx("div",{style:{background:"var(--booking-error-bg)",border:"1px solid var(--booking-error)",borderRadius:"var(--booking-radius)",padding:16,marginBottom:20,color:"var(--booking-error)"},children:S?.message??"Ein Fehler ist aufgetreten"}),e.jsx("button",{type:"button",onClick:m,style:{padding:"12px 24px",border:"none",borderRadius:"var(--booking-radius-sm)",background:"var(--booking-accent)",color:"#000",fontSize:14,fontWeight:600,cursor:"pointer",fontFamily:"inherit"},children:"Erneut versuchen"})]}),"date-selection"===t&&e.jsxs("div",{children:[e.jsxs("h3",{style:{fontSize:18,fontWeight:600,color:"var(--booking-text)",margin:"0 0 20px 0",display:"flex",alignItems:"center",gap:12},children:[e.jsx("span",{style:{width:40,height:40,borderRadius:"var(--booking-radius-sm)",background:"var(--booking-accent-muted)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:18},children:"📅"}),e.jsx("span",{children:"Datum wählen"})]}),e.jsx(f,{availableDates:r.availableDates,selectedDate:i,onSelectDate:a})]}),"time-selection"===t&&i&&e.jsx(k,{slots:s,selectedSlot:l?{...l,dateKey:"",timeString:"",durationMinutes:0,formattedPrice:"",isToday:!1,isPast:!1}:null,onSelectSlot:async e=>{await d(e)},selectedDate:i,onBack:()=>n("date-selection")}),"booking-form"===t&&w&&e.jsx(y,{slot:w,showLocationSelector:"not-fixed"===w.location,selectedLocation:c,onLocationChange:g,onSubmit:u,onBack:()=>n("time-selection"),isLoading:j,error:S}),"confirmation"===t&&w&&e.jsx(v,{slot:w,onReset:m})]})})]})]})}exports.CustomBookingExample=function({therapistUUID:t,categories:n}){return e.jsx(i,{therapistUUID:t,categories:n,children:e.jsx(j,{})})},exports.TebutoBookingWidget=function({noScriptText:t="Widget konnte nicht geladen werden. Möglicherweise ist Skripting im Browser deaktiviert.",...o}){return e.jsxs("div",{id:"tebuto-booking-widget","data-testid":"tebuto-booking-widget-container",children:[e.jsx(n,{config:o}),e.jsx("noscript",{"data-testid":"tebuto-booking-widget-noscript",children:t})]})},exports.TebutoProvider=i,exports.useAvailableSlots=c,exports.useBookAppointment=u,exports.useBookingFlow=b,exports.useClaimSlot=g,exports.useTebutoContext=a,exports.useTherapist=s;
//# sourceMappingURL=index.js.map