@tebuto/react-booking-widget 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ import type { StorybookConfig } from '@storybook/react-vite'
2
+
3
+ const config: StorybookConfig = {
4
+ stories: ['../src/**/*.stories.@(ts|tsx)'],
5
+ framework: {
6
+ name: '@storybook/react-vite',
7
+ options: {}
8
+ },
9
+ typescript: {
10
+ reactDocgen: 'react-docgen-typescript'
11
+ },
12
+ staticDirs: ['../public']
13
+ }
14
+
15
+ export default config
@@ -0,0 +1,39 @@
1
+ import type { Preview } from '@storybook/react'
2
+
3
+ let mswInitialized = false
4
+
5
+ // Initialize MSW
6
+ async function initMocks() {
7
+ if (typeof window !== 'undefined' && !mswInitialized) {
8
+ const { worker } = await import('../src/mocks/browser')
9
+ await worker.start({
10
+ onUnhandledRequest: 'bypass',
11
+ serviceWorker: {
12
+ url: '/mockServiceWorker.js'
13
+ }
14
+ })
15
+ mswInitialized = true
16
+ }
17
+ }
18
+
19
+ const preview: Preview = {
20
+ parameters: {
21
+ layout: 'padded',
22
+ backgrounds: {
23
+ default: 'light gray',
24
+ values: [
25
+ { name: 'light gray', value: '#f3f4f6' },
26
+ { name: 'white', value: '#ffffff' },
27
+ { name: 'dark', value: '#1f2937' }
28
+ ]
29
+ }
30
+ },
31
+ loaders: [
32
+ async () => {
33
+ await initMocks()
34
+ return {}
35
+ }
36
+ ]
37
+ }
38
+
39
+ export default preview
package/README.md CHANGED
@@ -1,62 +1,340 @@
1
-
2
1
  <div align="center">
3
2
  <img alt="Tebuto" src="https://tebuto.de/assets/logo.svg" width="400" />
4
3
  </div>
5
4
 
6
- <p align="center">A <a href="https://react.dev" target="_blank">React</a> component for integrating the <a href="https://tebuto.de" target="_blank">Tebuto</a> booking widget into your own website.
7
- <p align="center">
5
+ <p align="center">A <a href="https://react.dev" target="_blank">React</a> library for integrating <a href="https://tebuto.de" target="_blank">Tebuto</a> appointment booking into your website.</p>
8
6
 
9
7
  <div align="center">
10
- <a href="https://www.npmjs.com/package/jest"><img alt="NPM Version" src="https://img.shields.io/npm/v/%40tebuto%2Freact-booking-widget"></a>
11
- <a href="https://github.com/tebuto/react-gdpr-cookie-consent/blob/main/LICENSE"> <img alt="@tebuto/react-booking-widget is released under a MIT licence" src="https://img.shields.io/npm/l/%40tebuto%2Freact-booking-widget"></a>
12
- <a href="https://github.com/tebuto/react-gdpr-cookie-consent/actions/workflows/branch.yaml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/tebuto/react-booking-widget/.github%2Fworkflows%2Fbranch.yaml?label=CI&logo=GitHub"></a>
8
+ <a href="https://www.npmjs.com/package/@tebuto/react-booking-widget"><img alt="NPM Version" src="https://img.shields.io/npm/v/%40tebuto%2Freact-booking-widget"></a>
9
+ <a href="https://github.com/tebuto/react-booking-widget/blob/main/LICENSE"><img alt="MIT License" src="https://img.shields.io/npm/l/%40tebuto%2Freact-booking-widget"></a>
10
+ <a href="https://github.com/tebuto/react-booking-widget/actions/workflows/branch.yaml"><img alt="CI Status" src="https://img.shields.io/github/actions/workflow/status/tebuto/react-booking-widget/.github%2Fworkflows%2Fbranch.yaml?label=CI&logo=GitHub"></a>
13
11
  </div>
12
+
14
13
  <hr />
15
14
 
16
15
  ## Table of Contents <!-- omit in toc -->
17
16
 
