@idkwebsites/components 0.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/dist/index.cjs ADDED
@@ -0,0 +1,847 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AvailabilityPicker: () => AvailabilityPicker,
24
+ BookingWidget: () => BookingWidget,
25
+ ContactForm: () => ContactForm,
26
+ DatePicker: () => DatePicker,
27
+ PlatformProvider: () => PlatformProvider,
28
+ ServiceCard: () => ServiceCard,
29
+ ServicePicker: () => ServicePicker,
30
+ ServicesList: () => ServicesList,
31
+ StaffPicker: () => StaffPicker,
32
+ TeamGrid: () => TeamGrid,
33
+ TeamMember: () => TeamMember,
34
+ TimePicker: () => TimePicker,
35
+ useAvailability: () => useAvailability,
36
+ useBookingLookup: () => useBookingLookup,
37
+ useCancelBooking: () => useCancelBooking,
38
+ useCreateBooking: () => useCreateBooking,
39
+ useServices: () => useServices,
40
+ useTeam: () => useTeam,
41
+ useTenant: () => useTenant
42
+ });
43
+ module.exports = __toCommonJS(src_exports);
44
+
45
+ // src/core/PlatformProvider.tsx
46
+ var import_react2 = require("react");
47
+ var import_react_query = require("@tanstack/react-query");
48
+
49
+ // src/core/context.ts
50
+ var import_react = require("react");
51
+ var PlatformContext = (0, import_react.createContext)(null);
52
+ function usePlatformConfig() {
53
+ const context = (0, import_react.useContext)(PlatformContext);
54
+ if (!context) {
55
+ throw new Error("PlatformProvider is missing in the component tree.");
56
+ }
57
+ return context;
58
+ }
59
+
60
+ // src/core/PlatformProvider.tsx
61
+ var import_jsx_runtime = require("react/jsx-runtime");
62
+ var DEFAULT_API_URL = process.env.NEXT_PUBLIC_IDK_API_URL || process.env.NEXT_PUBLIC_PLATFORM_API_URL || "https://app.idkwebsites.com/api/v1";
63
+ function PlatformProvider({
64
+ apiKey,
65
+ apiUrl = DEFAULT_API_URL,
66
+ children,
67
+ dehydratedState,
68
+ queryOptions,
69
+ queryClient
70
+ }) {
71
+ const [client] = (0, import_react2.useState)(
72
+ () => queryClient || new import_react_query.QueryClient({
73
+ defaultOptions: {
74
+ queries: {
75
+ staleTime: 3e4,
76
+ retry: 1,
77
+ ...queryOptions?.queries
78
+ },
79
+ mutations: {
80
+ ...queryOptions?.mutations
81
+ }
82
+ }
83
+ })
84
+ );
85
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlatformContext.Provider, { value: { apiKey, apiUrl }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query.QueryClientProvider, { client, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query.HydrationBoundary, { state: dehydratedState, children }) }) });
86
+ }
87
+
88
+ // src/core/hooks/useTenant.ts
89
+ var import_react_query2 = require("@tanstack/react-query");
90
+
91
+ // src/core/api-client.ts
92
+ var ApiError = class extends Error {
93
+ status;
94
+ code;
95
+ constructor(message, status, code) {
96
+ super(message);
97
+ this.name = "ApiError";
98
+ this.status = status;
99
+ this.code = code;
100
+ }
101
+ };
102
+ function normalizeBaseUrl(apiUrl) {
103
+ return apiUrl.replace(/\/+$/, "");
104
+ }
105
+ async function apiRequest(config, path, options = {}) {
106
+ const url = `${normalizeBaseUrl(config.apiUrl)}${path.startsWith("/") ? path : `/${path}`}`;
107
+ const headers = new Headers(options.headers);
108
+ headers.set("X-API-Key", config.apiKey);
109
+ if (!headers.has("Content-Type") && options.body) {
110
+ headers.set("Content-Type", "application/json");
111
+ }
112
+ const response = await fetch(url, {
113
+ ...options,
114
+ headers
115
+ });
116
+ const payload = await response.json().catch(() => null);
117
+ if (!response.ok) {
118
+ const errorCode = payload?.error?.code || "REQUEST_FAILED";
119
+ const errorMessage = payload?.error?.message || response.statusText;
120
+ throw new ApiError(errorMessage, response.status, errorCode);
121
+ }
122
+ if (payload?.error) {
123
+ throw new ApiError(payload.error.message || "Request failed", payload.error.status || 400, payload.error.code || "REQUEST_FAILED");
124
+ }
125
+ return payload?.data;
126
+ }
127
+
128
+ // src/core/hooks/useTenant.ts
129
+ function useTenant() {
130
+ const config = usePlatformConfig();
131
+ return (0, import_react_query2.useQuery)({
132
+ queryKey: ["idk", "tenant"],
133
+ queryFn: () => apiRequest(config, "/tenant")
134
+ });
135
+ }
136
+
137
+ // src/core/hooks/useServices.ts
138
+ var import_react_query3 = require("@tanstack/react-query");
139
+ function useServices() {
140
+ const config = usePlatformConfig();
141
+ return (0, import_react_query3.useQuery)({
142
+ queryKey: ["idk", "services"],
143
+ queryFn: () => apiRequest(config, "/services")
144
+ });
145
+ }
146
+
147
+ // src/core/hooks/useTeam.ts
148
+ var import_react_query4 = require("@tanstack/react-query");
149
+ function useTeam() {
150
+ const config = usePlatformConfig();
151
+ return (0, import_react_query4.useQuery)({
152
+ queryKey: ["idk", "team"],
153
+ queryFn: () => apiRequest(config, "/team")
154
+ });
155
+ }
156
+
157
+ // src/core/hooks/useAvailability.ts
158
+ var import_react3 = require("react");
159
+ var import_react_query5 = require("@tanstack/react-query");
160
+ function buildQuery(params) {
161
+ const searchParams = new URLSearchParams();
162
+ searchParams.set("serviceId", params.serviceId);
163
+ if (params.staffId) searchParams.set("staffId", params.staffId);
164
+ if (params.startDate) searchParams.set("startDate", params.startDate);
165
+ if (params.endDate) searchParams.set("endDate", params.endDate);
166
+ if (params.date) searchParams.set("date", params.date);
167
+ if (params.days) searchParams.set("days", String(params.days));
168
+ return searchParams.toString();
169
+ }
170
+ function useAvailability(params) {
171
+ const config = usePlatformConfig();
172
+ const queryString = (0, import_react3.useMemo)(
173
+ () => buildQuery(params),
174
+ [params.serviceId, params.staffId, params.startDate, params.endDate, params.date, params.days]
175
+ );
176
+ const enabled = params.enabled ?? Boolean(params.serviceId);
177
+ return (0, import_react_query5.useQuery)({
178
+ queryKey: ["idk", "availability", queryString],
179
+ queryFn: () => apiRequest(config, `/availability?${queryString}`),
180
+ enabled
181
+ });
182
+ }
183
+
184
+ // src/core/hooks/useBooking.ts
185
+ var import_react_query6 = require("@tanstack/react-query");
186
+ function useCreateBooking() {
187
+ const config = usePlatformConfig();
188
+ return (0, import_react_query6.useMutation)({
189
+ mutationFn: (input) => apiRequest(config, "/bookings", {
190
+ method: "POST",
191
+ body: JSON.stringify(input)
192
+ })
193
+ });
194
+ }
195
+ function useCancelBooking() {
196
+ const config = usePlatformConfig();
197
+ return (0, import_react_query6.useMutation)({
198
+ mutationFn: (input) => apiRequest(config, `/bookings/${input.uid}/cancel`, {
199
+ method: "POST",
200
+ body: JSON.stringify({
201
+ reason: input.reason,
202
+ cancelledBy: input.cancelledBy || "customer"
203
+ })
204
+ })
205
+ });
206
+ }
207
+ function useBookingLookup(uid, enabled = true) {
208
+ const config = usePlatformConfig();
209
+ return (0, import_react_query6.useQuery)({
210
+ queryKey: ["idk", "booking", uid],
211
+ queryFn: () => apiRequest(config, `/bookings/${uid}`),
212
+ enabled: Boolean(uid) && enabled
213
+ });
214
+ }
215
+
216
+ // src/components/ServiceCard.tsx
217
+ var import_jsx_runtime2 = require("react/jsx-runtime");
218
+ function ServiceCard({
219
+ service,
220
+ className,
221
+ showDescription = true,
222
+ showPrice = true,
223
+ onSelect
224
+ }) {
225
+ const handleClick = () => {
226
+ if (onSelect) onSelect(service);
227
+ };
228
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
229
+ "div",
230
+ {
231
+ className: ["idk-card", onSelect ? "idk-card--clickable" : "", className].filter(Boolean).join(" "),
232
+ onClick: onSelect ? handleClick : void 0,
233
+ role: onSelect ? "button" : void 0,
234
+ tabIndex: onSelect ? 0 : void 0,
235
+ children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "idk-card__header", children: [
237
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "idk-card__title", children: service.name }),
238
+ showPrice && typeof service.price === "number" && service.price > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "idk-card__price", children: [
239
+ "$",
240
+ (service.price / 100).toFixed(2)
241
+ ] }) : null
242
+ ] }),
243
+ showDescription && service.description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "idk-card__description", children: service.description }) : null,
244
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "idk-card__meta", children: [
245
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
246
+ service.duration,
247
+ " min"
248
+ ] }),
249
+ service.category ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: service.category }) : null
250
+ ] })
251
+ ]
252
+ }
253
+ );
254
+ }
255
+
256
+ // src/components/ServicesList.tsx
257
+ var import_jsx_runtime3 = require("react/jsx-runtime");
258
+ function ServicesList({
259
+ layout = "grid",
260
+ columns = 3,
261
+ className,
262
+ showDescription,
263
+ showPrice,
264
+ onSelect
265
+ }) {
266
+ const { data, isLoading, error } = useServices();
267
+ if (isLoading) {
268
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "idk-state", children: "Loading services..." });
269
+ }
270
+ if (error || !data?.services?.length) {
271
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "idk-state", children: "No services available." });
272
+ }
273
+ const gridStyle = layout === "grid" ? { gridTemplateColumns: `repeat(${Math.max(1, columns)}, minmax(0, 1fr))` } : void 0;
274
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
275
+ "div",
276
+ {
277
+ className: [
278
+ "idk-services",
279
+ layout === "grid" ? "idk-services--grid" : "idk-services--list",
280
+ className
281
+ ].filter(Boolean).join(" "),
282
+ style: gridStyle,
283
+ children: data.services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
284
+ ServiceCard,
285
+ {
286
+ service,
287
+ showDescription,
288
+ showPrice,
289
+ onSelect
290
+ },
291
+ service.id
292
+ ))
293
+ }
294
+ );
295
+ }
296
+
297
+ // src/components/TeamMember.tsx
298
+ var import_jsx_runtime4 = require("react/jsx-runtime");
299
+ function TeamMember({
300
+ member,
301
+ className,
302
+ showRole = true,
303
+ showEmail = false
304
+ }) {
305
+ const initials = member.name?.split(" ").map((part) => part[0]).slice(0, 2).join("").toUpperCase();
306
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: ["idk-card", className].filter(Boolean).join(" "), children: [
307
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "idk-team__avatar", children: member.photo?.url ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: member.photo.url, alt: member.name }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: initials }) }),
308
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "idk-team__info", children: [
309
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "idk-card__title", children: member.name }),
310
+ showRole ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "idk-card__description", children: member.role || "Staff" }) : null,
311
+ showEmail ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "idk-card__meta", children: member.email }) : null
312
+ ] })
313
+ ] });
314
+ }
315
+
316
+ // src/components/TeamGrid.tsx
317
+ var import_jsx_runtime5 = require("react/jsx-runtime");
318
+ function TeamGrid({
319
+ columns = 3,
320
+ className,
321
+ showRole,
322
+ showEmail
323
+ }) {
324
+ const { data, isLoading, error } = useTeam();
325
+ if (isLoading) {
326
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "idk-state", children: "Loading team..." });
327
+ }
328
+ if (error || !data?.team?.length) {
329
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "idk-state", children: "No team members available." });
330
+ }
331
+ const gridStyle = { gridTemplateColumns: `repeat(${Math.max(1, columns)}, minmax(0, 1fr))` };
332
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
333
+ "div",
334
+ {
335
+ className: ["idk-team", className].filter(Boolean).join(" "),
336
+ style: gridStyle,
337
+ children: data.team.map((member) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
338
+ TeamMember,
339
+ {
340
+ member,
341
+ showRole,
342
+ showEmail
343
+ },
344
+ member.id
345
+ ))
346
+ }
347
+ );
348
+ }
349
+
350
+ // src/components/ContactForm.tsx
351
+ var import_react4 = require("react");
352
+ var import_react_query7 = require("@tanstack/react-query");
353
+ var import_jsx_runtime6 = require("react/jsx-runtime");
354
+ var DEFAULT_FIELDS = ["name", "email", "phone", "message"];
355
+ function ContactForm({
356
+ fields = DEFAULT_FIELDS,
357
+ formType = "contact",
358
+ submitLabel = "Send Message",
359
+ className,
360
+ onSuccess,
361
+ onError
362
+ }) {
363
+ const config = usePlatformConfig();
364
+ const [formState, setFormState] = (0, import_react4.useState)({
365
+ name: "",
366
+ email: "",
367
+ phone: "",
368
+ subject: "",
369
+ message: ""
370
+ });
371
+ const mutation = (0, import_react_query7.useMutation)({
372
+ mutationFn: (payload) => apiRequest(config, "/contact", {
373
+ method: "POST",
374
+ body: JSON.stringify(payload)
375
+ }),
376
+ onSuccess: () => {
377
+ setFormState({ name: "", email: "", phone: "", subject: "", message: "" });
378
+ onSuccess?.();
379
+ },
380
+ onError: (error) => {
381
+ const message = error instanceof Error ? error.message : "Failed to submit form";
382
+ onError?.(message);
383
+ }
384
+ });
385
+ const handleChange = (field, value) => {
386
+ setFormState((prev) => ({ ...prev, [field]: value }));
387
+ };
388
+ const handleSubmit = (event) => {
389
+ event.preventDefault();
390
+ mutation.mutate({
391
+ name: formState.name,
392
+ email: formState.email,
393
+ phone: formState.phone || void 0,
394
+ subject: formState.subject || void 0,
395
+ message: formState.message,
396
+ formType
397
+ });
398
+ };
399
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { className: ["idk-form", className].filter(Boolean).join(" "), onSubmit: handleSubmit, children: [
400
+ fields.includes("name") && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "idk-form__field", children: [
401
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Name" }),
402
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
403
+ "input",
404
+ {
405
+ value: formState.name,
406
+ onChange: (event) => handleChange("name", event.target.value),
407
+ required: true
408
+ }
409
+ )
410
+ ] }),
411
+ fields.includes("email") && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "idk-form__field", children: [
412
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Email" }),
413
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
414
+ "input",
415
+ {
416
+ type: "email",
417
+ value: formState.email,
418
+ onChange: (event) => handleChange("email", event.target.value),
419
+ required: true
420
+ }
421
+ )
422
+ ] }),
423
+ fields.includes("phone") && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "idk-form__field", children: [
424
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Phone" }),
425
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
426
+ "input",
427
+ {
428
+ value: formState.phone,
429
+ onChange: (event) => handleChange("phone", event.target.value)
430
+ }
431
+ )
432
+ ] }),
433
+ fields.includes("subject") && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "idk-form__field", children: [
434
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Subject" }),
435
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
436
+ "input",
437
+ {
438
+ value: formState.subject,
439
+ onChange: (event) => handleChange("subject", event.target.value)
440
+ }
441
+ )
442
+ ] }),
443
+ fields.includes("message") && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "idk-form__field", children: [
444
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Message" }),
445
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
446
+ "textarea",
447
+ {
448
+ value: formState.message,
449
+ onChange: (event) => handleChange("message", event.target.value),
450
+ rows: 5,
451
+ required: true
452
+ }
453
+ )
454
+ ] }),
455
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "submit", className: "idk-button", disabled: mutation.isPending, children: mutation.isPending ? "Sending..." : submitLabel }),
456
+ mutation.isError ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "idk-form__error", children: "Something went wrong. Please try again." }) : null,
457
+ mutation.isSuccess ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "idk-form__success", children: "Thanks! We'll be in touch soon." }) : null
458
+ ] });
459
+ }
460
+
461
+ // src/components/BookingWidget.tsx
462
+ var import_react5 = require("react");
463
+ var import_jsx_runtime7 = require("react/jsx-runtime");
464
+ function BookingWidget({
465
+ className,
466
+ showStaffSelection = true,
467
+ onSuccess
468
+ }) {
469
+ const { data: servicesData, isLoading: servicesLoading } = useServices();
470
+ const { data: teamData } = useTeam();
471
+ const createBooking = useCreateBooking();
472
+ const [step, setStep] = (0, import_react5.useState)("service");
473
+ const [selectedService, setSelectedService] = (0, import_react5.useState)(null);
474
+ const [selectedStaff, setSelectedStaff] = (0, import_react5.useState)(null);
475
+ const [selectedDate, setSelectedDate] = (0, import_react5.useState)(null);
476
+ const [selectedTime, setSelectedTime] = (0, import_react5.useState)(null);
477
+ const [selectedEndTime, setSelectedEndTime] = (0, import_react5.useState)(null);
478
+ const [details, setDetails] = (0, import_react5.useState)({
479
+ name: "",
480
+ email: "",
481
+ phone: "",
482
+ notes: ""
483
+ });
484
+ const staffOptions = (0, import_react5.useMemo)(() => {
485
+ if (!selectedService) return [];
486
+ if (selectedService.assignedStaff && selectedService.assignedStaff.length > 0) {
487
+ return selectedService.assignedStaff;
488
+ }
489
+ return teamData?.team || [];
490
+ }, [selectedService, teamData]);
491
+ const availability = useAvailability({
492
+ serviceId: selectedService?.id || "",
493
+ staffId: selectedStaff?.id,
494
+ days: 7,
495
+ enabled: Boolean(selectedService)
496
+ });
497
+ const requiresStaff = showStaffSelection || selectedService?.requiresStaffSelection || selectedService?.schedulingType === "customer-choice";
498
+ const handleSubmit = async (event) => {
499
+ event.preventDefault();
500
+ if (!selectedService || !selectedDate || !selectedTime || !selectedEndTime) return;
501
+ const start = /* @__PURE__ */ new Date(`${selectedDate}T${selectedTime}:00`);
502
+ const end = /* @__PURE__ */ new Date(`${selectedDate}T${selectedEndTime}:00`);
503
+ const result = await createBooking.mutateAsync({
504
+ serviceId: selectedService.id,
505
+ staffId: selectedStaff?.id,
506
+ startTime: start.toISOString(),
507
+ endTime: end.toISOString(),
508
+ customerName: details.name,
509
+ customerEmail: details.email,
510
+ customerPhone: details.phone || void 0,
511
+ customerNotes: details.notes || void 0
512
+ });
513
+ setStep("done");
514
+ onSuccess?.(result?.booking);
515
+ };
516
+ if (servicesLoading) {
517
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-state", children: "Loading booking options..." });
518
+ }
519
+ if (!servicesData?.services?.length) {
520
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-state", children: "No services available." });
521
+ }
522
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: ["idk-booking", className].filter(Boolean).join(" "), children: [
523
+ step === "service" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "idk-booking__step", children: [
524
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: "Select a service" }),
525
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-services idk-services--list", children: servicesData.services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
526
+ "button",
527
+ {
528
+ type: "button",
529
+ className: "idk-card idk-card--clickable",
530
+ onClick: () => {
531
+ setSelectedService(service);
532
+ if (requiresStaff) {
533
+ setStep("staff");
534
+ } else {
535
+ setStep("time");
536
+ }
537
+ },
538
+ children: [
539
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "idk-card__header", children: [
540
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "idk-card__title", children: service.name }),
541
+ typeof service.price === "number" && service.price > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "idk-card__price", children: [
542
+ "$",
543
+ (service.price / 100).toFixed(2)
544
+ ] }) : null
545
+ ] }),
546
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "idk-card__meta", children: [
547
+ service.duration,
548
+ " min"
549
+ ] })
550
+ ]
551
+ },
552
+ service.id
553
+ )) })
554
+ ] }),
555
+ step === "staff" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "idk-booking__step", children: [
556
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: "Select a team member" }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-team", children: staffOptions.map((staff) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
558
+ "button",
559
+ {
560
+ type: "button",
561
+ className: "idk-card idk-card--clickable",
562
+ onClick: () => {
563
+ setSelectedStaff(staff);
564
+ setStep("time");
565
+ },
566
+ children: [
567
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "idk-card__title", children: staff.name }),
568
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "idk-card__meta", children: staff.role || "Staff" })
569
+ ]
570
+ },
571
+ staff.id
572
+ )) }),
573
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
574
+ "button",
575
+ {
576
+ type: "button",
577
+ className: "idk-link",
578
+ onClick: () => {
579
+ setSelectedStaff(null);
580
+ setStep("time");
581
+ },
582
+ children: "Continue without selecting"
583
+ }
584
+ )
585
+ ] }),
586
+ step === "time" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "idk-booking__step", children: [
587
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: "Select a time" }),
588
+ availability.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-state", children: "Loading availability..." }) : availability.data ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "idk-availability", children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-availability__dates", children: availability.data.dates.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
590
+ "button",
591
+ {
592
+ type: "button",
593
+ className: entry.date === selectedDate ? "is-active" : void 0,
594
+ onClick: () => {
595
+ setSelectedDate(entry.date);
596
+ setSelectedTime(null);
597
+ setSelectedEndTime(null);
598
+ },
599
+ children: entry.date
600
+ },
601
+ entry.date
602
+ )) }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-availability__slots", children: (availability.data.dates.find((entry) => entry.date === selectedDate)?.slots || availability.data.dates[0]?.slots || []).filter((slot) => slot.available).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
604
+ "button",
605
+ {
606
+ type: "button",
607
+ className: slot.time === selectedTime ? "is-active" : void 0,
608
+ onClick: () => {
609
+ const date = selectedDate || availability.data?.dates[0]?.date;
610
+ setSelectedDate(date || null);
611
+ setSelectedTime(slot.time);
612
+ setSelectedEndTime(slot.endTime);
613
+ setStep("details");
614
+ },
615
+ children: slot.time
616
+ },
617
+ `${slot.time}-${slot.endTime}`
618
+ )) })
619
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-state", children: "No availability found." })
620
+ ] }),
621
+ step === "details" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { className: "idk-form", onSubmit: handleSubmit, children: [
622
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: "Your details" }),
623
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "idk-form__field", children: [
624
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Name" }),
625
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
626
+ "input",
627
+ {
628
+ value: details.name,
629
+ onChange: (event) => setDetails((prev) => ({ ...prev, name: event.target.value })),
630
+ required: true
631
+ }
632
+ )
633
+ ] }),
634
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "idk-form__field", children: [
635
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Email" }),
636
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
637
+ "input",
638
+ {
639
+ type: "email",
640
+ value: details.email,
641
+ onChange: (event) => setDetails((prev) => ({ ...prev, email: event.target.value })),
642
+ required: true
643
+ }
644
+ )
645
+ ] }),
646
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "idk-form__field", children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Phone" }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
649
+ "input",
650
+ {
651
+ value: details.phone,
652
+ onChange: (event) => setDetails((prev) => ({ ...prev, phone: event.target.value }))
653
+ }
654
+ )
655
+ ] }),
656
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "idk-form__field", children: [
657
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Notes" }),
658
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
659
+ "textarea",
660
+ {
661
+ rows: 4,
662
+ value: details.notes,
663
+ onChange: (event) => setDetails((prev) => ({ ...prev, notes: event.target.value }))
664
+ }
665
+ )
666
+ ] }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "submit", className: "idk-button", disabled: createBooking.isPending, children: createBooking.isPending ? "Booking..." : "Confirm booking" })
668
+ ] }),
669
+ step === "done" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "idk-state", children: "Booking confirmed. You'll receive a confirmation email shortly." })
670
+ ] });
671
+ }
672
+
673
+ // src/components/AvailabilityPicker.tsx
674
+ var import_react6 = require("react");
675
+ var import_jsx_runtime8 = require("react/jsx-runtime");
676
+ function AvailabilityPicker({
677
+ serviceId,
678
+ staffId,
679
+ date,
680
+ days = 7,
681
+ className,
682
+ onSelect
683
+ }) {
684
+ const [selectedDate, setSelectedDate] = (0, import_react6.useState)(date || null);
685
+ const [selectedTime, setSelectedTime] = (0, import_react6.useState)(null);
686
+ const { data, isLoading } = useAvailability({
687
+ serviceId,
688
+ staffId,
689
+ date,
690
+ days
691
+ });
692
+ if (isLoading) {
693
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-state", children: "Loading availability..." });
694
+ }
695
+ if (!data?.dates?.length) {
696
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-state", children: "No availability found." });
697
+ }
698
+ const activeDate = selectedDate || data.dates[0]?.date;
699
+ const activeSlots = data.dates.find((entry) => entry.date === activeDate)?.slots || [];
700
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: ["idk-availability", className].filter(Boolean).join(" "), children: [
701
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-availability__dates", children: data.dates.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
702
+ "button",
703
+ {
704
+ type: "button",
705
+ className: entry.date === activeDate ? "is-active" : void 0,
706
+ onClick: () => {
707
+ setSelectedDate(entry.date);
708
+ setSelectedTime(null);
709
+ },
710
+ children: entry.date
711
+ },
712
+ entry.date
713
+ )) }),
714
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-availability__slots", children: activeSlots.filter((slot) => slot.available).length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "idk-state", children: "No slots available." }) : activeSlots.filter((slot) => slot.available).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
715
+ "button",
716
+ {
717
+ type: "button",
718
+ className: selectedTime === slot.time ? "is-active" : void 0,
719
+ onClick: () => {
720
+ setSelectedTime(slot.time);
721
+ if (onSelect && activeDate) {
722
+ onSelect({
723
+ date: activeDate,
724
+ time: slot.time,
725
+ endTime: slot.endTime
726
+ });
727
+ }
728
+ },
729
+ children: slot.time
730
+ },
731
+ `${activeDate}-${slot.time}`
732
+ )) })
733
+ ] });
734
+ }
735
+
736
+ // src/components/ServicePicker.tsx
737
+ var import_jsx_runtime9 = require("react/jsx-runtime");
738
+ function ServicePicker({
739
+ services,
740
+ selectedId,
741
+ onSelect,
742
+ className
743
+ }) {
744
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: services.map((service) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
745
+ "button",
746
+ {
747
+ type: "button",
748
+ className: selectedId === service.id ? "is-active" : void 0,
749
+ onClick: () => onSelect?.(service),
750
+ children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: service.name }),
752
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("small", { children: [
753
+ service.duration,
754
+ " min"
755
+ ] })
756
+ ]
757
+ },
758
+ service.id
759
+ )) });
760
+ }
761
+
762
+ // src/components/StaffPicker.tsx
763
+ var import_jsx_runtime10 = require("react/jsx-runtime");
764
+ function StaffPicker({
765
+ staff,
766
+ selectedId,
767
+ onSelect,
768
+ className
769
+ }) {
770
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: staff.map((member) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
771
+ "button",
772
+ {
773
+ type: "button",
774
+ className: selectedId === member.id ? "is-active" : void 0,
775
+ onClick: () => onSelect?.(member),
776
+ children: [
777
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: member.name }),
778
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("small", { children: member.role || "Staff" })
779
+ ]
780
+ },
781
+ member.id
782
+ )) });
783
+ }
784
+
785
+ // src/components/DatePicker.tsx
786
+ var import_jsx_runtime11 = require("react/jsx-runtime");
787
+ function DatePicker({
788
+ dates,
789
+ selectedDate,
790
+ onSelect,
791
+ className
792
+ }) {
793
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: dates.map((date) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
794
+ "button",
795
+ {
796
+ type: "button",
797
+ className: selectedDate === date ? "is-active" : void 0,
798
+ onClick: () => onSelect?.(date),
799
+ children: date
800
+ },
801
+ date
802
+ )) });
803
+ }
804
+
805
+ // src/components/TimePicker.tsx
806
+ var import_jsx_runtime12 = require("react/jsx-runtime");
807
+ function TimePicker({
808
+ slots,
809
+ selectedTime,
810
+ onSelect,
811
+ className
812
+ }) {
813
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: ["idk-picker", className].filter(Boolean).join(" "), children: slots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
814
+ "button",
815
+ {
816
+ type: "button",
817
+ className: selectedTime === slot.time ? "is-active" : void 0,
818
+ disabled: !slot.available,
819
+ onClick: () => onSelect?.(slot),
820
+ children: slot.time
821
+ },
822
+ `${slot.time}-${slot.endTime}`
823
+ )) });
824
+ }
825
+ // Annotate the CommonJS export names for ESM import in node:
826
+ 0 && (module.exports = {
827
+ AvailabilityPicker,
828
+ BookingWidget,
829
+ ContactForm,
830
+ DatePicker,
831
+ PlatformProvider,
832
+ ServiceCard,
833
+ ServicePicker,
834
+ ServicesList,
835
+ StaffPicker,
836
+ TeamGrid,
837
+ TeamMember,
838
+ TimePicker,
839
+ useAvailability,
840
+ useBookingLookup,
841
+ useCancelBooking,
842
+ useCreateBooking,
843
+ useServices,
844
+ useTeam,
845
+ useTenant
846
+ });
847
+ //# sourceMappingURL=index.cjs.map