@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.
- package/.storybook/main.ts +15 -0
- package/.storybook/preview.ts +39 -0
- package/README.md +308 -30
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/types.d.ts +493 -3
- package/package.json +47 -21
- package/pnpm-workspace.yaml +2 -0
- package/vite.config.ts +6 -0
|
@@ -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>
|
|
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/
|
|
11
|
-
<a href="https://github.com/tebuto/react-
|
|
12
|
-
<a href="https://github.com/tebuto/react-
|
|
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
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
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
|
-
##
|
|
35
|
+
## Features
|
|
24
36
|
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
43
|
+
## Installation
|
|
29
44
|
|
|
30
|
-
```
|
|
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
|
-
|
|
225
|
+
### Individual Hooks
|
|
226
|
+
|
|
227
|
+
For fine-grained control, use the individual hooks:
|
|
35
228
|
|
|
36
|
-
|
|
229
|
+
#### useTherapist
|
|
37
230
|
|
|
38
|
-
|
|
231
|
+
Fetch therapist information:
|
|
39
232
|
|
|
40
|
-
```
|
|
41
|
-
|
|
233
|
+
```tsx
|
|
234
|
+
const { data, isLoading, error, refetch } = useTherapist();
|
|
235
|
+
// data: { name, firstName, lastName, address, showWatermark }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### useAvailableSlots
|
|
42
239
|
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
312
|
+
## API Types
|
|
49
313
|
|
|
50
|
-
|
|
314
|
+
All types are exported for TypeScript users:
|
|
51
315
|
|
|
52
|
-
|
|
316
|
+
```tsx
|
|
317
|
+
import type {
|
|
318
|
+
// Configuration
|
|
319
|
+
TebutoBookingWidgetConfiguration,
|
|
320
|
+
TebutoWidgetTheme,
|
|
53
321
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|