18
- - [Installing](#installing)
19
- - [Getting Started](#getting-started)
20
- - [API Reference](#api-reference)
17
+ - [Features](#features)
18
+ - [Installation](#installation)
19
+ - [Quick Start](#quick-start)
20
+ - [Widget Configuration](#widget-configuration)
21
+ - [Props Reference](#props-reference)
22
+ - [Theme Configuration](#theme-configuration)
23
+ - [Building Custom Booking UIs](#building-custom-booking-uis)
24
+ - [TebutoProvider](#tebutoprovider)
25
+ - [useBookingFlow Hook](#usebookingflow-hook)
26
+ - [Individual Hooks](#individual-hooks)
27
+ - [useTherapist](#usetherapist)
28
+ - [useAvailableSlots](#useavailableslots)
29
+ - [useClaimSlot](#useclaimslot)
30
+ - [useBookAppointment](#usebookappointment)
31
+ - [Custom Booking Example](#custom-booking-example)
32
+ - [API Types](#api-types)
21
33
  - [License](#license)
22
34
 
23
- ## Installing
35
+ ## Features
24
36
 
25
- This is a [Node.js](https://nodejs.org/en/) module available through the
26
- [npm registry](https://www.npmjs.com/package/@tebuto/react-booking-widget).
37
+ - **Drop-in Widget** - Embed the Tebuto booking widget with a single component
38
+ - **Custom Booking UIs** - Build your own booking interface with powerful React hooks
39
+ - **Full TypeScript Support** - Complete type definitions for all APIs
40
+ - **Theming** - Customize colors, fonts, and styles to match your brand
41
+ - **React 19 Compatible** - Built for the latest React version
27
42
 
28
- Installation is done using the`npm install` command:
43
+ ## Installation
29
44
 
30
- ``` bash
45
+ ```bash
46
+ # npm
31
47
  npm install @tebuto/react-booking-widget
48
+
49
+ # pnpm
50
+ pnpm add @tebuto/react-booking-widget
51
+
52
+ # yarn
53
+ yarn add @tebuto/react-booking-widget
54
+ ```
55
+
56
+ **Requirements:** React 19.0.0 or higher
57
+
58
+ ## Quick Start
59
+
60
+ The simplest way to add Tebuto booking to your site:
61
+
62
+ ```tsx
63
+ import { TebutoBookingWidget } from "@tebuto/react-booking-widget";
64
+
65
+ function BookingPage() {
66
+ return <TebutoBookingWidget therapistUUID="your-therapist-uuid" />;
67
+ }
68
+ ```
69
+
70
+ > **Note:** You can obtain the therapist UUID from the [appointment settings](https://app.tebuto.de/einstellungen/termine). In the embedding section, click on the HTML button and use the value from the `data-therapist-uuid` attribute.
71
+
72
+ ## Widget Configuration
73
+
74
+ ### Props Reference
75
+
76
+ | Prop | Type | Required | Default | Description |
77
+ | ------------------ | ------------------- | -------- | --------------- | ------------------------------------------- |
78
+ | `therapistUUID` | `string` | Yes | - | Unique identifier for the therapist |
79
+ | `backgroundColor` | `string` | No | `transparent` | Background color (hex, rgb, etc.) |
80
+ | `border` | `boolean` | No | `true` | Show border around the widget |
81
+ | `categories` | `number[]` | No | `[]` | Filter appointments by category IDs |
82
+ | `includeSubusers` | `boolean` | No | `false` | Include subuser appointments |
83
+ | `showQuickFilters` | `boolean` | No | `false` | Show quick filter buttons for time slots |
84
+ | `inheritFont` | `boolean` | No | `false` | Use parent page font instead of widget font |
85
+ | `theme` | `TebutoWidgetTheme` | No | - | Theme customization object |
86
+ | `noScriptText` | `string` | No | Default message | Text shown when JavaScript is disabled |
87
+
88
+ ### Theme Configuration
89
+
90
+ Customize the widget appearance with the `theme` prop:
91
+
92
+ ```tsx
93
+ import { TebutoBookingWidget } from "@tebuto/react-booking-widget";
94
+
95
+ function BookingPage() {
96
+ return (
97
+ <TebutoBookingWidget
98
+ therapistUUID="your-uuid"
99
+ theme={{
100
+ primaryColor: "#10b981",
101
+ backgroundColor: "#0f0f10",
102
+ textPrimary: "#fafafa",
103
+ textSecondary: "#a1a1aa",
104
+ borderColor: "#2d2d30",
105
+ fontFamily: '"Inter", sans-serif',
106
+ inheritFont: false,
107
+ }}
108
+ />
109
+ );
110
+ }
111
+ ```
112
+
113
+ | Theme Property | Type | Description |
114
+ | ----------------- | --------- | ------------------------------------------- |
115
+ | `primaryColor` | `string` | Primary brand color for buttons, highlights |
116
+ | `backgroundColor` | `string` | Main widget background |
117
+ | `textPrimary` | `string` | Primary text color |
118
+ | `textSecondary` | `string` | Secondary/muted text color |
119
+ | `borderColor` | `string` | Border color |
120
+ | `fontFamily` | `string` | Font family for the widget |
121
+ | `inheritFont` | `boolean` | Inherit font from parent page |
122
+
123
+ ## Building Custom Booking UIs
124
+
125
+ For complete control over your booking interface, use the provided hooks to build a custom implementation.
126
+
127
+ ### TebutoProvider
128
+
129
+ Wrap your booking components with `TebutoProvider` to share configuration:
130
+
131
+ ```tsx
132
+ import { TebutoProvider } from "@tebuto/react-booking-widget";
133
+
134
+ function App() {
135
+ return (
136
+ <TebutoProvider
137
+ therapistUUID="your-uuid"
138
+ categories={[1, 2, 3]}
139
+ includeSubusers={false}
140
+ >
141
+ <YourCustomBookingUI />
142
+ </TebutoProvider>
143
+ );
144
+ }
145
+ ```
146
+
147
+ | Prop | Type | Required | Description |
148
+ | ----------------- | ---------- | -------- | -------------------- |
149
+ | `therapistUUID` | `string` | Yes | Therapist identifier |
150
+ | `apiBaseUrl` | `string` | No | Custom API base URL |
151
+ | `categories` | `number[]` | No | Category filter |
152
+ | `includeSubusers` | `boolean` | No | Include subusers |
153
+
154
+ ### useBookingFlow Hook
155
+
156
+ The `useBookingFlow` hook provides complete booking flow orchestration:
157
+
158
+ ```tsx
159
+ import { TebutoProvider, useBookingFlow } from "@tebuto/react-booking-widget";
160
+
161
+ function BookingUI() {
162
+ const {
163
+ step,
164
+ goToStep,
165
+ therapist,
166
+ slots,
167
+ selectedDate,
168
+ selectDate,
169
+ selectedDateSlots,
170
+ selectedSlot,
171
+ selectSlot,
172
+ selectedLocation,
173
+ setLocation,
174
+ submitBooking,
175
+ booking,
176
+ reset,
177
+ isLoading,
178
+ error,
179
+ } = useBookingFlow({
180
+ onBookingComplete: (booking) => console.log("Booked!", booking),
181
+ onError: (error) => console.error("Error:", error),
182
+ });
183
+
184
+ switch (step) {
185
+ case "loading":
186
+ return <LoadingSpinner />;
187
+
188
+ case "date-selection":
189
+ return (
190
+ <DatePicker
191
+ availableDates={slots.availableDates}
192
+ onSelect={selectDate}
193
+ />
194
+ );
195
+
196
+ case "time-selection":
197
+ return <TimeSlotPicker slots={selectedDateSlots} onSelect={selectSlot} />;
198
+
199
+ case "booking-form":
200
+ return <BookingForm onSubmit={submitBooking} isLoading={isLoading} />;
201
+
202
+ case "confirmation":
203
+ return (
204
+ <Confirmation
205
+ booking={booking.booking}
206
+ onDownloadCalendar={booking.downloadCalendar}
207
+ onReset={reset}
208
+ />
209
+ );
210
+
211
+ case "error":
212
+ return <ErrorDisplay error={error} onRetry={reset} />;
213
+ }
214
+ }
215
+
216
+ function App() {
217
+ return (
218
+ <TebutoProvider therapistUUID="your-uuid">
219
+ <BookingUI />
220
+ </TebutoProvider>
221
+ );
222
+ }
32
223
  ```
33
224
 
34
- ## Getting Started
225
+ ### Individual Hooks
226
+
227
+ For fine-grained control, use the individual hooks:
35
228
 
36
- You need an existing react project to use the Tebuto booking component, if you don't have one follow this [Guide](https://react.dev/learn/start-a-new-react-project) to create one.
229
+ #### useTherapist
37
230
 
38
- Here's a basic example how the widget can be used:
231
+ Fetch therapist information:
39
232
 
40
- ```typescript
41
- import { TebutoBookingWidget } from '@tebuto/react-booking-widget'
233
+ ```tsx
234
+ const { data, isLoading, error, refetch } = useTherapist();
235
+ // data: { name, firstName, lastName, address, showWatermark }
236
+ ```
237
+
238
+ #### useAvailableSlots
42
239
 
43
- const YourComponent = () => {
44
- return <TebutoBookingWidget therapistUUID="<your-therapist-uuid>" />
240
+ Fetch and manage available time slots:
241
+
242
+ ```tsx
243
+ const {
244
+ slots, // All available slots
245
+ slotsByDate, // Slots grouped by date
246
+ availableDates, // Array of dates with availability
247
+ getSlotsForDate, // Get slots for a specific date
248
+ isLoading,
249
+ error,
250
+ refetch,
251
+ } = useAvailableSlots({ categories: [1, 2] });
252
+ ```
253
+
254
+ #### useClaimSlot
255
+
256
+ Claim (reserve) a time slot before booking:
257
+
258
+ ```tsx
259
+ const {
260
+ claim, // Claim a slot
261
+ unclaim, // Release claimed slot
262
+ claimData, // Claim response data
263
+ isLoading,
264
+ error,
265
+ } = useClaimSlot();
266
+
267
+ // Claim a slot
268
+ const response = await claim(selectedSlot);
269
+ // response: { isAvailable, requirePhoneNumber, requireAddress }
270
+ ```
271
+
272
+ #### useBookAppointment
273
+
274
+ Complete the booking:
275
+
276
+ ```tsx
277
+ const {
278
+ book, // Submit booking
279
+ booking, // Booking response
280
+ downloadCalendar, // Download ICS file
281
+ reset, // Reset state
282
+ isLoading,
283
+ error,
284
+ } = useBookAppointment();
285
+
286
+ // Book appointment
287
+ const result = await book({
288
+ slot: selectedSlot,
289
+ client: {
290
+ firstName: "Max",
291
+ lastName: "Mustermann",
292
+ email: "max@example.com",
293
+ phone: "+49123456789", // optional
294
+ notes: "First appointment", // optional
295
+ },
296
+ locationSelection: "onsite", // 'virtual' | 'onsite' | 'not-fixed'
297
+ });
298
+ ```
299
+
300
+ ### Custom Booking Example
301
+
302
+ The library includes a full example implementation:
303
+
304
+ ```tsx
305
+ import { CustomBookingExample } from "@tebuto/react-booking-widget";
306
+
307
+ function BookingPage() {
308
+ return <CustomBookingExample therapistUUID="your-uuid" categories={[1, 2]} />;
45
309
  }
46
310
  ```
47
311
 
48
- You can obtain the therapist UUID from the [appointment settings](https://app.tebuto.de/einstellungen/termine). In the embedding section, click on the HTML button and use the value from the `data-therapist-uuid` attribute.
312
+ ## API Types
49
313
 
50
- ## API Reference
314
+ All types are exported for TypeScript users:
51
315
 
52
- > **Note**: The values for the therapist UUID and the category IDs can be obtained from the HTML embedding option in the [appointment settings](https://app.tebuto.de/einstellungen/termine).
316
+ ```tsx
317
+ import type {
318
+ // Configuration
319
+ TebutoBookingWidgetConfiguration,
320
+ TebutoWidgetTheme,
53
321
 
54
- | Name | Description | Type | Required | Default |
55
- | --------------- | --------------------------------------------------------------- | ---------- | -------- | ------------- |
56
- | therapistUUID | A unique identifier for the therapist. | `string` | `Yes` | - |
57
- | backgroundColor | The hex background color of the component. | `string` | `No` | `transparent` |
58
- | border | Specifies the border style (e.g., `none`, `solid`, `dashed`). | `boolean` | `No` | `true` |
59
- | categories | An array of appointment category IDs to be shown in the widget. | `number[]` | `No` | `[]` |
322
+ // API Data
323
+ Therapist,
324
+ TimeSlot,
325
+ EnrichedTimeSlot,
326
+ ClaimResponse,
327
+ BookingRequest,
328
+ BookingResponse,
329
+ ClientInfo,
330
+
331
+ // Utilities
332
+ AppointmentLocation, // 'virtual' | 'onsite' | 'not-fixed'
333
+ Address,
334
+ SlotsByDate,
335
+ AsyncState,
336
+ } from "@tebuto/react-booking-widget";
337
+ ```
60
338
 
61
339
  ## License
62
340
 
package/dist/cjs/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var t=require("react/jsx-runtime"),e=function(){return e=Object.assign||function(t){for(var e,r=1,o=arguments.length;r<o;r++)for(var i in e=arguments[r])Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t},e.apply(this,arguments)};"function"==typeof SuppressedError&&SuppressedError;function r(r){var o=r.config;return t.jsx("script",e({src:"https://widget.tebuto.de/booking-widget.js","data-therapist-uuid":o.therapistUUID},o.backgroundColor?{"data-background-color":o.backgroundColor}:{},o.categories?{"data-categories":o.categories.join(",")}:{},void 0!==o.border?{"data-border":o.border?"true":"false"}:{},{"data-testid":"tebuto-booking-widget-script"}))}exports.TebutoBookingWidget=function(e){var o=e.noScriptText,i=void 0===o?"Widget konnte nicht geladen werden. Möglicherweise ist Skripting im Browser deaktiviert.":o,n=function(t,e){var r={};for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&e.indexOf(o)<0&&(r[o]=t[o]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(o=Object.getOwnPropertySymbols(t);i<o.length;i++)e.indexOf(o[i])<0&&Object.prototype.propertyIsEnumerable.call(t,o[i])&&(r[o[i]]=t[o[i]])}return r}(e,["noScriptText"]);return t.jsxs("div",{id:"tebuto-booking-widget","data-testid":"tebuto-booking-widget-container",children:[t.jsx(r,{config:n}),t.jsx("noscript",{"data-testid":"tebuto-booking-widget-noscript",children:i})]})};
1
+ "use strict";var e=require("react/jsx-runtime"),t=require("react"),n=function(){return n=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},n.apply(this,arguments)};function r(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))}function o(e,t){var n,r,o,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},a=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return a.next=s(0),a.throw=s(1),a.return=s(2),"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(s){return function(l){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;a&&(a=0,s[0]&&(i=0)),i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]<o[3])){i.label=s[1];break}if(6===s[0]&&i.label<o[1]){i.label=o[1],o=s;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(s);break}o[2]&&i.ops.pop(),i.trys.pop();continue}s=t.call(e,i)}catch(e){s=[6,e],r=0}finally{n=o=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}([s,l])}}}"function"==typeof SuppressedError&&SuppressedError;function i(t){var r=function(e){var t,n,r,o,i={"data-therapist-uuid":e.therapistUUID},a=null!==(t=e.backgroundColor)&&void 0!==t?t:null===(n=e.theme)||void 0===n?void 0:n.backgroundColor;a&&(i["data-background-color"]=a),e.categories&&e.categories.length>0&&(i["data-categories"]=e.categories.join(",")),void 0!==e.border&&(i["data-border"]=e.border?"true":"false"),void 0!==e.includeSubusers&&(i["data-include-subusers"]=e.includeSubusers?"true":"false"),void 0!==e.showQuickFilters&&(i["data-show-quick-filters"]=e.showQuickFilters?"true":"false");var s=null!==(r=e.inheritFont)&&void 0!==r?r:null===(o=e.theme)||void 0===o?void 0:o.inheritFont;return void 0!==s&&(i["data-inherit-font"]=s?"true":"false"),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)}(i,e.theme),i}(t.config);return e.jsx("script",n({src:"https://widget.tebuto.de/booking.js"},r,{"data-testid":"tebuto-booking-widget-script"}))}var a=t.createContext(null);function s(n){var r=n.therapistUUID,o=n.apiBaseUrl,i=void 0===o?"https://api.tebuto.de":o,s=n.categories,l=n.includeSubusers,c=n.children,d=t.useMemo((function(){return{therapistUUID:r,apiBaseUrl:i,categories:s,includeSubusers:l,buildUrl:function(e){return"".concat(i).concat(e)}}}),[r,i,s,l]);return e.jsx(a.Provider,{value:d,children:c})}function l(){var e=t.useContext(a);if(!e)throw new Error("useTebutoContext must be used within a TebutoProvider");return e}function c(){var e=this,i=l(),a=i.therapistUUID,s=i.buildUrl,c=t.useState({data:null,isLoading:!0,error:null}),d=c[0],u=c[1],g=t.useCallback((function(){return r(e,void 0,void 0,(function(){var e,t,r,i;return o(this,(function(o){switch(o.label){case 0:u((function(e){return n(n({},e),{isLoading:!0,error:null})})),o.label=1;case 1:return o.trys.push([1,4,,5]),[4,fetch(s("/therapists/uuid/".concat(a)))];case 2:if(!(e=o.sent()).ok)throw new Error("Failed to fetch therapist: ".concat(e.statusText));return[4,e.json()];case 3:return t=o.sent(),u({data:t,isLoading:!1,error:null}),[3,5];case 4:return r=o.sent(),i=r instanceof Error?r:new Error("Unknown error occurred"),u({data:null,isLoading:!1,error:i}),[3,5];case 5:return[2]}}))}))}),[a,s]);return t.useEffect((function(){g()}),[g]),n(n({},d),{refetch:g})}function d(e){var t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0");return"".concat(t,"-").concat(n,"-").concat(r)}function u(e){var t=new Date(e.start),r=new Date(e.end),o=new Date,i=new Date(o.getFullYear(),o.getMonth(),o.getDate()),a=new Date(t.getFullYear(),t.getMonth(),t.getDate()),s=Math.round((r.getTime()-t.getTime())/6e4);return n(n({},e),{dateKey:d(t),timeString:t.toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"}),durationMinutes:s,formattedPrice:new Intl.NumberFormat("de-DE",{style:"currency",currency:"EUR"}).format(Number.parseFloat(e.price)),isToday:a.getTime()===i.getTime(),isPast:t<o})}function g(e){var i,a,s,c=this;void 0===e&&(e={});var g=e.autoFetch,b=void 0===g||g,f=e.categories,p=l(),h=p.therapistUUID,m=p.buildUrl,v=p.categories,x=t.useState({data:null,isLoading:b,error:null}),y=x[0],k=x[1],j=null!=f?f:v,S=null!==(i=null==j?void 0:j.join(","))&&void 0!==i?i:"",w=t.useCallback((function(){return r(c,void 0,void 0,(function(){var e,t,r,i,a;return o(this,(function(o){switch(o.label){case 0:k((function(e){return n(n({},e),{isLoading:!0,error:null})})),o.label=1;case 1:return o.trys.push([1,4,,5]),e=new URL(m("/events/".concat(h))),S&&e.searchParams.set("categories",S),[4,fetch(e.toString())];case 2:if(!(t=o.sent()).ok)throw new Error("Failed to fetch slots: ".concat(t.statusText));return[4,t.json()];case 3:return r=o.sent(),k({data:r,isLoading:!1,error:null}),[3,5];case 4:return i=o.sent(),a=i instanceof Error?i:new Error("Unknown error occurred"),k({data:null,isLoading:!1,error:a}),[3,5];case 5:return[2]}}))}))}),[h,m,S]);t.useEffect((function(){b&&w()}),[b,w]);var D=t.useMemo((function(){if(!y.data)return{};for(var e={},t=new Date,n=0,r=y.data;n<r.length;n++){var o=r[n],i=new Date(o.start);if(!(i<=t))e[a=d(i)]||(e[a]=[]),e[a].push(o)}for(var a in e)e[a].sort((function(e,t){return new Date(e.start).getTime()-new Date(t.start).getTime()}));return e}),[y.data]),C=t.useMemo((function(){return Object.keys(D).map((function(e){return new Date(e)})).sort((function(e,t){return e.getTime()-t.getTime()}))}),[D]),I=t.useCallback((function(e){var t,n=d(e);return(null!==(t=D[n])&&void 0!==t?t:[]).map(u)}),[D]),T=t.useMemo((function(){if(!y.data)return[];for(var e=new Map,t=0,n=y.data;t<n.length;t++){var r=n[t];e.has(r.eventCategoryId)||e.set(r.eventCategoryId,{id:r.eventCategoryId,name:r.title,color:r.color})}return Array.from(e.values())}),[y.data]);return n(n({},y),{refetch:w,slotsByDate:D,availableDates:C,getSlotsForDate:I,categories:T,totalSlots:null!==(s=null===(a=y.data)||void 0===a?void 0:a.length)&&void 0!==s?s:0})}function b(){var e=this,i=l(),a=i.therapistUUID,s=i.buildUrl,c=t.useState({claimedSlot:null,claimResponse:null,isLoading:!1,error:null}),d=c[0],u=c[1],g=t.useRef(null),b=t.useCallback((function(){return r(e,void 0,void 0,(function(){return o(this,(function(e){switch(e.label){case 0:if(!d.claimedSlot||!g.current)return u((function(e){return n(n({},e),{claimedSlot:null,claimResponse:null})})),[2];e.label=1;case 1:return e.trys.push([1,3,4,5]),[4,fetch(s("/events/".concat(a,"/unclaim")),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({start:d.claimedSlot.start,eventRuleId:d.claimedSlot.eventRuleId})})];case 2:case 3:return e.sent(),[3,5];case 4:return g.current=null,u((function(e){return n(n({},e),{claimedSlot:null,claimResponse:null})})),[7];case 5:return[2]}}))}))}),[d.claimedSlot,a,s]),f=t.useCallback((function(t){return r(e,void 0,void 0,(function(){var e,r,i,l,c;return o(this,(function(o){switch(o.label){case 0:return e="".concat(t.start,"-").concat(t.eventRuleId),g.current===e&&d.claimResponse?[2,d.claimResponse]:g.current&&g.current!==e?[4,b()]:[3,2];case 1:o.sent(),o.label=2;case 2:u((function(e){return n(n({},e),{isLoading:!0,error:null})})),o.label=3;case 3:return o.trys.push([3,6,,7]),[4,fetch(s("/events/".concat(a,"/claim")),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({start:t.start,end:t.end,eventRuleId:t.eventRuleId})})];case 4:if(!(r=o.sent()).ok)throw new Error("Failed to claim slot: ".concat(r.statusText));return[4,r.json()];case 5:return(i=o.sent()).isAvailable?(g.current=e,u({claimedSlot:t,claimResponse:i,isLoading:!1,error:null}),[2,i]):(u((function(e){return n(n({},e),{isLoading:!1,error:new Error("This time slot is no longer available")})})),[2,null]);case 6:return l=o.sent(),c=l instanceof Error?l:new Error("Unknown error occurred"),u((function(e){return n(n({},e),{isLoading:!1,error:c})})),[2,null];case 7:return[2]}}))}))}),[a,s,b,d.claimResponse]),p=t.useCallback((function(e){return!!d.claimedSlot&&(d.claimedSlot.start===e.start&&d.claimedSlot.eventRuleId===e.eventRuleId)}),[d.claimedSlot]),h=t.useCallback((function(){u((function(e){return n(n({},e),{error:null})}))}),[]);return n(n({},d),{claim:f,unclaim:b,isClaimed:p,clearError:h})}function f(){var e=this,i=l(),a=i.therapistUUID,s=i.buildUrl,c=t.useState({booking:null,isLoading:!1,error:null,isSuccess:!1}),d=c[0],u=c[1],g=t.useCallback((function(t){return r(e,void 0,void 0,(function(){var e,r,i,l,c,d,g;return o(this,(function(o){switch(o.label){case 0:e=t.slot,r=t.client,i=t.locationSelection,u((function(e){return n(n({},e),{isLoading:!0,error:null})})),o.label=1;case 1:return o.trys.push([1,4,,5]),[4,fetch(s("/events/".concat(a,"/book")),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n({start:e.start,end:e.end,eventRuleId:e.eventRuleId,locationSelection:null!=i?i:e.location},r))})];case 2:if(!(l=o.sent()).ok)throw new Error("Booking failed: ".concat(l.statusText));return[4,l.json()];case 3:return c=o.sent(),u({booking:c,isLoading:!1,error:null,isSuccess:!0}),[2,c];case 4:return d=o.sent(),g=d instanceof Error?d:new Error("Unknown error occurred"),u((function(e){return n(n({},e),{isLoading:!1,error:g,isSuccess:!1})})),[2,null];case 5:return[2]}}))}))}),[a,s]),b=t.useCallback((function(){u({booking:null,isLoading:!1,error:null,isSuccess:!1})}),[]),f=t.useCallback((function(){var e;if(null===(e=d.booking)||void 0===e?void 0:e.ics){var t=new Blob([d.booking.ics],{type:"text/calendar;charset=utf-8"}),n=window.URL.createObjectURL(t),r=document.createElement("a");r.href=n,r.setAttribute("download","appointment.ics"),document.body.appendChild(r),r.click(),document.body.removeChild(r),window.URL.revokeObjectURL(n)}}),[d.booking]);return n(n({},d),{book:g,reset:b,downloadCalendar:f})}function p(e){var n,i,a,s=this;void 0===e&&(e={});var l=e.onBookingComplete,d=e.onError,u=t.useState("loading"),p=u[0],h=u[1],m=t.useState(null),v=m[0],x=m[1],y=t.useState(null),k=y[0],j=y[1],S=t.useState(null),w=S[0],D=S[1],C=c(),I=g({categories:e.categories}),T=b(),R=f();t.useMemo((function(){"loading"!==p||C.isLoading||I.isLoading||(C.error||I.error?h("error"):h("date-selection"))}),[p,C.isLoading,I.isLoading,C.error,I.error]);var L=t.useMemo((function(){return v?I.getSlotsForDate(v):[]}),[v,I]),F=t.useCallback((function(e){x(e),j(null),D(null),e&&h("time-selection")}),[x,j,D,h]),z=t.useCallback((function(e){return r(s,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return e?[3,2]:[4,T.unclaim()];case 1:return t.sent(),j(null),D(null),[2,!0];case 2:return[4,T.claim(e)];case 3:return t.sent()?(j(e),"not-fixed"!==e.location&&D(e.location),h("booking-form"),[2,!0]):(T.error&&(null==d||d(T.error)),[2,!1])}}))}))}),[T,d]),U=t.useCallback((function(e){return r(s,void 0,void 0,(function(){var t;return o(this,(function(n){switch(n.label){case 0:return k?[4,R.book({slot:k,client:e,locationSelection:null!=w?w:k.location})]:[2,!1];case 1:return(t=n.sent())?(null==l||l(t),h("confirmation"),[2,!0]):(R.error&&(null==d||d(R.error)),[2,!1])}}))}))}),[k,w,R,l,d]),B=t.useCallback((function(){h("loading"),x(null),j(null),D(null),R.reset(),T.unclaim(),I.refetch()}),[R,T,I]),E=t.useCallback((function(e){h(e)}),[]),W=t.useCallback((function(e){D(e)}),[]),M=C.isLoading||I.isLoading||T.isLoading||R.isLoading,O=null!==(a=null!==(i=null!==(n=C.error)&&void 0!==n?n:I.error)&&void 0!==i?i:T.error)&&void 0!==a?a:R.error;return{step:p,goToStep:E,therapist:C,slots:I,selectedDate:v,selectDate:F,selectedDateSlots:L,selectedSlot:k,selectSlot:z,selectedLocation:w,setLocation:W,claim:T,booking:R,submitBooking:U,reset:B,isLoading:M,error:O}}var h={"--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 v(){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(t){var n=t.current,r=t.total;return e.jsx("div",{style:{display:"flex",alignItems:"center",justifyContent:"center",gap:8,marginBottom:32},children:Array.from({length:r},(function(t,r){return e.jsx("div",{style:{display:"flex",alignItems:"center",gap:8},children:e.jsx("div",{style:{width:r===n?28:10,height:10,borderRadius:5,background:r<=n?"var(--booking-accent)":"var(--booking-border)",transition:"all 0.2s ease",boxShadow:r===n?"0 0 12px var(--booking-accent-glow)":"none"}})},r)}))})}function y(n){var r=n.availableDates,o=n.selectedDate,i=n.onSelectDate,a=t.useState((function(){return r.length>0?new Date(r[0]):new Date})),s=a[0],l=a[1],c=t.useMemo((function(){return new Set(r.map((function(e){return e.toDateString()})))}),[r]),d=t.useMemo((function(){for(var e=s.getFullYear(),t=s.getMonth(),n=new Date(e,t,1),r=new Date(e,t+1,0).getDate(),o=(n.getDay()+6)%7,i=[],a=[],l=0;l<o;l++)a.push(null);for(var c=1;c<=r;c++)a.push(c),7===a.length&&(i.push(a),a=[]);if(a.length>0){for(;a.length<7;)a.push(null);i.push(a)}return{year:e,month:t,daysInMonth:r,firstDayOfWeek:o,weeks:i}}),[s]),u=d.year,g=d.month,b=d.weeks,f=function(e){var t=new Date(u,g,e);return c.has(t.toDateString())},p=new Date,h=u>p.getFullYear()||u===p.getFullYear()&&g>p.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:function(){l(new Date(u,g-1,1))},disabled:!h,style:{width:36,height:36,border:"1px solid var(--booking-border)",borderRadius:"var(--booking-radius-sm)",background:"var(--booking-surface)",color:h?"var(--booking-text)":"var(--booking-text-subtle)",cursor:h?"pointer":"not-allowed",display:"flex",alignItems:"center",justifyContent:"center",fontSize:16,transition:"var(--booking-transition)",opacity:h?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"][g]," ",u]}),e.jsx("button",{type:"button",onClick:function(){l(new Date(u,g+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((function(t){return 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:b.map((function(t,n){return e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(7, 1fr)",gap:4},children:t.map((function(t,n){if(null===t)return e.jsx("div",{style:{aspectRatio:"1",minHeight:44}},n);var r=f(t),a=function(e){return!!o&&new Date(u,g,e).toDateString()===o.toDateString()}(t),s=function(e){var t=new Date;return e===t.getDate()&&g===t.getMonth()&&u===t.getFullYear()}(t);return e.jsxs("button",{type:"button",onClick:function(){return function(e){f(e)&&i(new Date(u,g,e))}(t)},disabled:!r,style:{aspectRatio:"1",minHeight:44,border:a?"2px solid var(--booking-accent)":s?"1px solid var(--booking-accent)":"1px solid transparent",borderRadius:"var(--booking-radius-sm)",background:a?"var(--booking-accent)":r?"var(--booking-surface-elevated)":"transparent",color:a?"#000":r?"var(--booking-text)":"var(--booking-text-subtle)",cursor:r?"pointer":"default",fontSize:14,fontWeight:a?600:r?500:400,fontFamily:"inherit",display:"flex",alignItems:"center",justifyContent:"center",transition:"var(--booking-transition)",opacity:r?1:.35,position:"relative"},children:[t,r&&!a&&e.jsx("span",{style:{position:"absolute",bottom:6,width:4,height:4,borderRadius:"50%",background:"var(--booking-accent)"}})]},n)}))},n)}))}),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)"}}),"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}}),"Keine Termine"]})]})]})}function k(t){var n,r=t.slots,o=t.selectedSlot,i=t.onSelectSlot,a=t.selectedDate,s=t.onBack,l=function(e){switch(e){case"virtual":return"🎥";case"onsite":return"📍";case"not-fixed":return"🔄"}},c=function(e){switch(e){case"virtual":return"Online";case"onsite":return"Vor Ort";case"not-fixed":return"Flexibel"}},d=r.reduce((function(e,t){var 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:s,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:"🕐"}),(n=a,n.toLocaleDateString("de-DE",{weekday:"long",day:"numeric",month:"long",year:"numeric"}))]})]}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:28},children:Object.entries(d).map((function(t){var n,r=t[0],a=t[1];return 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:(null===(n=a[0])||void 0===n?void 0:n.color)||"var(--booking-accent)"}}),r]}),e.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(240px, 1fr))",gap:12},children:a.map((function(t){var n=(null==o?void 0:o.start)===t.start&&(null==o?void 0:o.eventRuleId)===t.eventRuleId;return e.jsxs("button",{type:"button",onClick:function(){return i(t)},style:{padding:"14px 16px",border:n?"2px solid var(--booking-accent)":"1px solid var(--booking-border)",borderRadius:"var(--booking-radius)",background:n?"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:[l(t.location)," ",c(t.location)]})]}),e.jsx("span",{style:{fontSize:14,fontWeight:600,color:"var(--booking-accent)"},children:t.formattedPrice})]},"".concat(t.start,"-").concat(t.eventRuleId))}))})]},r)}))}),0===r.length&&e.jsx("div",{style:{textAlign:"center",padding:40,color:"var(--booking-text-muted)"},children:"Keine Termine an diesem Tag verfügbar."})]})}function j(r){var o,i=r.slot,a=r.showLocationSelector,s=r.selectedLocation,l=r.onLocationChange,c=r.onSubmit,d=r.onBack,u=r.isLoading,g=r.error,b=t.useState({firstName:"",lastName:"",email:"",phone:"",notes:""}),f=b[0],p=b[1],h={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"},v={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:d,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:i.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:i.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:"📆"})," ",(o=i.start,new Date(o).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:"🕐"})," ",i.timeString," Uhr"]}),e.jsxs("span",{style:{display:"flex",alignItems:"center",gap:6},children:[e.jsx("span",{style:{opacity:.7},children:"⏱"})," ",i.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:i.formattedPrice})]})]})]}),a&&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((function(t){return e.jsxs("button",{type:"button",onClick:function(){return l(t)},style:{padding:"16px 20px",border:s===t?"2px solid var(--booking-accent)":"1px solid var(--booking-border)",borderRadius:"var(--booking-radius)",background:s===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"})]})]}),g&&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:"⚠️"})," ",g.message]}),e.jsxs("form",{onSubmit:function(e){e.preventDefault(),c({firstName:f.firstName,lastName:f.lastName,email:f.email,phone:f.phone||void 0,notes:f.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:v,children:"Vorname"}),e.jsx("input",{id:"booking-firstName",type:"text",required:!0,value:f.firstName,onChange:function(e){return p((function(t){return n(n({},t),{firstName:e.target.value})}))},style:h,placeholder:"Max"})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"booking-lastName",style:v,children:"Nachname"}),e.jsx("input",{id:"booking-lastName",type:"text",required:!0,value:f.lastName,onChange:function(e){return p((function(t){return n(n({},t),{lastName:e.target.value})}))},style:h,placeholder:"Mustermann"})]})]}),e.jsxs("div",{children:[e.jsx("label",{htmlFor:"booking-email",style:v,children:"E-Mail-Adresse"}),e.jsx("input",{id:"booking-email",type:"email",required:!0,value:f.email,onChange:function(e){return p((function(t){return n(n({},t),{email:e.target.value})}))},style:h,placeholder:"max@beispiel.de"})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"booking-phone",style:n(n({},v),{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:f.phone,onChange:function(e){return p((function(t){return n(n({},t),{phone:e.target.value})}))},style:h,placeholder:"+49 123 456789"})]}),e.jsxs("div",{children:[e.jsxs("label",{htmlFor:"booking-notes",style:n(n({},v),{color:"var(--booking-text-muted)"}),children:["Nachricht ",e.jsx("span",{style:{fontWeight:400,textTransform:"none"},children:"(optional)"})]}),e.jsx("textarea",{id:"booking-notes",value:f.notes,onChange:function(e){return p((function(t){return n(n({},t),{notes:e.target.value})}))},style:n(n({},h),{minHeight:100,resize:"vertical"}),placeholder:"Gibt es etwas, das wir vorab wissen sollten?"})]}),e.jsx("button",{type:"submit",disabled:u||a&&!s,style:{marginTop:8,padding:"18px 32px",border:"none",borderRadius:"var(--booking-radius)",background:"var(--booking-accent)",color:"#000",fontSize:16,fontWeight:700,cursor:u||a&&!s?"not-allowed":"pointer",fontFamily:"inherit",transition:"var(--booking-transition)",opacity:u||a&&!s?.5:1,display:"flex",alignItems:"center",justifyContent:"center",gap:10,boxShadow:u||a&&!s?"none":"0 4px 14px var(--booking-accent-glow)"},children:u?e.jsxs(e.Fragment,{children:[e.jsx(m,{})," Wird gebucht..."]}):e.jsx(e.Fragment,{children:"Termin verbindlich buchen"})})]})]})]})}function S(t){var n,r=t.slot,o=t.onDownloadCalendar,i=t.onReset;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:"Termin gebucht!"}),e.jsxs("p",{style:{fontSize:15,color:"var(--booking-text-muted)",maxWidth:400,margin:"0 auto 32px",lineHeight:1.6},children:["Ihr Termin ",e.jsxs("strong",{style:{color:"var(--booking-text)"},children:['"',r.title,'"']})," am"," ",e.jsx("strong",{style:{color:"var(--booking-text)"},children:(n=r.start,new Date(n).toLocaleDateString("de-DE",{weekday:"long",day:"numeric",month:"long",year:"numeric"}))})," um"," ",e.jsxs("strong",{style:{color:"var(--booking-text)"},children:[r.timeString," Uhr"]})," wurde erfolgreich gebucht."]}),e.jsxs("div",{style:{display:"flex",gap:12,justifyContent:"center",flexWrap:"wrap"},children:[e.jsx("button",{type:"button",onClick:o,style:{padding:"12px 20px",border:"none",borderRadius:"var(--booking-radius-sm)",background:"var(--booking-accent)",color:"#000",fontSize:14,fontWeight:600,cursor:"pointer",fontFamily:"inherit",display:"flex",alignItems:"center",gap:8},children:"📅 Zum Kalender hinzufügen"}),e.jsx("button",{type:"button",onClick:i,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"})]})]})}function w(){var t,i=this,a=p(),s=a.step,l=a.goToStep,c=a.therapist,d=a.slots,u=a.selectedDate,g=a.selectDate,b=a.selectedDateSlots,f=a.selectedSlot,m=a.selectSlot,w=a.selectedLocation,D=a.setLocation,C=a.booking,I=a.submitBooking,T=a.reset,R=a.isLoading,L=a.error,F=f?n(n({},f),{dateKey:new Date(f.start).toISOString().split("T")[0],timeString:new Date(f.start).toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"}),durationMinutes:Math.round((new Date(f.end).getTime()-new Date(f.start).getTime())/6e4),formattedPrice:new Intl.NumberFormat("de-DE",{style:"currency",currency:"EUR"}).format(Number.parseFloat(f.price)),isToday:!1,isPast:!1}):null;return e.jsxs("div",{style:n(n({},h),{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",c.data?" bei ".concat(c.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"!==s&&"error"!==s&&"confirmation"!==s&&e.jsx(x,{current:function(){switch(s){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"===s&&e.jsx(v,{}),"error"===s&&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:null!==(t=null==L?void 0:L.message)&&void 0!==t?t:"Ein Fehler ist aufgetreten"}),e.jsx("button",{type:"button",onClick:T,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"===s&&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:"📅"}),"Datum wählen"]}),e.jsx(y,{availableDates:d.availableDates,selectedDate:u,onSelectDate:g})]}),"time-selection"===s&&u&&e.jsx(k,{slots:b,selectedSlot:f?n(n({},f),{dateKey:"",timeString:"",durationMinutes:0,formattedPrice:"",isToday:!1,isPast:!1}):null,onSelectSlot:function(e){return r(i,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return[4,m(e)];case 1:return t.sent(),[2]}}))}))},selectedDate:u,onBack:function(){return l("date-selection")}}),"booking-form"===s&&F&&e.jsx(j,{slot:F,showLocationSelector:"not-fixed"===F.location,selectedLocation:w,onLocationChange:D,onSubmit:I,onBack:function(){return l("time-selection")},isLoading:R,error:L}),"confirmation"===s&&F&&e.jsx(S,{slot:F,onDownloadCalendar:C.downloadCalendar,onReset:T})]})})]})]})}exports.CustomBookingExample=function(t){var n=t.therapistUUID,r=t.categories;return e.jsx(s,{therapistUUID:n,categories:r,children:e.jsx(w,{})})},exports.TebutoBookingWidget=function(t){var n=t.noScriptText,r=void 0===n?"Widget konnte nicht geladen werden. Möglicherweise ist Skripting im Browser deaktiviert.":n,o=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(n[r[o]]=e[r[o]])}return n}(t,["noScriptText"]);return e.jsxs("div",{id:"tebuto-booking-widget","data-testid":"tebuto-booking-widget-container",children:[e.jsx(i,{config:o}),e.jsx("noscript",{"data-testid":"tebuto-booking-widget-noscript",children:r})]})},exports.TebutoProvider=s,exports.useAvailableSlots=g,exports.useBookAppointment=f,exports.useBookingFlow=p,exports.useClaimSlot=b,exports.useTebutoContext=l,exports.useTherapist=c;
2
2
  //# sourceMappingURL=index.js.map