@bookr-cl/widget 0.0.1 → 0.1.1
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 +116 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.js +111 -6
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
DAY_NAMES: () => DAY_NAMES,
|
|
24
24
|
DAY_NAMES_FULL: () => DAY_NAMES_FULL,
|
|
25
25
|
DEFAULT_PROVIDER_COLORS: () => DEFAULT_PROVIDER_COLORS,
|
|
26
|
+
cancelMyBooking: () => cancelMyBooking,
|
|
26
27
|
clearClientToken: () => clearClientToken,
|
|
27
28
|
createBooking: () => createBooking,
|
|
28
29
|
createClient: () => createClient,
|
|
@@ -35,11 +36,15 @@ __export(index_exports, {
|
|
|
35
36
|
getMondayOf: () => getMondayOf,
|
|
36
37
|
getMyBookings: () => getMyBookings,
|
|
37
38
|
getMyPackages: () => getMyPackages,
|
|
39
|
+
getMyWaitlist: () => getMyWaitlist,
|
|
40
|
+
joinWaitlist: () => joinWaitlist,
|
|
41
|
+
leaveWaitlist: () => leaveWaitlist,
|
|
38
42
|
listLocations: () => listLocations,
|
|
39
43
|
listProviders: () => listProviders,
|
|
40
44
|
listServices: () => listServices,
|
|
41
45
|
loginWithGoogle: () => loginWithGoogle,
|
|
42
46
|
register: () => register,
|
|
47
|
+
rescheduleMyBooking: () => rescheduleMyBooking,
|
|
43
48
|
sendMagicLink: () => sendMagicLink,
|
|
44
49
|
setClientToken: () => setClientToken,
|
|
45
50
|
useBookingFlow: () => useBookingFlow,
|
|
@@ -186,6 +191,52 @@ async function getMyPackages(apiBase, token) {
|
|
|
186
191
|
const data = await res.json();
|
|
187
192
|
return data.data || [];
|
|
188
193
|
}
|
|
194
|
+
async function cancelMyBooking(apiBase, token, bookingId) {
|
|
195
|
+
const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/cancel`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: authHeaders(token)
|
|
198
|
+
});
|
|
199
|
+
const data = await res.json().catch(() => ({}));
|
|
200
|
+
if (!res.ok) return { ok: false, message: data.detail || "No se pudo cancelar la reserva." };
|
|
201
|
+
return { ok: true, booking: data };
|
|
202
|
+
}
|
|
203
|
+
async function rescheduleMyBooking(apiBase, token, bookingId, startTime, endTime) {
|
|
204
|
+
const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/reschedule`, {
|
|
205
|
+
method: "POST",
|
|
206
|
+
headers: authHeaders(token, true),
|
|
207
|
+
body: JSON.stringify({ start_time: startTime, end_time: endTime })
|
|
208
|
+
});
|
|
209
|
+
const data = await res.json().catch(() => ({}));
|
|
210
|
+
if (!res.ok) return { ok: false, message: data.detail || "No se pudo reprogramar la reserva." };
|
|
211
|
+
return { ok: true, booking: data };
|
|
212
|
+
}
|
|
213
|
+
async function joinWaitlist(apiBase, token, params) {
|
|
214
|
+
const res = await fetch(`${apiBase}/v1/me/waitlist`, {
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: authHeaders(token, true),
|
|
217
|
+
body: JSON.stringify({
|
|
218
|
+
service_id: params.serviceId,
|
|
219
|
+
location_id: params.locationId,
|
|
220
|
+
provider_id: params.providerId,
|
|
221
|
+
date: params.date,
|
|
222
|
+
class_session_id: params.classSessionId
|
|
223
|
+
})
|
|
224
|
+
});
|
|
225
|
+
const data = await res.json();
|
|
226
|
+
if (!res.ok) throw new Error(data.detail || "No se pudo unir a la lista de espera.");
|
|
227
|
+
return data;
|
|
228
|
+
}
|
|
229
|
+
async function getMyWaitlist(apiBase, token) {
|
|
230
|
+
const res = await fetch(`${apiBase}/v1/me/waitlist`, { headers: authHeaders(token) });
|
|
231
|
+
const data = await res.json();
|
|
232
|
+
return data.data || [];
|
|
233
|
+
}
|
|
234
|
+
async function leaveWaitlist(apiBase, token, entryId) {
|
|
235
|
+
await fetch(`${apiBase}/v1/me/waitlist/${entryId}`, {
|
|
236
|
+
method: "DELETE",
|
|
237
|
+
headers: authHeaders(token)
|
|
238
|
+
});
|
|
239
|
+
}
|
|
189
240
|
|
|
190
241
|
// src/clientAuth.ts
|
|
191
242
|
var TOKEN_KEY = "client_token";
|
|
@@ -240,11 +291,14 @@ var DEFAULT_PROVIDER_COLORS = [
|
|
|
240
291
|
// src/hooks/useBookingFlow.ts
|
|
241
292
|
var import_react = require("react");
|
|
242
293
|
var EMPTY_FORM = { firstName: "", lastName: "", email: "", phone: "", rut: "" };
|
|
294
|
+
function isValidPhone(phone) {
|
|
295
|
+
return phone.replace(/[^0-9]/g, "").length >= 8;
|
|
296
|
+
}
|
|
243
297
|
function validateClientForm(form) {
|
|
244
298
|
if (!form.firstName.trim()) return "Por favor ingresa tu nombre.";
|
|
245
299
|
if (!form.lastName.trim()) return "Por favor ingresa tu apellido.";
|
|
246
300
|
if (!form.email.trim() || !/\S+@\S+\.\S+/.test(form.email)) return "Por favor ingresa un email v\xE1lido.";
|
|
247
|
-
if (!form.phone.trim()) return "Por favor ingresa
|
|
301
|
+
if (!form.phone.trim() || !isValidPhone(form.phone)) return "Por favor ingresa un tel\xE9fono v\xE1lido.";
|
|
248
302
|
if (!form.rut.trim()) return "Por favor ingresa tu RUT.";
|
|
249
303
|
return null;
|
|
250
304
|
}
|
|
@@ -295,7 +349,8 @@ function useBookingFlow(opts) {
|
|
|
295
349
|
firstName: profile.first_name || f.firstName,
|
|
296
350
|
lastName: profile.last_name || f.lastName,
|
|
297
351
|
email: profile.email || f.email,
|
|
298
|
-
phone: profile.phone || f.phone
|
|
352
|
+
phone: profile.phone || f.phone,
|
|
353
|
+
rut: profile.rut || f.rut
|
|
299
354
|
}));
|
|
300
355
|
});
|
|
301
356
|
}, []);
|
|
@@ -394,11 +449,14 @@ function useBookingFlow(opts) {
|
|
|
394
449
|
// src/hooks/useClassSchedule.ts
|
|
395
450
|
var import_react2 = require("react");
|
|
396
451
|
var EMPTY_FORM2 = { firstName: "", lastName: "", email: "", phone: "", rut: "" };
|
|
452
|
+
function isValidPhone2(phone) {
|
|
453
|
+
return phone.replace(/[^0-9]/g, "").length >= 8;
|
|
454
|
+
}
|
|
397
455
|
function validateClassForm(form) {
|
|
398
456
|
if (!form.firstName.trim()) return "Por favor ingresa tu nombre.";
|
|
399
457
|
if (!form.lastName.trim()) return "Por favor ingresa tu apellido.";
|
|
400
458
|
if (!form.email.trim() || !/\S+@\S+\.\S+/.test(form.email)) return "Por favor ingresa un email v\xE1lido.";
|
|
401
|
-
if (!form.phone.trim()) return "Por favor ingresa
|
|
459
|
+
if (!form.phone.trim() || !isValidPhone2(form.phone)) return "Por favor ingresa un tel\xE9fono v\xE1lido.";
|
|
402
460
|
return null;
|
|
403
461
|
}
|
|
404
462
|
function buildClassBookingPayload(form, client, selectedSlot, svc, locationId) {
|
|
@@ -450,7 +508,8 @@ function useClassSchedule(opts) {
|
|
|
450
508
|
firstName: profile.first_name || f.firstName,
|
|
451
509
|
lastName: profile.last_name || f.lastName,
|
|
452
510
|
email: profile.email || f.email,
|
|
453
|
-
phone: profile.phone || f.phone
|
|
511
|
+
phone: profile.phone || f.phone,
|
|
512
|
+
rut: profile.rut || f.rut
|
|
454
513
|
}));
|
|
455
514
|
});
|
|
456
515
|
}, []);
|
|
@@ -668,7 +727,9 @@ function useClientAccount(opts) {
|
|
|
668
727
|
const [history, setHistory] = (0, import_react5.useState)([]);
|
|
669
728
|
const [packages, setPackages] = (0, import_react5.useState)([]);
|
|
670
729
|
const [loading, setLoading] = (0, import_react5.useState)(true);
|
|
671
|
-
(0, import_react5.
|
|
730
|
+
const [actionError, setActionError] = (0, import_react5.useState)("");
|
|
731
|
+
const [actionLoading, setActionLoading] = (0, import_react5.useState)(false);
|
|
732
|
+
const loadAll = () => {
|
|
672
733
|
const token = getClientToken();
|
|
673
734
|
if (!token) {
|
|
674
735
|
opts.onUnauthenticated();
|
|
@@ -686,19 +747,64 @@ function useClientAccount(opts) {
|
|
|
686
747
|
setPackages(pkgs);
|
|
687
748
|
setLoading(false);
|
|
688
749
|
}).catch(() => opts.onUnauthenticated());
|
|
750
|
+
};
|
|
751
|
+
(0, import_react5.useEffect)(() => {
|
|
752
|
+
loadAll();
|
|
689
753
|
}, []);
|
|
690
754
|
const handleLogout = () => {
|
|
691
755
|
clearClientToken();
|
|
692
756
|
opts.onLoggedOut();
|
|
693
757
|
};
|
|
758
|
+
const cancelBooking = async (bookingId) => {
|
|
759
|
+
const token = getClientToken();
|
|
760
|
+
if (!token) return;
|
|
761
|
+
setActionLoading(true);
|
|
762
|
+
setActionError("");
|
|
763
|
+
const result = await cancelMyBooking(opts.apiBase, token, bookingId);
|
|
764
|
+
if (!result.ok) {
|
|
765
|
+
setActionError(result.message);
|
|
766
|
+
setActionLoading(false);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
loadAll();
|
|
770
|
+
setActionLoading(false);
|
|
771
|
+
};
|
|
772
|
+
const rescheduleBooking = async (bookingId, startTime, endTime) => {
|
|
773
|
+
const token = getClientToken();
|
|
774
|
+
if (!token) return false;
|
|
775
|
+
setActionLoading(true);
|
|
776
|
+
setActionError("");
|
|
777
|
+
const result = await rescheduleMyBooking(opts.apiBase, token, bookingId, startTime, endTime);
|
|
778
|
+
setActionLoading(false);
|
|
779
|
+
if (!result.ok) {
|
|
780
|
+
setActionError(result.message);
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
loadAll();
|
|
784
|
+
return true;
|
|
785
|
+
};
|
|
694
786
|
const totalCredits = packages.reduce((s, p) => s + p.remaining_credits, 0);
|
|
695
|
-
return {
|
|
787
|
+
return {
|
|
788
|
+
profile,
|
|
789
|
+
upcoming,
|
|
790
|
+
history,
|
|
791
|
+
packages,
|
|
792
|
+
loading,
|
|
793
|
+
totalCredits,
|
|
794
|
+
handleLogout,
|
|
795
|
+
cancelBooking,
|
|
796
|
+
rescheduleBooking,
|
|
797
|
+
actionError,
|
|
798
|
+
setActionError,
|
|
799
|
+
actionLoading
|
|
800
|
+
};
|
|
696
801
|
}
|
|
697
802
|
// Annotate the CommonJS export names for ESM import in node:
|
|
698
803
|
0 && (module.exports = {
|
|
699
804
|
DAY_NAMES,
|
|
700
805
|
DAY_NAMES_FULL,
|
|
701
806
|
DEFAULT_PROVIDER_COLORS,
|
|
807
|
+
cancelMyBooking,
|
|
702
808
|
clearClientToken,
|
|
703
809
|
createBooking,
|
|
704
810
|
createClient,
|
|
@@ -711,11 +817,15 @@ function useClientAccount(opts) {
|
|
|
711
817
|
getMondayOf,
|
|
712
818
|
getMyBookings,
|
|
713
819
|
getMyPackages,
|
|
820
|
+
getMyWaitlist,
|
|
821
|
+
joinWaitlist,
|
|
822
|
+
leaveWaitlist,
|
|
714
823
|
listLocations,
|
|
715
824
|
listProviders,
|
|
716
825
|
listServices,
|
|
717
826
|
loginWithGoogle,
|
|
718
827
|
register,
|
|
828
|
+
rescheduleMyBooking,
|
|
719
829
|
sendMagicLink,
|
|
720
830
|
setClientToken,
|
|
721
831
|
useBookingFlow,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/clientAuth.ts","../src/dateUtils.ts","../src/hooks/useBookingFlow.ts","../src/hooks/useClassSchedule.ts","../src/hooks/useMagicLinkAuth.ts","../src/hooks/useRegisterForm.ts","../src/hooks/useClientAccount.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./api\";\nexport * from \"./clientAuth\";\nexport * from \"./dateUtils\";\nexport * from \"./hooks/useBookingFlow\";\nexport * from \"./hooks/useClassSchedule\";\nexport * from \"./hooks/useMagicLinkAuth\";\nexport * from \"./hooks/useRegisterForm\";\nexport * from \"./hooks/useClientAccount\";\n","import type {\n ApiConfig,\n BookingCreateInput,\n BookingRecord,\n BookingResult,\n ClientFormValues,\n ClientProfile,\n Location,\n PackageRecord,\n Provider,\n Service,\n Slot,\n} from \"./types\";\n\nfunction headers(cfg: ApiConfig, withJson = false): Record<string, string> {\n const h: Record<string, string> = { \"X-API-Key\": cfg.apiKey };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nfunction authHeaders(token: string, withJson = false): Record<string, string> {\n const h: Record<string, string> = { Authorization: `Bearer ${token}` };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nexport async function listLocations(cfg: ApiConfig): Promise<Location[]> {\n const res = await fetch(`${cfg.apiBase}/v1/locations`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listServices(cfg: ApiConfig, perPage = 100): Promise<Service[]> {\n const res = await fetch(`${cfg.apiBase}/v1/services?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listProviders(cfg: ApiConfig, perPage = 100): Promise<Provider[]> {\n const res = await fetch(`${cfg.apiBase}/v1/providers?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getAvailableSlots(\n cfg: ApiConfig,\n params: { locationId: string; startDate: string; serviceId?: string; providerId?: string }\n): Promise<Slot[]> {\n const q = new URLSearchParams({ location_id: params.locationId, start_date: params.startDate });\n if (params.serviceId) q.set(\"service_id\", params.serviceId);\n if (params.providerId) q.set(\"provider_id\", params.providerId);\n const res = await fetch(`${cfg.apiBase}/v1/available-slots?${q.toString()}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data?.slots || [];\n}\n\nexport async function createClient(cfg: ApiConfig, form: ClientFormValues): Promise<{ id: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/clients`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone,\n rut: form.rut || undefined,\n }),\n });\n if (!res.ok) throw new Error(\"Error registrando datos\");\n return res.json();\n}\n\nexport async function createBooking(\n cfg: ApiConfig,\n input: BookingCreateInput,\n clientToken?: string\n): Promise<BookingResult> {\n // A logged-in client's token takes priority so the server attributes the\n // booking to their account rather than treating it as anonymous — see\n // create_booking in routes/bookings.py, which ignores any client_id sent\n // by an unauthenticated caller.\n const reqHeaders = headers(cfg, true);\n if (clientToken) reqHeaders.Authorization = `Bearer ${clientToken}`;\n const res = await fetch(`${cfg.apiBase}/v1/bookings`, {\n method: \"POST\",\n headers: reqHeaders,\n body: JSON.stringify(input),\n });\n if (res.status === 409) {\n const data = await res.json().catch(() => ({}));\n return { ok: false, conflict: true, message: data.detail || \"Ese horario ya no está disponible.\" };\n }\n if (!res.ok) {\n return { ok: false, conflict: false, message: \"Error creando la reserva. Por favor intenta nuevamente.\" };\n }\n return { ok: true, booking: await res.json() };\n}\n\nexport async function sendMagicLink(\n cfg: ApiConfig,\n email: string,\n frontendUrl: string,\n profile?: { firstName?: string; lastName?: string; phone?: string; rut?: string }\n): Promise<void> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n frontend_url: frontendUrl,\n first_name: profile?.firstName,\n last_name: profile?.lastName,\n phone: profile?.phone,\n rut: profile?.rut,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error enviando el link.\");\n}\n\nexport async function verifyMagicLink(cfg: ApiConfig, token: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link/verify`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ token }),\n });\n const data = await res.json();\n if (!data.access_token) throw new Error(data.detail || \"Link inválido.\");\n return data;\n}\n\nexport async function loginWithGoogle(cfg: ApiConfig, credential: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/google`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ credential }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error con Google.\");\n return data;\n}\n\nexport async function register(\n cfg: ApiConfig,\n form: { firstName: string; lastName: string; email: string; phone?: string; password: string }\n): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/register`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone || undefined,\n password: form.password,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error al registrarse.\");\n return data;\n}\n\nexport async function getMe(apiBase: string, token: string): Promise<ClientProfile> {\n const res = await fetch(`${apiBase}/v1/me`, { headers: authHeaders(token) });\n return res.json();\n}\n\nexport async function getMyBookings(\n apiBase: string,\n token: string,\n params?: { upcoming?: boolean; perPage?: number }\n): Promise<BookingRecord[]> {\n const q = new URLSearchParams();\n if (params?.upcoming) q.set(\"upcoming\", \"true\");\n q.set(\"per_page\", String(params?.perPage ?? 20));\n const res = await fetch(`${apiBase}/v1/me/bookings?${q.toString()}`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getMyPackages(apiBase: string, token: string): Promise<PackageRecord[]> {\n const res = await fetch(`${apiBase}/v1/me/packages`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n","import { getMe } from \"./api\";\nimport type { ClientProfile } from \"./types\";\n\nconst TOKEN_KEY = \"client_token\";\n\nexport function getClientToken(): string | null {\n if (typeof window === \"undefined\") return null;\n return localStorage.getItem(TOKEN_KEY);\n}\n\nexport function setClientToken(token: string): void {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(TOKEN_KEY, token);\n}\n\nexport function clearClientToken(): void {\n if (typeof window === \"undefined\") return;\n localStorage.removeItem(TOKEN_KEY);\n}\n\nexport async function fetchMe(apiBase: string, token: string): Promise<ClientProfile | null> {\n try {\n return await getMe(apiBase, token);\n } catch {\n return null;\n }\n}\n","export function getMondayOf(date: Date): Date {\n const d = new Date(date);\n const day = (d.getDay() + 6) % 7;\n d.setDate(d.getDate() - day);\n d.setHours(0, 0, 0, 0);\n return d;\n}\n\nexport function dateStr(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n}\n\nexport function formatMonthRange(monday: Date): string {\n const sunday = new Date(monday);\n sunday.setDate(monday.getDate() + 6);\n const opts: Intl.DateTimeFormatOptions = { day: \"numeric\", month: \"long\" };\n const start = monday.toLocaleDateString(\"es-CL\", opts);\n const end = sunday.toLocaleDateString(\"es-CL\", opts);\n return `${start} – ${end}`;\n}\n\nexport const DAY_NAMES = [\"Lun\", \"Mar\", \"Mié\", \"Jue\", \"Vie\", \"Sáb\", \"Dom\"];\nexport const DAY_NAMES_FULL = [\"Lunes\", \"Martes\", \"Miércoles\", \"Jueves\", \"Viernes\", \"Sábado\", \"Domingo\"];\n\nexport interface ProviderColor {\n bg: string;\n border: string;\n text: string;\n}\n\nexport const DEFAULT_PROVIDER_COLORS: ProviderColor[] = [\n { bg: \"#e8f5f0\", border: \"#2bbf8e\", text: \"#1a7a5a\" },\n { bg: \"#eef0fb\", border: \"#6b7de8\", text: \"#3d4fa8\" },\n { bg: \"#fdf3e8\", border: \"#e8a24b\", text: \"#a0600a\" },\n { bg: \"#fdeef4\", border: \"#e86b9a\", text: \"#a03060\" },\n];\n","import { useEffect, useState } from \"react\";\nimport {\n createBooking,\n getAvailableSlots,\n listLocations,\n listProviders,\n listServices,\n} from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport type {\n ApiConfig,\n BookingRecord,\n ClientFormValues,\n ClientProfile,\n Location,\n Provider,\n Service,\n Slot,\n} from \"../types\";\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction validateClientForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim()) return \"Por favor ingresa tu teléfono.\";\n if (!form.rut.trim()) return \"Por favor ingresa tu RUT.\";\n return null;\n}\n\ninterface BuildBookingPayloadArgs {\n form: ClientFormValues;\n client: ClientProfile | null;\n selectedDate: string;\n selectedSlot: Slot;\n selectedSvc: string;\n selectedLoc: string;\n svcPrice?: string;\n defaultNotes?: string;\n overrides?: SubmitBookingOverrides;\n}\n\nfunction buildBookingPayload(args: BuildBookingPayloadArgs) {\n const { form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc } = args;\n const price = args.overrides?.price ?? args.svcPrice ?? \"0\";\n const notes = args.overrides?.notes ?? args.defaultNotes;\n return {\n start_time: `${selectedDate}T${selectedSlot.start_time}:00-04:00`,\n end_time: `${selectedDate}T${selectedSlot.end_time}:00-04:00`,\n service_id: selectedSvc,\n service_provider_id: selectedSlot.provider_id,\n location_id: selectedLoc,\n price,\n notes,\n // Ignored server-side unless a client/staff auth token proves the caller's\n // identity; for a logged-in client it's redundant but harmless to include.\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nexport interface UseBookingFlowOptions extends ApiConfig {\n onBookingComplete: (booking: BookingRecord) => void;\n /** Default notes attached to every booking (e.g. \"Reserva realizada desde sitio web X\"). */\n defaultNotes?: string;\n}\n\nexport interface SubmitBookingOverrides {\n /** Override the service's list price (e.g. magnolia's price-variant step). */\n price?: string;\n /** Override defaultNotes for this specific submission. */\n notes?: string;\n}\n\n/**\n * Shared booking-flow logic: catalog fetching (locations/services/providers/\n * slots), logged-in-client prefill, form validation, and submit-with-409-\n * handling. Does NOT own step navigation or app-specific filtering (e.g.\n * magnolia's category/price-variant logic) — those stay in the consuming\n * component, layered on top of the `services`/`providers` this hook returns.\n */\nexport function useBookingFlow(opts: UseBookingFlowOptions) {\n const [locations, setLocations] = useState<Location[]>([]);\n const [services, setServices] = useState<Service[]>([]);\n const [providers, setProviders] = useState<Provider[]>([]);\n const [slots, setSlots] = useState<Slot[]>([]);\n\n const [selectedLoc, setSelectedLoc] = useState(\"\");\n const [selectedSvc, setSelectedSvc] = useState(\"\");\n const [selectedProv, setSelectedProv] = useState(\"\");\n const [selectedDate, setSelectedDate] = useState(\"\");\n const [selectedSlot, setSelectedSlot] = useState<Slot | null>(null);\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n // Prefill from a logged-in client session, if any.\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n listLocations(opts)\n .then(setLocations)\n .catch(() => setError(\"Error cargando sucursales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (!selectedLoc) return;\n listServices(opts)\n .then(setServices)\n .catch(() => setError(\"Error cargando servicios\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc]);\n\n useEffect(() => {\n if (!selectedSvc) return;\n listProviders(opts)\n .then(setProviders)\n .catch(() => setError(\"Error cargando profesionales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedSvc]);\n\n useEffect(() => {\n if (!selectedLoc || !selectedDate) return;\n setLoading(true);\n getAvailableSlots(opts, {\n locationId: selectedLoc,\n startDate: selectedDate,\n serviceId: selectedSvc || undefined,\n providerId: selectedProv || undefined,\n })\n .then(s => { setSlots(s); setLoading(false); })\n .catch(() => { setError(\"Error cargando horarios\"); setLoading(false); });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc, selectedSvc, selectedProv, selectedDate]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (overrides?: SubmitBookingOverrides) => {\n if (!selectedSlot) return;\n const validationError = validateClientForm(form);\n if (validationError) { setError(validationError); return; }\n\n setLoading(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.id === selectedSvc);\n const token = getClientToken();\n const payload = buildBookingPayload({\n form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc, overrides,\n svcPrice: svc?.price, defaultNotes: opts.defaultNotes,\n });\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar la reserva\");\n setLoading(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(result.message);\n if (result.conflict) setSelectedSlot(null);\n setLoading(false);\n return;\n }\n opts.onBookingComplete(result.booking);\n }\n\n return {\n locations, services, providers, slots,\n selectedLoc, setSelectedLoc,\n selectedSvc, setSelectedSvc,\n selectedProv, setSelectedProv,\n selectedDate, setSelectedDate,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n loading, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { createBooking, getAvailableSlots, listLocations, listServices } from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport { dateStr, DEFAULT_PROVIDER_COLORS, getMondayOf, type ProviderColor } from \"../dateUtils\";\nimport type { ApiConfig, ClientFormValues, ClientProfile, Service, Slot } from \"../types\";\n\nexport interface SelectedClassSlot {\n slot: Slot;\n date: string;\n}\n\nexport interface UseClassScheduleOptions extends ApiConfig {\n onBookingComplete: () => void;\n colors?: ProviderColor[];\n}\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction validateClassForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim()) return \"Por favor ingresa tu teléfono.\";\n return null;\n}\n\nfunction buildClassBookingPayload(\n form: ClientFormValues,\n client: ClientProfile | null,\n selectedSlot: SelectedClassSlot,\n svc: Service | undefined,\n locationId: string\n) {\n return {\n start_time: `${selectedSlot.date}T${selectedSlot.slot.start_time}:00-04:00`,\n end_time: `${selectedSlot.date}T${selectedSlot.slot.end_time}:00-04:00`,\n service_id: svc?.id ?? \"\",\n service_provider_id: selectedSlot.slot.provider_id,\n location_id: locationId,\n price: svc?.price || \"0\",\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nfunction resolveConflictMessage(result: { conflict: boolean; message: string }): string {\n return result.conflict\n ? \"Ese cupo ya fue tomado por otra persona. Cierra esta ventana y elige otro horario.\"\n : result.message;\n}\n\n/**\n * Shared weekly group-class schedule logic: week-of-slots fetching, provider\n * color-palette assignment, and submit-with-409-handling. The week-grid table\n * JSX and modal markup stay in the consuming component — this hook only owns\n * data and state transitions.\n */\nexport function useClassSchedule(opts: UseClassScheduleOptions) {\n const colors = opts.colors ?? DEFAULT_PROVIDER_COLORS;\n\n const [monday, setMonday] = useState<Date>(() => getMondayOf(new Date()));\n const [slotsByDay, setSlotsByDay] = useState<Record<string, Slot[]>>({});\n const [services, setServices] = useState<Service[]>([]);\n const [locationId, setLocationId] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [selectedSlot, setSelectedSlot] = useState<SelectedClassSlot | null>(null);\n const [providerColorMap, setProviderColorMap] = useState<Record<string, ProviderColor>>({});\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n useEffect(() => {\n Promise.all([listLocations(opts), listServices(opts, 50)])\n .then(([locs, svcs]) => {\n if (locs.length) setLocationId(locs[0].id);\n setServices(svcs);\n })\n .catch(() => setError(\"Error cargando datos\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const loadWeek = useCallback(async (mon: Date, locId: string) => {\n if (!locId) return;\n setLoading(true);\n const days = Array.from({ length: 7 }, (_, i) => {\n const d = new Date(mon);\n d.setDate(mon.getDate() + i);\n return d;\n });\n\n const results = await Promise.all(\n days.map(async d => {\n const ds = dateStr(d);\n try {\n const slots = await getAvailableSlots(opts, { locationId: locId, startDate: ds });\n return { date: ds, slots };\n } catch {\n return { date: ds, slots: [] as Slot[] };\n }\n })\n );\n\n const map: Record<string, Slot[]> = {};\n const colorMap: Record<string, ProviderColor> = {};\n let colorIdx = 0;\n results.forEach(({ date, slots }) => {\n map[date] = slots;\n slots.forEach(s => {\n if (!colorMap[s.provider_id]) {\n colorMap[s.provider_id] = colors[colorIdx % colors.length];\n colorIdx++;\n }\n });\n });\n\n setSlotsByDay(map);\n setProviderColorMap(prev => ({ ...prev, ...colorMap }));\n setLoading(false);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (locationId) loadWeek(monday, locationId);\n }, [monday, locationId, loadWeek]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!selectedSlot) return;\n const validationError = validateClassForm(form);\n if (validationError) { setError(validationError); return; }\n setSubmitting(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.name === selectedSlot.slot.service_name);\n const token = getClientToken();\n const payload = buildClassBookingPayload(form, client, selectedSlot, svc, locationId);\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar\");\n setSubmitting(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(resolveConflictMessage(result));\n setSubmitting(false);\n return;\n }\n opts.onBookingComplete();\n }\n\n return {\n monday, setMonday,\n slotsByDay, loading, providerColorMap,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n submitting, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useState } from \"react\";\nimport { loginWithGoogle, sendMagicLink } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\ndeclare global {\n interface Window {\n google?: {\n accounts: {\n id: {\n initialize: (config: { client_id: string; callback: (response: { credential: string }) => void }) => void;\n prompt: () => void;\n };\n };\n };\n }\n}\n\nexport interface UseMagicLinkAuthOptions extends ApiConfig {\n googleClientId?: string;\n frontendUrl: string;\n onGoogleLoggedIn: () => void;\n}\n\nexport type MagicLinkStep = \"email\" | \"sent\";\n\nexport function useMagicLinkAuth(opts: UseMagicLinkAuthOptions) {\n const [email, setEmail] = useState(\"\");\n // Optional — only relevant on a signup-style form (e.g. registro pages)\n // that collects a bit more than login does. Login pages can ignore these.\n const [firstName, setFirstName] = useState(\"\");\n const [lastName, setLastName] = useState(\"\");\n const [phone, setPhone] = useState(\"\");\n const [rut, setRut] = useState(\"\");\n const [step, setStep] = useState<MagicLinkStep>(\"email\");\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const handleMagicLink = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!email.trim() || !/\\S+@\\S+\\.\\S+/.test(email)) {\n setError(\"Ingresa un email válido.\");\n return;\n }\n setLoading(true);\n setError(\"\");\n try {\n await sendMagicLink(opts, email, opts.frontendUrl, {\n firstName: firstName || undefined,\n lastName: lastName || undefined,\n phone: phone || undefined,\n rut: rut || undefined,\n });\n setStep(\"sent\");\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error enviando el link.\");\n } finally {\n setLoading(false);\n }\n };\n\n const handleGoogle = () => {\n if (!opts.googleClientId || typeof window === \"undefined\" || !window.google) return;\n window.google.accounts.id.initialize({\n client_id: opts.googleClientId,\n callback: async (response: { credential: string }) => {\n setLoading(true);\n try {\n const data = await loginWithGoogle(opts, response.credential);\n setClientToken(data.access_token);\n opts.onGoogleLoggedIn();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error con Google.\");\n setLoading(false);\n }\n },\n });\n window.google.accounts.id.prompt();\n };\n\n const reset = () => {\n setStep(\"email\");\n setError(\"\");\n };\n\n return {\n email, setEmail,\n firstName, setFirstName,\n lastName, setLastName,\n phone, setPhone,\n rut, setRut,\n step, error, loading, handleMagicLink, handleGoogle, reset,\n };\n}\n","import { useState } from \"react\";\nimport { register } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\nexport interface RegisterFormValues {\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n password: string;\n confirm: string;\n}\n\nconst EMPTY: RegisterFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", password: \"\", confirm: \"\" };\n\nexport interface UseRegisterFormOptions extends ApiConfig {\n onRegistered: () => void;\n}\n\nexport function useRegisterForm(opts: UseRegisterFormOptions) {\n const [form, setForm] = useState<RegisterFormValues>(EMPTY);\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const updateField = (fields: Partial<RegisterFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const handleSubmit = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!form.firstName.trim()) { setError(\"Ingresa tu nombre.\"); return; }\n if (!form.lastName.trim()) { setError(\"Ingresa tu apellido.\"); return; }\n if (!form.email.trim()) { setError(\"Ingresa tu email.\"); return; }\n if (form.password.length < 6) { setError(\"La contraseña debe tener al menos 6 caracteres.\"); return; }\n if (form.password !== form.confirm) { setError(\"Las contraseñas no coinciden.\"); return; }\n setLoading(true);\n setError(\"\");\n try {\n const data = await register(opts, form);\n setClientToken(data.access_token);\n opts.onRegistered();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al registrarse.\");\n } finally {\n setLoading(false);\n }\n };\n\n return { form, updateField, error, loading, handleSubmit };\n}\n","import { useEffect, useState } from \"react\";\nimport { getMe, getMyBookings, getMyPackages } from \"../api\";\nimport { clearClientToken, getClientToken } from \"../clientAuth\";\nimport type { BookingRecord, ClientProfile, PackageRecord } from \"../types\";\n\nexport interface UseClientAccountOptions {\n apiBase: string;\n onUnauthenticated: () => void;\n onLoggedOut: () => void;\n}\n\nexport function useClientAccount(opts: UseClientAccountOptions) {\n const [profile, setProfile] = useState<ClientProfile | null>(null);\n const [upcoming, setUpcoming] = useState<BookingRecord[]>([]);\n const [history, setHistory] = useState<BookingRecord[]>([]);\n const [packages, setPackages] = useState<PackageRecord[]>([]);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n const token = getClientToken();\n if (!token) {\n opts.onUnauthenticated();\n return;\n }\n Promise.all([\n getMe(opts.apiBase, token),\n getMyBookings(opts.apiBase, token, { upcoming: true, perPage: 10 }),\n getMyBookings(opts.apiBase, token, { perPage: 20 }),\n getMyPackages(opts.apiBase, token),\n ])\n .then(([prof, up, hist, pkgs]) => {\n setProfile(prof);\n setUpcoming(up);\n setHistory(hist);\n setPackages(pkgs);\n setLoading(false);\n })\n .catch(() => opts.onUnauthenticated());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const handleLogout = () => {\n clearClientToken();\n opts.onLoggedOut();\n };\n\n const totalCredits = packages.reduce((s, p) => s + p.remaining_credits, 0);\n\n return { profile, upcoming, history, packages, loading, totalCredits, handleLogout };\n}\n"],"mappings":"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","names":["import_react","EMPTY_FORM","import_react","import_react","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/clientAuth.ts","../src/dateUtils.ts","../src/hooks/useBookingFlow.ts","../src/hooks/useClassSchedule.ts","../src/hooks/useMagicLinkAuth.ts","../src/hooks/useRegisterForm.ts","../src/hooks/useClientAccount.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./api\";\nexport * from \"./clientAuth\";\nexport * from \"./dateUtils\";\nexport * from \"./hooks/useBookingFlow\";\nexport * from \"./hooks/useClassSchedule\";\nexport * from \"./hooks/useMagicLinkAuth\";\nexport * from \"./hooks/useRegisterForm\";\nexport * from \"./hooks/useClientAccount\";\n","import type {\n ApiConfig,\n BookingCreateInput,\n BookingRecord,\n BookingResult,\n ClientFormValues,\n ClientProfile,\n Location,\n PackageRecord,\n Provider,\n Service,\n Slot,\n WaitlistEntry,\n} from \"./types\";\n\nfunction headers(cfg: ApiConfig, withJson = false): Record<string, string> {\n const h: Record<string, string> = { \"X-API-Key\": cfg.apiKey };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nfunction authHeaders(token: string, withJson = false): Record<string, string> {\n const h: Record<string, string> = { Authorization: `Bearer ${token}` };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nexport async function listLocations(cfg: ApiConfig): Promise<Location[]> {\n const res = await fetch(`${cfg.apiBase}/v1/locations`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listServices(cfg: ApiConfig, perPage = 100): Promise<Service[]> {\n const res = await fetch(`${cfg.apiBase}/v1/services?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listProviders(cfg: ApiConfig, perPage = 100): Promise<Provider[]> {\n const res = await fetch(`${cfg.apiBase}/v1/providers?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getAvailableSlots(\n cfg: ApiConfig,\n params: { locationId: string; startDate: string; serviceId?: string; providerId?: string }\n): Promise<Slot[]> {\n const q = new URLSearchParams({ location_id: params.locationId, start_date: params.startDate });\n if (params.serviceId) q.set(\"service_id\", params.serviceId);\n if (params.providerId) q.set(\"provider_id\", params.providerId);\n const res = await fetch(`${cfg.apiBase}/v1/available-slots?${q.toString()}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data?.slots || [];\n}\n\nexport async function createClient(cfg: ApiConfig, form: ClientFormValues): Promise<{ id: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/clients`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone,\n rut: form.rut || undefined,\n }),\n });\n if (!res.ok) throw new Error(\"Error registrando datos\");\n return res.json();\n}\n\nexport async function createBooking(\n cfg: ApiConfig,\n input: BookingCreateInput,\n clientToken?: string\n): Promise<BookingResult> {\n // A logged-in client's token takes priority so the server attributes the\n // booking to their account rather than treating it as anonymous — see\n // create_booking in routes/bookings.py, which ignores any client_id sent\n // by an unauthenticated caller.\n const reqHeaders = headers(cfg, true);\n if (clientToken) reqHeaders.Authorization = `Bearer ${clientToken}`;\n const res = await fetch(`${cfg.apiBase}/v1/bookings`, {\n method: \"POST\",\n headers: reqHeaders,\n body: JSON.stringify(input),\n });\n if (res.status === 409) {\n const data = await res.json().catch(() => ({}));\n return { ok: false, conflict: true, message: data.detail || \"Ese horario ya no está disponible.\" };\n }\n if (!res.ok) {\n return { ok: false, conflict: false, message: \"Error creando la reserva. Por favor intenta nuevamente.\" };\n }\n return { ok: true, booking: await res.json() };\n}\n\nexport async function sendMagicLink(\n cfg: ApiConfig,\n email: string,\n frontendUrl: string,\n profile?: { firstName?: string; lastName?: string; phone?: string; rut?: string }\n): Promise<void> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n frontend_url: frontendUrl,\n first_name: profile?.firstName,\n last_name: profile?.lastName,\n phone: profile?.phone,\n rut: profile?.rut,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error enviando el link.\");\n}\n\nexport async function verifyMagicLink(cfg: ApiConfig, token: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link/verify`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ token }),\n });\n const data = await res.json();\n if (!data.access_token) throw new Error(data.detail || \"Link inválido.\");\n return data;\n}\n\nexport async function loginWithGoogle(cfg: ApiConfig, credential: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/google`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ credential }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error con Google.\");\n return data;\n}\n\nexport async function register(\n cfg: ApiConfig,\n form: { firstName: string; lastName: string; email: string; phone?: string; password: string }\n): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/register`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone || undefined,\n password: form.password,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error al registrarse.\");\n return data;\n}\n\nexport async function getMe(apiBase: string, token: string): Promise<ClientProfile> {\n const res = await fetch(`${apiBase}/v1/me`, { headers: authHeaders(token) });\n return res.json();\n}\n\nexport async function getMyBookings(\n apiBase: string,\n token: string,\n params?: { upcoming?: boolean; perPage?: number }\n): Promise<BookingRecord[]> {\n const q = new URLSearchParams();\n if (params?.upcoming) q.set(\"upcoming\", \"true\");\n q.set(\"per_page\", String(params?.perPage ?? 20));\n const res = await fetch(`${apiBase}/v1/me/bookings?${q.toString()}`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getMyPackages(apiBase: string, token: string): Promise<PackageRecord[]> {\n const res = await fetch(`${apiBase}/v1/me/packages`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\n/** Result of a client cancelling/rescheduling their own booking — callers\n * branch on `.ok` to show the server's specific reason (cutoff window,\n * overlap, etc.) instead of a generic error. */\nexport type MyBookingActionResult =\n | { ok: true; booking: BookingRecord }\n | { ok: false; message: string };\n\nexport async function cancelMyBooking(\n apiBase: string, token: string, bookingId: string\n): Promise<MyBookingActionResult> {\n const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/cancel`, {\n method: \"POST\",\n headers: authHeaders(token),\n });\n const data = await res.json().catch(() => ({}));\n if (!res.ok) return { ok: false, message: data.detail || \"No se pudo cancelar la reserva.\" };\n return { ok: true, booking: data };\n}\n\nexport async function rescheduleMyBooking(\n apiBase: string, token: string, bookingId: string, startTime: string, endTime: string\n): Promise<MyBookingActionResult> {\n const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/reschedule`, {\n method: \"POST\",\n headers: authHeaders(token, true),\n body: JSON.stringify({ start_time: startTime, end_time: endTime }),\n });\n const data = await res.json().catch(() => ({}));\n if (!res.ok) return { ok: false, message: data.detail || \"No se pudo reprogramar la reserva.\" };\n return { ok: true, booking: data };\n}\n\nexport async function joinWaitlist(\n apiBase: string,\n token: string,\n params: { serviceId: string; locationId: string; providerId?: string; date?: string; classSessionId?: string }\n): Promise<WaitlistEntry> {\n const res = await fetch(`${apiBase}/v1/me/waitlist`, {\n method: \"POST\",\n headers: authHeaders(token, true),\n body: JSON.stringify({\n service_id: params.serviceId,\n location_id: params.locationId,\n provider_id: params.providerId,\n date: params.date,\n class_session_id: params.classSessionId,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"No se pudo unir a la lista de espera.\");\n return data;\n}\n\nexport async function getMyWaitlist(apiBase: string, token: string): Promise<WaitlistEntry[]> {\n const res = await fetch(`${apiBase}/v1/me/waitlist`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function leaveWaitlist(apiBase: string, token: string, entryId: string): Promise<void> {\n await fetch(`${apiBase}/v1/me/waitlist/${entryId}`, {\n method: \"DELETE\",\n headers: authHeaders(token),\n });\n}\n","import { getMe } from \"./api\";\nimport type { ClientProfile } from \"./types\";\n\nconst TOKEN_KEY = \"client_token\";\n\nexport function getClientToken(): string | null {\n if (typeof window === \"undefined\") return null;\n return localStorage.getItem(TOKEN_KEY);\n}\n\nexport function setClientToken(token: string): void {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(TOKEN_KEY, token);\n}\n\nexport function clearClientToken(): void {\n if (typeof window === \"undefined\") return;\n localStorage.removeItem(TOKEN_KEY);\n}\n\nexport async function fetchMe(apiBase: string, token: string): Promise<ClientProfile | null> {\n try {\n return await getMe(apiBase, token);\n } catch {\n return null;\n }\n}\n","export function getMondayOf(date: Date): Date {\n const d = new Date(date);\n const day = (d.getDay() + 6) % 7;\n d.setDate(d.getDate() - day);\n d.setHours(0, 0, 0, 0);\n return d;\n}\n\nexport function dateStr(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n}\n\nexport function formatMonthRange(monday: Date): string {\n const sunday = new Date(monday);\n sunday.setDate(monday.getDate() + 6);\n const opts: Intl.DateTimeFormatOptions = { day: \"numeric\", month: \"long\" };\n const start = monday.toLocaleDateString(\"es-CL\", opts);\n const end = sunday.toLocaleDateString(\"es-CL\", opts);\n return `${start} – ${end}`;\n}\n\nexport const DAY_NAMES = [\"Lun\", \"Mar\", \"Mié\", \"Jue\", \"Vie\", \"Sáb\", \"Dom\"];\nexport const DAY_NAMES_FULL = [\"Lunes\", \"Martes\", \"Miércoles\", \"Jueves\", \"Viernes\", \"Sábado\", \"Domingo\"];\n\nexport interface ProviderColor {\n bg: string;\n border: string;\n text: string;\n}\n\nexport const DEFAULT_PROVIDER_COLORS: ProviderColor[] = [\n { bg: \"#e8f5f0\", border: \"#2bbf8e\", text: \"#1a7a5a\" },\n { bg: \"#eef0fb\", border: \"#6b7de8\", text: \"#3d4fa8\" },\n { bg: \"#fdf3e8\", border: \"#e8a24b\", text: \"#a0600a\" },\n { bg: \"#fdeef4\", border: \"#e86b9a\", text: \"#a03060\" },\n];\n","import { useEffect, useState } from \"react\";\nimport {\n createBooking,\n getAvailableSlots,\n listLocations,\n listProviders,\n listServices,\n} from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport type {\n ApiConfig,\n BookingRecord,\n ClientFormValues,\n ClientProfile,\n Location,\n Provider,\n Service,\n Slot,\n} from \"../types\";\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction isValidPhone(phone: string): boolean {\n return phone.replace(/[^0-9]/g, \"\").length >= 8;\n}\n\nfunction validateClientForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim() || !isValidPhone(form.phone)) return \"Por favor ingresa un teléfono válido.\";\n if (!form.rut.trim()) return \"Por favor ingresa tu RUT.\";\n return null;\n}\n\ninterface BuildBookingPayloadArgs {\n form: ClientFormValues;\n client: ClientProfile | null;\n selectedDate: string;\n selectedSlot: Slot;\n selectedSvc: string;\n selectedLoc: string;\n svcPrice?: string;\n defaultNotes?: string;\n overrides?: SubmitBookingOverrides;\n}\n\nfunction buildBookingPayload(args: BuildBookingPayloadArgs) {\n const { form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc } = args;\n const price = args.overrides?.price ?? args.svcPrice ?? \"0\";\n const notes = args.overrides?.notes ?? args.defaultNotes;\n return {\n start_time: `${selectedDate}T${selectedSlot.start_time}:00-04:00`,\n end_time: `${selectedDate}T${selectedSlot.end_time}:00-04:00`,\n service_id: selectedSvc,\n service_provider_id: selectedSlot.provider_id,\n location_id: selectedLoc,\n price,\n notes,\n // Ignored server-side unless a client/staff auth token proves the caller's\n // identity; for a logged-in client it's redundant but harmless to include.\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nexport interface UseBookingFlowOptions extends ApiConfig {\n onBookingComplete: (booking: BookingRecord) => void;\n /** Default notes attached to every booking (e.g. \"Reserva realizada desde sitio web X\"). */\n defaultNotes?: string;\n}\n\nexport interface SubmitBookingOverrides {\n /** Override the service's list price (e.g. magnolia's price-variant step). */\n price?: string;\n /** Override defaultNotes for this specific submission. */\n notes?: string;\n}\n\n/**\n * Shared booking-flow logic: catalog fetching (locations/services/providers/\n * slots), logged-in-client prefill, form validation, and submit-with-409-\n * handling. Does NOT own step navigation or app-specific filtering (e.g.\n * magnolia's category/price-variant logic) — those stay in the consuming\n * component, layered on top of the `services`/`providers` this hook returns.\n */\nexport function useBookingFlow(opts: UseBookingFlowOptions) {\n const [locations, setLocations] = useState<Location[]>([]);\n const [services, setServices] = useState<Service[]>([]);\n const [providers, setProviders] = useState<Provider[]>([]);\n const [slots, setSlots] = useState<Slot[]>([]);\n\n const [selectedLoc, setSelectedLoc] = useState(\"\");\n const [selectedSvc, setSelectedSvc] = useState(\"\");\n const [selectedProv, setSelectedProv] = useState(\"\");\n const [selectedDate, setSelectedDate] = useState(\"\");\n const [selectedSlot, setSelectedSlot] = useState<Slot | null>(null);\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n // Prefill from a logged-in client session, if any.\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n rut: profile.rut || f.rut,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n listLocations(opts)\n .then(setLocations)\n .catch(() => setError(\"Error cargando sucursales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (!selectedLoc) return;\n listServices(opts)\n .then(setServices)\n .catch(() => setError(\"Error cargando servicios\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc]);\n\n useEffect(() => {\n if (!selectedSvc) return;\n listProviders(opts)\n .then(setProviders)\n .catch(() => setError(\"Error cargando profesionales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedSvc]);\n\n useEffect(() => {\n if (!selectedLoc || !selectedDate) return;\n setLoading(true);\n getAvailableSlots(opts, {\n locationId: selectedLoc,\n startDate: selectedDate,\n serviceId: selectedSvc || undefined,\n providerId: selectedProv || undefined,\n })\n .then(s => { setSlots(s); setLoading(false); })\n .catch(() => { setError(\"Error cargando horarios\"); setLoading(false); });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc, selectedSvc, selectedProv, selectedDate]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (overrides?: SubmitBookingOverrides) => {\n if (!selectedSlot) return;\n const validationError = validateClientForm(form);\n if (validationError) { setError(validationError); return; }\n\n setLoading(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.id === selectedSvc);\n const token = getClientToken();\n const payload = buildBookingPayload({\n form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc, overrides,\n svcPrice: svc?.price, defaultNotes: opts.defaultNotes,\n });\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar la reserva\");\n setLoading(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(result.message);\n if (result.conflict) setSelectedSlot(null);\n setLoading(false);\n return;\n }\n opts.onBookingComplete(result.booking);\n }\n\n return {\n locations, services, providers, slots,\n selectedLoc, setSelectedLoc,\n selectedSvc, setSelectedSvc,\n selectedProv, setSelectedProv,\n selectedDate, setSelectedDate,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n loading, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { createBooking, getAvailableSlots, listLocations, listServices } from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport { dateStr, DEFAULT_PROVIDER_COLORS, getMondayOf, type ProviderColor } from \"../dateUtils\";\nimport type { ApiConfig, ClientFormValues, ClientProfile, Service, Slot } from \"../types\";\n\nexport interface SelectedClassSlot {\n slot: Slot;\n date: string;\n}\n\nexport interface UseClassScheduleOptions extends ApiConfig {\n onBookingComplete: () => void;\n colors?: ProviderColor[];\n}\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction isValidPhone(phone: string): boolean {\n return phone.replace(/[^0-9]/g, \"\").length >= 8;\n}\n\nfunction validateClassForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim() || !isValidPhone(form.phone)) return \"Por favor ingresa un teléfono válido.\";\n return null;\n}\n\nfunction buildClassBookingPayload(\n form: ClientFormValues,\n client: ClientProfile | null,\n selectedSlot: SelectedClassSlot,\n svc: Service | undefined,\n locationId: string\n) {\n return {\n start_time: `${selectedSlot.date}T${selectedSlot.slot.start_time}:00-04:00`,\n end_time: `${selectedSlot.date}T${selectedSlot.slot.end_time}:00-04:00`,\n service_id: svc?.id ?? \"\",\n service_provider_id: selectedSlot.slot.provider_id,\n location_id: locationId,\n price: svc?.price || \"0\",\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nfunction resolveConflictMessage(result: { conflict: boolean; message: string }): string {\n return result.conflict\n ? \"Ese cupo ya fue tomado por otra persona. Cierra esta ventana y elige otro horario.\"\n : result.message;\n}\n\n/**\n * Shared weekly group-class schedule logic: week-of-slots fetching, provider\n * color-palette assignment, and submit-with-409-handling. The week-grid table\n * JSX and modal markup stay in the consuming component — this hook only owns\n * data and state transitions.\n */\nexport function useClassSchedule(opts: UseClassScheduleOptions) {\n const colors = opts.colors ?? DEFAULT_PROVIDER_COLORS;\n\n const [monday, setMonday] = useState<Date>(() => getMondayOf(new Date()));\n const [slotsByDay, setSlotsByDay] = useState<Record<string, Slot[]>>({});\n const [services, setServices] = useState<Service[]>([]);\n const [locationId, setLocationId] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [selectedSlot, setSelectedSlot] = useState<SelectedClassSlot | null>(null);\n const [providerColorMap, setProviderColorMap] = useState<Record<string, ProviderColor>>({});\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n useEffect(() => {\n Promise.all([listLocations(opts), listServices(opts, 50)])\n .then(([locs, svcs]) => {\n if (locs.length) setLocationId(locs[0].id);\n setServices(svcs);\n })\n .catch(() => setError(\"Error cargando datos\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n rut: profile.rut || f.rut,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const loadWeek = useCallback(async (mon: Date, locId: string) => {\n if (!locId) return;\n setLoading(true);\n const days = Array.from({ length: 7 }, (_, i) => {\n const d = new Date(mon);\n d.setDate(mon.getDate() + i);\n return d;\n });\n\n const results = await Promise.all(\n days.map(async d => {\n const ds = dateStr(d);\n try {\n const slots = await getAvailableSlots(opts, { locationId: locId, startDate: ds });\n return { date: ds, slots };\n } catch {\n return { date: ds, slots: [] as Slot[] };\n }\n })\n );\n\n const map: Record<string, Slot[]> = {};\n const colorMap: Record<string, ProviderColor> = {};\n let colorIdx = 0;\n results.forEach(({ date, slots }) => {\n map[date] = slots;\n slots.forEach(s => {\n if (!colorMap[s.provider_id]) {\n colorMap[s.provider_id] = colors[colorIdx % colors.length];\n colorIdx++;\n }\n });\n });\n\n setSlotsByDay(map);\n setProviderColorMap(prev => ({ ...prev, ...colorMap }));\n setLoading(false);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (locationId) loadWeek(monday, locationId);\n }, [monday, locationId, loadWeek]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!selectedSlot) return;\n const validationError = validateClassForm(form);\n if (validationError) { setError(validationError); return; }\n setSubmitting(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.name === selectedSlot.slot.service_name);\n const token = getClientToken();\n const payload = buildClassBookingPayload(form, client, selectedSlot, svc, locationId);\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar\");\n setSubmitting(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(resolveConflictMessage(result));\n setSubmitting(false);\n return;\n }\n opts.onBookingComplete();\n }\n\n return {\n monday, setMonday,\n slotsByDay, loading, providerColorMap,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n submitting, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useState } from \"react\";\nimport { loginWithGoogle, sendMagicLink } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\ndeclare global {\n interface Window {\n google?: {\n accounts: {\n id: {\n initialize: (config: { client_id: string; callback: (response: { credential: string }) => void }) => void;\n prompt: () => void;\n };\n };\n };\n }\n}\n\nexport interface UseMagicLinkAuthOptions extends ApiConfig {\n googleClientId?: string;\n frontendUrl: string;\n onGoogleLoggedIn: () => void;\n}\n\nexport type MagicLinkStep = \"email\" | \"sent\";\n\nexport function useMagicLinkAuth(opts: UseMagicLinkAuthOptions) {\n const [email, setEmail] = useState(\"\");\n // Optional — only relevant on a signup-style form (e.g. registro pages)\n // that collects a bit more than login does. Login pages can ignore these.\n const [firstName, setFirstName] = useState(\"\");\n const [lastName, setLastName] = useState(\"\");\n const [phone, setPhone] = useState(\"\");\n const [rut, setRut] = useState(\"\");\n const [step, setStep] = useState<MagicLinkStep>(\"email\");\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const handleMagicLink = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!email.trim() || !/\\S+@\\S+\\.\\S+/.test(email)) {\n setError(\"Ingresa un email válido.\");\n return;\n }\n setLoading(true);\n setError(\"\");\n try {\n await sendMagicLink(opts, email, opts.frontendUrl, {\n firstName: firstName || undefined,\n lastName: lastName || undefined,\n phone: phone || undefined,\n rut: rut || undefined,\n });\n setStep(\"sent\");\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error enviando el link.\");\n } finally {\n setLoading(false);\n }\n };\n\n const handleGoogle = () => {\n if (!opts.googleClientId || typeof window === \"undefined\" || !window.google) return;\n window.google.accounts.id.initialize({\n client_id: opts.googleClientId,\n callback: async (response: { credential: string }) => {\n setLoading(true);\n try {\n const data = await loginWithGoogle(opts, response.credential);\n setClientToken(data.access_token);\n opts.onGoogleLoggedIn();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error con Google.\");\n setLoading(false);\n }\n },\n });\n window.google.accounts.id.prompt();\n };\n\n const reset = () => {\n setStep(\"email\");\n setError(\"\");\n };\n\n return {\n email, setEmail,\n firstName, setFirstName,\n lastName, setLastName,\n phone, setPhone,\n rut, setRut,\n step, error, loading, handleMagicLink, handleGoogle, reset,\n };\n}\n","import { useState } from \"react\";\nimport { register } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\nexport interface RegisterFormValues {\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n password: string;\n confirm: string;\n}\n\nconst EMPTY: RegisterFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", password: \"\", confirm: \"\" };\n\nexport interface UseRegisterFormOptions extends ApiConfig {\n onRegistered: () => void;\n}\n\nexport function useRegisterForm(opts: UseRegisterFormOptions) {\n const [form, setForm] = useState<RegisterFormValues>(EMPTY);\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const updateField = (fields: Partial<RegisterFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const handleSubmit = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!form.firstName.trim()) { setError(\"Ingresa tu nombre.\"); return; }\n if (!form.lastName.trim()) { setError(\"Ingresa tu apellido.\"); return; }\n if (!form.email.trim()) { setError(\"Ingresa tu email.\"); return; }\n if (form.password.length < 6) { setError(\"La contraseña debe tener al menos 6 caracteres.\"); return; }\n if (form.password !== form.confirm) { setError(\"Las contraseñas no coinciden.\"); return; }\n setLoading(true);\n setError(\"\");\n try {\n const data = await register(opts, form);\n setClientToken(data.access_token);\n opts.onRegistered();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al registrarse.\");\n } finally {\n setLoading(false);\n }\n };\n\n return { form, updateField, error, loading, handleSubmit };\n}\n","import { useEffect, useState } from \"react\";\nimport { cancelMyBooking, getMe, getMyBookings, getMyPackages, rescheduleMyBooking } from \"../api\";\nimport { clearClientToken, getClientToken } from \"../clientAuth\";\nimport type { BookingRecord, ClientProfile, PackageRecord } from \"../types\";\n\nexport interface UseClientAccountOptions {\n apiBase: string;\n onUnauthenticated: () => void;\n onLoggedOut: () => void;\n}\n\nexport function useClientAccount(opts: UseClientAccountOptions) {\n const [profile, setProfile] = useState<ClientProfile | null>(null);\n const [upcoming, setUpcoming] = useState<BookingRecord[]>([]);\n const [history, setHistory] = useState<BookingRecord[]>([]);\n const [packages, setPackages] = useState<PackageRecord[]>([]);\n const [loading, setLoading] = useState(true);\n const [actionError, setActionError] = useState(\"\");\n const [actionLoading, setActionLoading] = useState(false);\n\n const loadAll = () => {\n const token = getClientToken();\n if (!token) {\n opts.onUnauthenticated();\n return;\n }\n Promise.all([\n getMe(opts.apiBase, token),\n getMyBookings(opts.apiBase, token, { upcoming: true, perPage: 10 }),\n getMyBookings(opts.apiBase, token, { perPage: 20 }),\n getMyPackages(opts.apiBase, token),\n ])\n .then(([prof, up, hist, pkgs]) => {\n setProfile(prof);\n setUpcoming(up);\n setHistory(hist);\n setPackages(pkgs);\n setLoading(false);\n })\n .catch(() => opts.onUnauthenticated());\n };\n\n useEffect(() => {\n loadAll();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const handleLogout = () => {\n clearClientToken();\n opts.onLoggedOut();\n };\n\n const cancelBooking = async (bookingId: string) => {\n const token = getClientToken();\n if (!token) return;\n setActionLoading(true);\n setActionError(\"\");\n const result = await cancelMyBooking(opts.apiBase, token, bookingId);\n if (!result.ok) {\n setActionError(result.message);\n setActionLoading(false);\n return;\n }\n loadAll();\n setActionLoading(false);\n };\n\n const rescheduleBooking = async (bookingId: string, startTime: string, endTime: string) => {\n const token = getClientToken();\n if (!token) return false;\n setActionLoading(true);\n setActionError(\"\");\n const result = await rescheduleMyBooking(opts.apiBase, token, bookingId, startTime, endTime);\n setActionLoading(false);\n if (!result.ok) {\n setActionError(result.message);\n return false;\n }\n loadAll();\n return true;\n };\n\n const totalCredits = packages.reduce((s, p) => s + p.remaining_credits, 0);\n\n return {\n profile, upcoming, history, packages, loading, totalCredits, handleLogout,\n cancelBooking, rescheduleBooking, actionError, setActionError, actionLoading,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACeA,SAAS,QAAQ,KAAgB,WAAW,OAA+B;AACzE,QAAM,IAA4B,EAAE,aAAa,IAAI,OAAO;AAC5D,MAAI,SAAU,GAAE,cAAc,IAAI;AAClC,SAAO;AACT;AAEA,SAAS,YAAY,OAAe,WAAW,OAA+B;AAC5E,QAAM,IAA4B,EAAE,eAAe,UAAU,KAAK,GAAG;AACrE,MAAI,SAAU,GAAE,cAAc,IAAI;AAClC,SAAO;AACT;AAEA,eAAsB,cAAc,KAAqC;AACvE,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,iBAAiB,EAAE,SAAS,QAAQ,GAAG,EAAE,CAAC;AAChF,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,KAAK,QAAQ,CAAC;AACvB;AAEA,eAAsB,aAAa,KAAgB,UAAU,KAAyB;AACpF,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,yBAAyB,OAAO,IAAI,EAAE,SAAS,QAAQ,GAAG,EAAE,CAAC;AACnG,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,KAAK,QAAQ,CAAC;AACvB;AAEA,eAAsB,cAAc,KAAgB,UAAU,KAA0B;AACtF,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,0BAA0B,OAAO,IAAI,EAAE,SAAS,QAAQ,GAAG,EAAE,CAAC;AACpG,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,KAAK,QAAQ,CAAC;AACvB;AAEA,eAAsB,kBACpB,KACA,QACiB;AACjB,QAAM,IAAI,IAAI,gBAAgB,EAAE,aAAa,OAAO,YAAY,YAAY,OAAO,UAAU,CAAC;AAC9F,MAAI,OAAO,UAAW,GAAE,IAAI,cAAc,OAAO,SAAS;AAC1D,MAAI,OAAO,WAAY,GAAE,IAAI,eAAe,OAAO,UAAU;AAC7D,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,uBAAuB,EAAE,SAAS,CAAC,IAAI,EAAE,SAAS,QAAQ,GAAG,EAAE,CAAC;AACtG,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,KAAK,MAAM,SAAS,CAAC;AAC9B;AAEA,eAAsB,aAAa,KAAgB,MAAiD;AAClG,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,eAAe;AAAA,IACnD,QAAQ;AAAA,IACR,SAAS,QAAQ,KAAK,IAAI;AAAA,IAC1B,MAAM,KAAK,UAAU;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,KAAK,KAAK,OAAO;AAAA,IACnB,CAAC;AAAA,EACH,CAAC;AACD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,yBAAyB;AACtD,SAAO,IAAI,KAAK;AAClB;AAEA,eAAsB,cACpB,KACA,OACA,aACwB;AAKxB,QAAM,aAAa,QAAQ,KAAK,IAAI;AACpC,MAAI,YAAa,YAAW,gBAAgB,UAAU,WAAW;AACjE,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,gBAAgB;AAAA,IACpD,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,MAAM,KAAK,UAAU,KAAK;AAAA,EAC5B,CAAC;AACD,MAAI,IAAI,WAAW,KAAK;AACtB,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,WAAO,EAAE,IAAI,OAAO,UAAU,MAAM,SAAS,KAAK,UAAU,wCAAqC;AAAA,EACnG;AACA,MAAI,CAAC,IAAI,IAAI;AACX,WAAO,EAAE,IAAI,OAAO,UAAU,OAAO,SAAS,0DAA0D;AAAA,EAC1G;AACA,SAAO,EAAE,IAAI,MAAM,SAAS,MAAM,IAAI,KAAK,EAAE;AAC/C;AAEA,eAAsB,cACpB,KACA,OACA,aACA,SACe;AACf,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,8BAA8B;AAAA,IAClE,QAAQ;AAAA,IACR,SAAS,QAAQ,KAAK,IAAI;AAAA,IAC1B,MAAM,KAAK,UAAU;AAAA,MACnB,OAAO,MAAM,KAAK,EAAE,YAAY;AAAA,MAChC,cAAc;AAAA,MACd,YAAY,SAAS;AAAA,MACrB,WAAW,SAAS;AAAA,MACpB,OAAO,SAAS;AAAA,MAChB,KAAK,SAAS;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,KAAK,UAAU,yBAAyB;AACvE;AAEA,eAAsB,gBAAgB,KAAgB,OAAkD;AACtG,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,qCAAqC;AAAA,IACzE,QAAQ;AAAA,IACR,SAAS,QAAQ,KAAK,IAAI;AAAA,IAC1B,MAAM,KAAK,UAAU,EAAE,MAAM,CAAC;AAAA,EAChC,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,KAAK,aAAc,OAAM,IAAI,MAAM,KAAK,UAAU,mBAAgB;AACvE,SAAO;AACT;AAEA,eAAsB,gBAAgB,KAAgB,YAAuD;AAC3G,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,0BAA0B;AAAA,IAC9D,QAAQ;AAAA,IACR,SAAS,QAAQ,KAAK,IAAI;AAAA,IAC1B,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC;AAAA,EACrC,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,KAAK,UAAU,mBAAmB;AAC/D,SAAO;AACT;AAEA,eAAsB,SACpB,KACA,MACmC;AACnC,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,OAAO,4BAA4B;AAAA,IAChE,QAAQ;AAAA,IACR,SAAS,QAAQ,KAAK,IAAI;AAAA,IAC1B,MAAM,KAAK,UAAU;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK,SAAS;AAAA,MACrB,UAAU,KAAK;AAAA,IACjB,CAAC;AAAA,EACH,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,KAAK,UAAU,uBAAuB;AACnE,SAAO;AACT;AAEA,eAAsB,MAAM,SAAiB,OAAuC;AAClF,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,UAAU,EAAE,SAAS,YAAY,KAAK,EAAE,CAAC;AAC3E,SAAO,IAAI,KAAK;AAClB;AAEA,eAAsB,cACpB,SACA,OACA,QAC0B;AAC1B,QAAM,IAAI,IAAI,gBAAgB;AAC9B,MAAI,QAAQ,SAAU,GAAE,IAAI,YAAY,MAAM;AAC9C,IAAE,IAAI,YAAY,OAAO,QAAQ,WAAW,EAAE,CAAC;AAC/C,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,mBAAmB,EAAE,SAAS,CAAC,IAAI,EAAE,SAAS,YAAY,KAAK,EAAE,CAAC;AACpG,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,KAAK,QAAQ,CAAC;AACvB;AAEA,eAAsB,cAAc,SAAiB,OAAyC;AAC5F,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,mBAAmB,EAAE,SAAS,YAAY,KAAK,EAAE,CAAC;AACpF,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,KAAK,QAAQ,CAAC;AACvB;AASA,eAAsB,gBACpB,SAAiB,OAAe,WACA;AAChC,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,mBAAmB,SAAS,WAAW;AAAA,IACvE,QAAQ;AAAA,IACR,SAAS,YAAY,KAAK;AAAA,EAC5B,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,MAAI,CAAC,IAAI,GAAI,QAAO,EAAE,IAAI,OAAO,SAAS,KAAK,UAAU,kCAAkC;AAC3F,SAAO,EAAE,IAAI,MAAM,SAAS,KAAK;AACnC;AAEA,eAAsB,oBACpB,SAAiB,OAAe,WAAmB,WAAmB,SACtC;AAChC,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,mBAAmB,SAAS,eAAe;AAAA,IAC3E,QAAQ;AAAA,IACR,SAAS,YAAY,OAAO,IAAI;AAAA,IAChC,MAAM,KAAK,UAAU,EAAE,YAAY,WAAW,UAAU,QAAQ,CAAC;AAAA,EACnE,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,MAAI,CAAC,IAAI,GAAI,QAAO,EAAE,IAAI,OAAO,SAAS,KAAK,UAAU,qCAAqC;AAC9F,SAAO,EAAE,IAAI,MAAM,SAAS,KAAK;AACnC;AAEA,eAAsB,aACpB,SACA,OACA,QACwB;AACxB,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,mBAAmB;AAAA,IACnD,QAAQ;AAAA,IACR,SAAS,YAAY,OAAO,IAAI;AAAA,IAChC,MAAM,KAAK,UAAU;AAAA,MACnB,YAAY,OAAO;AAAA,MACnB,aAAa,OAAO;AAAA,MACpB,aAAa,OAAO;AAAA,MACpB,MAAM,OAAO;AAAA,MACb,kBAAkB,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,KAAK,UAAU,uCAAuC;AACnF,SAAO;AACT;AAEA,eAAsB,cAAc,SAAiB,OAAyC;AAC5F,QAAM,MAAM,MAAM,MAAM,GAAG,OAAO,mBAAmB,EAAE,SAAS,YAAY,KAAK,EAAE,CAAC;AACpF,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,SAAO,KAAK,QAAQ,CAAC;AACvB;AAEA,eAAsB,cAAc,SAAiB,OAAe,SAAgC;AAClG,QAAM,MAAM,GAAG,OAAO,mBAAmB,OAAO,IAAI;AAAA,IAClD,QAAQ;AAAA,IACR,SAAS,YAAY,KAAK;AAAA,EAC5B,CAAC;AACH;;;ACxPA,IAAM,YAAY;AAEX,SAAS,iBAAgC;AAC9C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,aAAa,QAAQ,SAAS;AACvC;AAEO,SAAS,eAAe,OAAqB;AAClD,MAAI,OAAO,WAAW,YAAa;AACnC,eAAa,QAAQ,WAAW,KAAK;AACvC;AAEO,SAAS,mBAAyB;AACvC,MAAI,OAAO,WAAW,YAAa;AACnC,eAAa,WAAW,SAAS;AACnC;AAEA,eAAsB,QAAQ,SAAiB,OAA8C;AAC3F,MAAI;AACF,WAAO,MAAM,MAAM,SAAS,KAAK;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC1BO,SAAS,YAAY,MAAkB;AAC5C,QAAM,IAAI,IAAI,KAAK,IAAI;AACvB,QAAM,OAAO,EAAE,OAAO,IAAI,KAAK;AAC/B,IAAE,QAAQ,EAAE,QAAQ,IAAI,GAAG;AAC3B,IAAE,SAAS,GAAG,GAAG,GAAG,CAAC;AACrB,SAAO;AACT;AAEO,SAAS,QAAQ,GAAiB;AACvC,SAAO,GAAG,EAAE,YAAY,CAAC,IAAI,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAChH;AAEO,SAAS,iBAAiB,QAAsB;AACrD,QAAM,SAAS,IAAI,KAAK,MAAM;AAC9B,SAAO,QAAQ,OAAO,QAAQ,IAAI,CAAC;AACnC,QAAM,OAAmC,EAAE,KAAK,WAAW,OAAO,OAAO;AACzE,QAAM,QAAQ,OAAO,mBAAmB,SAAS,IAAI;AACrD,QAAM,MAAM,OAAO,mBAAmB,SAAS,IAAI;AACnD,SAAO,GAAG,KAAK,WAAM,GAAG;AAC1B;AAEO,IAAM,YAAY,CAAC,OAAO,OAAO,UAAO,OAAO,OAAO,UAAO,KAAK;AAClE,IAAM,iBAAiB,CAAC,SAAS,UAAU,gBAAa,UAAU,WAAW,aAAU,SAAS;AAQhG,IAAM,0BAA2C;AAAA,EACtD,EAAE,IAAI,WAAW,QAAQ,WAAW,MAAM,UAAU;AAAA,EACpD,EAAE,IAAI,WAAW,QAAQ,WAAW,MAAM,UAAU;AAAA,EACpD,EAAE,IAAI,WAAW,QAAQ,WAAW,MAAM,UAAU;AAAA,EACpD,EAAE,IAAI,WAAW,QAAQ,WAAW,MAAM,UAAU;AACtD;;;ACnCA,mBAAoC;AAoBpC,IAAM,aAA+B,EAAE,WAAW,IAAI,UAAU,IAAI,OAAO,IAAI,OAAO,IAAI,KAAK,GAAG;AAElG,SAAS,aAAa,OAAwB;AAC5C,SAAO,MAAM,QAAQ,WAAW,EAAE,EAAE,UAAU;AAChD;AAEA,SAAS,mBAAmB,MAAuC;AACjE,MAAI,CAAC,KAAK,UAAU,KAAK,EAAG,QAAO;AACnC,MAAI,CAAC,KAAK,SAAS,KAAK,EAAG,QAAO;AAClC,MAAI,CAAC,KAAK,MAAM,KAAK,KAAK,CAAC,eAAe,KAAK,KAAK,KAAK,EAAG,QAAO;AACnE,MAAI,CAAC,KAAK,MAAM,KAAK,KAAK,CAAC,aAAa,KAAK,KAAK,EAAG,QAAO;AAC5D,MAAI,CAAC,KAAK,IAAI,KAAK,EAAG,QAAO;AAC7B,SAAO;AACT;AAcA,SAAS,oBAAoB,MAA+B;AAC1D,QAAM,EAAE,MAAM,QAAQ,cAAc,cAAc,aAAa,YAAY,IAAI;AAC/E,QAAM,QAAQ,KAAK,WAAW,SAAS,KAAK,YAAY;AACxD,QAAM,QAAQ,KAAK,WAAW,SAAS,KAAK;AAC5C,SAAO;AAAA,IACL,YAAY,GAAG,YAAY,IAAI,aAAa,UAAU;AAAA,IACtD,UAAU,GAAG,YAAY,IAAI,aAAa,QAAQ;AAAA,IAClD,YAAY;AAAA,IACZ,qBAAqB,aAAa;AAAA,IAClC,aAAa;AAAA,IACb;AAAA,IACA;AAAA;AAAA;AAAA,IAGA,WAAW,QAAQ;AAAA,IACnB,mBAAmB,KAAK;AAAA,IACxB,kBAAkB,KAAK;AAAA,IACvB,cAAc,KAAK;AAAA,IACnB,cAAc,KAAK;AAAA,IACnB,YAAY,KAAK,OAAO;AAAA,EAC1B;AACF;AAsBO,SAAS,eAAe,MAA6B;AAC1D,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAqB,CAAC,CAAC;AACzD,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAoB,CAAC,CAAC;AACtD,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAqB,CAAC,CAAC;AACzD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAiB,CAAC,CAAC;AAE7C,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,EAAE;AACjD,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,EAAE;AACjD,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,EAAE;AACnD,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,EAAE;AACnD,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAsB,IAAI;AAElE,QAAM,CAAC,MAAM,OAAO,QAAI,uBAA2B,UAAU;AAC7D,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAA+B,IAAI;AAG/D,8BAAU,MAAM;AACd,UAAM,QAAQ,eAAe;AAC7B,QAAI,CAAC,MAAO;AACZ,YAAQ,KAAK,SAAS,KAAK,EAAE,KAAK,aAAW;AAC3C,UAAI,CAAC,QAAS;AACd,gBAAU,OAAO;AACjB,cAAQ,QAAM;AAAA,QACZ,GAAG;AAAA,QACH,WAAW,QAAQ,cAAc,EAAE;AAAA,QACnC,UAAU,QAAQ,aAAa,EAAE;AAAA,QACjC,OAAO,QAAQ,SAAS,EAAE;AAAA,QAC1B,OAAO,QAAQ,SAAS,EAAE;AAAA,QAC1B,KAAK,QAAQ,OAAO,EAAE;AAAA,MACxB,EAAE;AAAA,IACJ,CAAC;AAAA,EAEH,GAAG,CAAC,CAAC;AAEL,8BAAU,MAAM;AACd,kBAAc,IAAI,EACf,KAAK,YAAY,EACjB,MAAM,MAAM,SAAS,2BAA2B,CAAC;AAAA,EAEtD,GAAG,CAAC,CAAC;AAEL,8BAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,iBAAa,IAAI,EACd,KAAK,WAAW,EAChB,MAAM,MAAM,SAAS,0BAA0B,CAAC;AAAA,EAErD,GAAG,CAAC,WAAW,CAAC;AAEhB,8BAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,kBAAc,IAAI,EACf,KAAK,YAAY,EACjB,MAAM,MAAM,SAAS,8BAA8B,CAAC;AAAA,EAEzD,GAAG,CAAC,WAAW,CAAC;AAEhB,8BAAU,MAAM;AACd,QAAI,CAAC,eAAe,CAAC,aAAc;AACnC,eAAW,IAAI;AACf,sBAAkB,MAAM;AAAA,MACtB,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,WAAW,eAAe;AAAA,MAC1B,YAAY,gBAAgB;AAAA,IAC9B,CAAC,EACE,KAAK,OAAK;AAAE,eAAS,CAAC;AAAG,iBAAW,KAAK;AAAA,IAAG,CAAC,EAC7C,MAAM,MAAM;AAAE,eAAS,yBAAyB;AAAG,iBAAW,KAAK;AAAA,IAAG,CAAC;AAAA,EAE5E,GAAG,CAAC,aAAa,aAAa,cAAc,YAAY,CAAC;AAEzD,QAAM,aAAa,CAAC,WAAsC,QAAQ,QAAM,EAAE,GAAG,GAAG,GAAG,OAAO,EAAE;AAE5F,QAAM,gBAAgB,OAAO,cAAuC;AAClE,QAAI,CAAC,aAAc;AACnB,UAAM,kBAAkB,mBAAmB,IAAI;AAC/C,QAAI,iBAAiB;AAAE,eAAS,eAAe;AAAG;AAAA,IAAQ;AAE1D,eAAW,IAAI;AACf,aAAS,EAAE;AACX,QAAI;AACF,YAAM,MAAM,SAAS,KAAK,OAAK,EAAE,OAAO,WAAW;AACnD,YAAM,QAAQ,eAAe;AAC7B,YAAM,UAAU,oBAAoB;AAAA,QAClC;AAAA,QAAM;AAAA,QAAQ;AAAA,QAAc;AAAA,QAAc;AAAA,QAAa;AAAA,QAAa;AAAA,QACpE,UAAU,KAAK;AAAA,QAAO,cAAc,KAAK;AAAA,MAC3C,CAAC;AACD,YAAM,SAAS,MAAM,cAAc,MAAM,SAAS,SAAS,MAAS;AACpE,yBAAmB,MAAM;AAAA,IAC3B,SAAS,KAAc;AACrB,eAAS,eAAe,QAAQ,IAAI,UAAU,8BAA8B;AAC5E,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,WAAS,mBAAmB,QAAmD;AAC7E,QAAI,CAAC,OAAO,IAAI;AACd,eAAS,OAAO,OAAO;AACvB,UAAI,OAAO,SAAU,iBAAgB,IAAI;AACzC,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,SAAK,kBAAkB,OAAO,OAAO;AAAA,EACvC;AAEA,SAAO;AAAA,IACL;AAAA,IAAW;AAAA,IAAU;AAAA,IAAW;AAAA,IAChC;AAAA,IAAa;AAAA,IACb;AAAA,IAAa;AAAA,IACb;AAAA,IAAc;AAAA,IACd;AAAA,IAAc;AAAA,IACd;AAAA,IAAc;AAAA,IACd;AAAA,IAAM;AAAA,IACN;AAAA,IAAS;AAAA,IAAO;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;;;ACjNA,IAAAA,gBAAiD;AAgBjD,IAAMC,cAA+B,EAAE,WAAW,IAAI,UAAU,IAAI,OAAO,IAAI,OAAO,IAAI,KAAK,GAAG;AAElG,SAASC,cAAa,OAAwB;AAC5C,SAAO,MAAM,QAAQ,WAAW,EAAE,EAAE,UAAU;AAChD;AAEA,SAAS,kBAAkB,MAAuC;AAChE,MAAI,CAAC,KAAK,UAAU,KAAK,EAAG,QAAO;AACnC,MAAI,CAAC,KAAK,SAAS,KAAK,EAAG,QAAO;AAClC,MAAI,CAAC,KAAK,MAAM,KAAK,KAAK,CAAC,eAAe,KAAK,KAAK,KAAK,EAAG,QAAO;AACnE,MAAI,CAAC,KAAK,MAAM,KAAK,KAAK,CAACA,cAAa,KAAK,KAAK,EAAG,QAAO;AAC5D,SAAO;AACT;AAEA,SAAS,yBACP,MACA,QACA,cACA,KACA,YACA;AACA,SAAO;AAAA,IACL,YAAY,GAAG,aAAa,IAAI,IAAI,aAAa,KAAK,UAAU;AAAA,IAChE,UAAU,GAAG,aAAa,IAAI,IAAI,aAAa,KAAK,QAAQ;AAAA,IAC5D,YAAY,KAAK,MAAM;AAAA,IACvB,qBAAqB,aAAa,KAAK;AAAA,IACvC,aAAa;AAAA,IACb,OAAO,KAAK,SAAS;AAAA,IACrB,WAAW,QAAQ;AAAA,IACnB,mBAAmB,KAAK;AAAA,IACxB,kBAAkB,KAAK;AAAA,IACvB,cAAc,KAAK;AAAA,IACnB,cAAc,KAAK;AAAA,IACnB,YAAY,KAAK,OAAO;AAAA,EAC1B;AACF;AAEA,SAAS,uBAAuB,QAAwD;AACtF,SAAO,OAAO,WACV,uFACA,OAAO;AACb;AAQO,SAAS,iBAAiB,MAA+B;AAC9D,QAAM,SAAS,KAAK,UAAU;AAE9B,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAe,MAAM,YAAY,oBAAI,KAAK,CAAC,CAAC;AACxE,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAiC,CAAC,CAAC;AACvE,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAoB,CAAC,CAAC;AACtD,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAS,EAAE;AAC/C,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,IAAI;AAC3C,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAmC,IAAI;AAC/E,QAAM,CAAC,kBAAkB,mBAAmB,QAAI,wBAAwC,CAAC,CAAC;AAE1F,QAAM,CAAC,MAAM,OAAO,QAAI,wBAA2BD,WAAU;AAC7D,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAA+B,IAAI;AAE/D,+BAAU,MAAM;AACd,YAAQ,IAAI,CAAC,cAAc,IAAI,GAAG,aAAa,MAAM,EAAE,CAAC,CAAC,EACtD,KAAK,CAAC,CAAC,MAAM,IAAI,MAAM;AACtB,UAAI,KAAK,OAAQ,eAAc,KAAK,CAAC,EAAE,EAAE;AACzC,kBAAY,IAAI;AAAA,IAClB,CAAC,EACA,MAAM,MAAM,SAAS,sBAAsB,CAAC;AAAA,EAEjD,GAAG,CAAC,CAAC;AAEL,+BAAU,MAAM;AACd,UAAM,QAAQ,eAAe;AAC7B,QAAI,CAAC,MAAO;AACZ,YAAQ,KAAK,SAAS,KAAK,EAAE,KAAK,aAAW;AAC3C,UAAI,CAAC,QAAS;AACd,gBAAU,OAAO;AACjB,cAAQ,QAAM;AAAA,QACZ,GAAG;AAAA,QACH,WAAW,QAAQ,cAAc,EAAE;AAAA,QACnC,UAAU,QAAQ,aAAa,EAAE;AAAA,QACjC,OAAO,QAAQ,SAAS,EAAE;AAAA,QAC1B,OAAO,QAAQ,SAAS,EAAE;AAAA,QAC1B,KAAK,QAAQ,OAAO,EAAE;AAAA,MACxB,EAAE;AAAA,IACJ,CAAC;AAAA,EAEH,GAAG,CAAC,CAAC;AAEL,QAAM,eAAW,2BAAY,OAAO,KAAW,UAAkB;AAC/D,QAAI,CAAC,MAAO;AACZ,eAAW,IAAI;AACf,UAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,MAAM;AAC/C,YAAM,IAAI,IAAI,KAAK,GAAG;AACtB,QAAE,QAAQ,IAAI,QAAQ,IAAI,CAAC;AAC3B,aAAO;AAAA,IACT,CAAC;AAED,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,KAAK,IAAI,OAAM,MAAK;AAClB,cAAM,KAAK,QAAQ,CAAC;AACpB,YAAI;AACF,gBAAM,QAAQ,MAAM,kBAAkB,MAAM,EAAE,YAAY,OAAO,WAAW,GAAG,CAAC;AAChF,iBAAO,EAAE,MAAM,IAAI,MAAM;AAAA,QAC3B,QAAQ;AACN,iBAAO,EAAE,MAAM,IAAI,OAAO,CAAC,EAAY;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,MAA8B,CAAC;AACrC,UAAM,WAA0C,CAAC;AACjD,QAAI,WAAW;AACf,YAAQ,QAAQ,CAAC,EAAE,MAAM,MAAM,MAAM;AACnC,UAAI,IAAI,IAAI;AACZ,YAAM,QAAQ,OAAK;AACjB,YAAI,CAAC,SAAS,EAAE,WAAW,GAAG;AAC5B,mBAAS,EAAE,WAAW,IAAI,OAAO,WAAW,OAAO,MAAM;AACzD;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,kBAAc,GAAG;AACjB,wBAAoB,WAAS,EAAE,GAAG,MAAM,GAAG,SAAS,EAAE;AACtD,eAAW,KAAK;AAAA,EAElB,GAAG,CAAC,CAAC;AAEL,+BAAU,MAAM;AACd,QAAI,WAAY,UAAS,QAAQ,UAAU;AAAA,EAC7C,GAAG,CAAC,QAAQ,YAAY,QAAQ,CAAC;AAEjC,QAAM,aAAa,CAAC,WAAsC,QAAQ,QAAM,EAAE,GAAG,GAAG,GAAG,OAAO,EAAE;AAE5F,QAAM,gBAAgB,OAAO,MAAsC;AACjE,MAAE,eAAe;AACjB,QAAI,CAAC,aAAc;AACnB,UAAM,kBAAkB,kBAAkB,IAAI;AAC9C,QAAI,iBAAiB;AAAE,eAAS,eAAe;AAAG;AAAA,IAAQ;AAC1D,kBAAc,IAAI;AAClB,aAAS,EAAE;AACX,QAAI;AACF,YAAM,MAAM,SAAS,KAAK,OAAK,EAAE,SAAS,aAAa,KAAK,YAAY;AACxE,YAAM,QAAQ,eAAe;AAC7B,YAAM,UAAU,yBAAyB,MAAM,QAAQ,cAAc,KAAK,UAAU;AACpF,YAAM,SAAS,MAAM,cAAc,MAAM,SAAS,SAAS,MAAS;AACpE,yBAAmB,MAAM;AAAA,IAC3B,SAAS,KAAc;AACrB,eAAS,eAAe,QAAQ,IAAI,UAAU,mBAAmB;AACjE,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,WAAS,mBAAmB,QAAmD;AAC7E,QAAI,CAAC,OAAO,IAAI;AACd,eAAS,uBAAuB,MAAM,CAAC;AACvC,oBAAc,KAAK;AACnB;AAAA,IACF;AACA,SAAK,kBAAkB;AAAA,EACzB;AAEA,SAAO;AAAA,IACL;AAAA,IAAQ;AAAA,IACR;AAAA,IAAY;AAAA,IAAS;AAAA,IACrB;AAAA,IAAc;AAAA,IACd;AAAA,IAAM;AAAA,IACN;AAAA,IAAY;AAAA,IAAO;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACF;;;AChMA,IAAAE,gBAAyB;AA0BlB,SAAS,iBAAiB,MAA+B;AAC9D,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AAGrC,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,EAAE;AAC7C,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,EAAE;AAC3C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,KAAK,MAAM,QAAI,wBAAS,EAAE;AACjC,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAwB,OAAO;AACvD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAE5C,QAAM,kBAAkB,OAAO,MAAsC;AACnE,MAAE,eAAe;AACjB,QAAI,CAAC,MAAM,KAAK,KAAK,CAAC,eAAe,KAAK,KAAK,GAAG;AAChD,eAAS,6BAA0B;AACnC;AAAA,IACF;AACA,eAAW,IAAI;AACf,aAAS,EAAE;AACX,QAAI;AACF,YAAM,cAAc,MAAM,OAAO,KAAK,aAAa;AAAA,QACjD,WAAW,aAAa;AAAA,QACxB,UAAU,YAAY;AAAA,QACtB,OAAO,SAAS;AAAA,QAChB,KAAK,OAAO;AAAA,MACd,CAAC;AACD,cAAQ,MAAM;AAAA,IAChB,SAAS,KAAc;AACrB,eAAS,eAAe,QAAQ,IAAI,UAAU,yBAAyB;AAAA,IACzE,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AACzB,QAAI,CAAC,KAAK,kBAAkB,OAAO,WAAW,eAAe,CAAC,OAAO,OAAQ;AAC7E,WAAO,OAAO,SAAS,GAAG,WAAW;AAAA,MACnC,WAAW,KAAK;AAAA,MAChB,UAAU,OAAO,aAAqC;AACpD,mBAAW,IAAI;AACf,YAAI;AACF,gBAAM,OAAO,MAAM,gBAAgB,MAAM,SAAS,UAAU;AAC5D,yBAAe,KAAK,YAAY;AAChC,eAAK,iBAAiB;AAAA,QACxB,SAAS,KAAc;AACrB,mBAAS,eAAe,QAAQ,IAAI,UAAU,mBAAmB;AACjE,qBAAW,KAAK;AAAA,QAClB;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAO,OAAO,SAAS,GAAG,OAAO;AAAA,EACnC;AAEA,QAAM,QAAQ,MAAM;AAClB,YAAQ,OAAO;AACf,aAAS,EAAE;AAAA,EACb;AAEA,SAAO;AAAA,IACL;AAAA,IAAO;AAAA,IACP;AAAA,IAAW;AAAA,IACX;AAAA,IAAU;AAAA,IACV;AAAA,IAAO;AAAA,IACP;AAAA,IAAK;AAAA,IACL;AAAA,IAAM;AAAA,IAAO;AAAA,IAAS;AAAA,IAAiB;AAAA,IAAc;AAAA,EACvD;AACF;;;AC7FA,IAAAC,gBAAyB;AAczB,IAAM,QAA4B,EAAE,WAAW,IAAI,UAAU,IAAI,OAAO,IAAI,OAAO,IAAI,UAAU,IAAI,SAAS,GAAG;AAM1G,SAAS,gBAAgB,MAA8B;AAC5D,QAAM,CAAC,MAAM,OAAO,QAAI,wBAA6B,KAAK;AAC1D,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAE5C,QAAM,cAAc,CAAC,WAAwC,QAAQ,QAAM,EAAE,GAAG,GAAG,GAAG,OAAO,EAAE;AAE/F,QAAM,eAAe,OAAO,MAAsC;AAChE,MAAE,eAAe;AACjB,QAAI,CAAC,KAAK,UAAU,KAAK,GAAG;AAAE,eAAS,oBAAoB;AAAG;AAAA,IAAQ;AACtE,QAAI,CAAC,KAAK,SAAS,KAAK,GAAG;AAAE,eAAS,sBAAsB;AAAG;AAAA,IAAQ;AACvE,QAAI,CAAC,KAAK,MAAM,KAAK,GAAG;AAAE,eAAS,mBAAmB;AAAG;AAAA,IAAQ;AACjE,QAAI,KAAK,SAAS,SAAS,GAAG;AAAE,eAAS,oDAAiD;AAAG;AAAA,IAAQ;AACrG,QAAI,KAAK,aAAa,KAAK,SAAS;AAAE,eAAS,kCAA+B;AAAG;AAAA,IAAQ;AACzF,eAAW,IAAI;AACf,aAAS,EAAE;AACX,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,MAAM,IAAI;AACtC,qBAAe,KAAK,YAAY;AAChC,WAAK,aAAa;AAAA,IACpB,SAAS,KAAc;AACrB,eAAS,eAAe,QAAQ,IAAI,UAAU,uBAAuB;AAAA,IACvE,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,aAAa,OAAO,SAAS,aAAa;AAC3D;;;AChDA,IAAAC,gBAAoC;AAW7B,SAAS,iBAAiB,MAA+B;AAC9D,QAAM,CAAC,SAAS,UAAU,QAAI,wBAA+B,IAAI;AACjE,QAAM,CAAC,UAAU,WAAW,QAAI,wBAA0B,CAAC,CAAC;AAC5D,QAAM,CAAC,SAAS,UAAU,QAAI,wBAA0B,CAAC,CAAC;AAC1D,QAAM,CAAC,UAAU,WAAW,QAAI,wBAA0B,CAAC,CAAC;AAC5D,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,IAAI;AAC3C,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,EAAE;AACjD,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAS,KAAK;AAExD,QAAM,UAAU,MAAM;AACpB,UAAM,QAAQ,eAAe;AAC7B,QAAI,CAAC,OAAO;AACV,WAAK,kBAAkB;AACvB;AAAA,IACF;AACA,YAAQ,IAAI;AAAA,MACV,MAAM,KAAK,SAAS,KAAK;AAAA,MACzB,cAAc,KAAK,SAAS,OAAO,EAAE,UAAU,MAAM,SAAS,GAAG,CAAC;AAAA,MAClE,cAAc,KAAK,SAAS,OAAO,EAAE,SAAS,GAAG,CAAC;AAAA,MAClD,cAAc,KAAK,SAAS,KAAK;AAAA,IACnC,CAAC,EACE,KAAK,CAAC,CAAC,MAAM,IAAI,MAAM,IAAI,MAAM;AAChC,iBAAW,IAAI;AACf,kBAAY,EAAE;AACd,iBAAW,IAAI;AACf,kBAAY,IAAI;AAChB,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,MAAM,KAAK,kBAAkB,CAAC;AAAA,EACzC;AAEA,+BAAU,MAAM;AACd,YAAQ;AAAA,EAEV,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,MAAM;AACzB,qBAAiB;AACjB,SAAK,YAAY;AAAA,EACnB;AAEA,QAAM,gBAAgB,OAAO,cAAsB;AACjD,UAAM,QAAQ,eAAe;AAC7B,QAAI,CAAC,MAAO;AACZ,qBAAiB,IAAI;AACrB,mBAAe,EAAE;AACjB,UAAM,SAAS,MAAM,gBAAgB,KAAK,SAAS,OAAO,SAAS;AACnE,QAAI,CAAC,OAAO,IAAI;AACd,qBAAe,OAAO,OAAO;AAC7B,uBAAiB,KAAK;AACtB;AAAA,IACF;AACA,YAAQ;AACR,qBAAiB,KAAK;AAAA,EACxB;AAEA,QAAM,oBAAoB,OAAO,WAAmB,WAAmB,YAAoB;AACzF,UAAM,QAAQ,eAAe;AAC7B,QAAI,CAAC,MAAO,QAAO;AACnB,qBAAiB,IAAI;AACrB,mBAAe,EAAE;AACjB,UAAM,SAAS,MAAM,oBAAoB,KAAK,SAAS,OAAO,WAAW,WAAW,OAAO;AAC3F,qBAAiB,KAAK;AACtB,QAAI,CAAC,OAAO,IAAI;AACd,qBAAe,OAAO,OAAO;AAC7B,aAAO;AAAA,IACT;AACA,YAAQ;AACR,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,SAAS,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,mBAAmB,CAAC;AAEzE,SAAO;AAAA,IACL;AAAA,IAAS;AAAA,IAAU;AAAA,IAAS;AAAA,IAAU;AAAA,IAAS;AAAA,IAAc;AAAA,IAC7D;AAAA,IAAe;AAAA,IAAmB;AAAA,IAAa;AAAA,IAAgB;AAAA,EACjE;AACF;","names":["import_react","EMPTY_FORM","isValidPhone","import_react","import_react","import_react"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -33,12 +33,25 @@ interface Slot {
|
|
|
33
33
|
enrolled_count?: number;
|
|
34
34
|
class_session_id?: string;
|
|
35
35
|
}
|
|
36
|
+
interface WaitlistEntry {
|
|
37
|
+
id: string;
|
|
38
|
+
service_id: string;
|
|
39
|
+
service_name: string;
|
|
40
|
+
provider_id: string | null;
|
|
41
|
+
provider_name: string | null;
|
|
42
|
+
location_id: string;
|
|
43
|
+
class_session_id: string | null;
|
|
44
|
+
date: string | null;
|
|
45
|
+
notified_at: string | null;
|
|
46
|
+
created_at: string;
|
|
47
|
+
}
|
|
36
48
|
interface ClientProfile {
|
|
37
49
|
id: string;
|
|
38
50
|
first_name: string;
|
|
39
51
|
last_name: string;
|
|
40
52
|
email: string | null;
|
|
41
53
|
phone: string | null;
|
|
54
|
+
rut: string | null;
|
|
42
55
|
}
|
|
43
56
|
interface BookingRecord {
|
|
44
57
|
id: string;
|
|
@@ -51,7 +64,8 @@ interface BookingRecord {
|
|
|
51
64
|
status?: {
|
|
52
65
|
id: string;
|
|
53
66
|
name: string;
|
|
54
|
-
};
|
|
67
|
+
} | number;
|
|
68
|
+
status_name?: string;
|
|
55
69
|
approval_status?: string | null;
|
|
56
70
|
}
|
|
57
71
|
interface PackageRecord {
|
|
@@ -141,6 +155,27 @@ declare function getMyBookings(apiBase: string, token: string, params?: {
|
|
|
141
155
|
perPage?: number;
|
|
142
156
|
}): Promise<BookingRecord[]>;
|
|
143
157
|
declare function getMyPackages(apiBase: string, token: string): Promise<PackageRecord[]>;
|
|
158
|
+
/** Result of a client cancelling/rescheduling their own booking — callers
|
|
159
|
+
* branch on `.ok` to show the server's specific reason (cutoff window,
|
|
160
|
+
* overlap, etc.) instead of a generic error. */
|
|
161
|
+
type MyBookingActionResult = {
|
|
162
|
+
ok: true;
|
|
163
|
+
booking: BookingRecord;
|
|
164
|
+
} | {
|
|
165
|
+
ok: false;
|
|
166
|
+
message: string;
|
|
167
|
+
};
|
|
168
|
+
declare function cancelMyBooking(apiBase: string, token: string, bookingId: string): Promise<MyBookingActionResult>;
|
|
169
|
+
declare function rescheduleMyBooking(apiBase: string, token: string, bookingId: string, startTime: string, endTime: string): Promise<MyBookingActionResult>;
|
|
170
|
+
declare function joinWaitlist(apiBase: string, token: string, params: {
|
|
171
|
+
serviceId: string;
|
|
172
|
+
locationId: string;
|
|
173
|
+
providerId?: string;
|
|
174
|
+
date?: string;
|
|
175
|
+
classSessionId?: string;
|
|
176
|
+
}): Promise<WaitlistEntry>;
|
|
177
|
+
declare function getMyWaitlist(apiBase: string, token: string): Promise<WaitlistEntry[]>;
|
|
178
|
+
declare function leaveWaitlist(apiBase: string, token: string, entryId: string): Promise<void>;
|
|
144
179
|
|
|
145
180
|
declare function getClientToken(): string | null;
|
|
146
181
|
declare function setClientToken(token: string): void;
|
|
@@ -312,6 +347,11 @@ declare function useClientAccount(opts: UseClientAccountOptions): {
|
|
|
312
347
|
loading: boolean;
|
|
313
348
|
totalCredits: number;
|
|
314
349
|
handleLogout: () => void;
|
|
350
|
+
cancelBooking: (bookingId: string) => Promise<void>;
|
|
351
|
+
rescheduleBooking: (bookingId: string, startTime: string, endTime: string) => Promise<boolean>;
|
|
352
|
+
actionError: string;
|
|
353
|
+
setActionError: react.Dispatch<react.SetStateAction<string>>;
|
|
354
|
+
actionLoading: boolean;
|
|
315
355
|
};
|
|
316
356
|
|
|
317
|
-
export { type ApiConfig, type BookingCreateInput, type BookingRecord, type BookingResult, type ClientFormValues, type ClientProfile, DAY_NAMES, DAY_NAMES_FULL, DEFAULT_PROVIDER_COLORS, type Location, type MagicLinkStep, type PackageRecord, type Provider, type ProviderColor, type RegisterFormValues, type SelectedClassSlot, type Service, type Slot, type SubmitBookingOverrides, type UseBookingFlowOptions, type UseClassScheduleOptions, type UseClientAccountOptions, type UseMagicLinkAuthOptions, type UseRegisterFormOptions, clearClientToken, createBooking, createClient, dateStr, fetchMe, formatMonthRange, getAvailableSlots, getClientToken, getMe, getMondayOf, getMyBookings, getMyPackages, listLocations, listProviders, listServices, loginWithGoogle, register, sendMagicLink, setClientToken, useBookingFlow, useClassSchedule, useClientAccount, useMagicLinkAuth, useRegisterForm, verifyMagicLink };
|
|
357
|
+
export { type ApiConfig, type BookingCreateInput, type BookingRecord, type BookingResult, type ClientFormValues, type ClientProfile, DAY_NAMES, DAY_NAMES_FULL, DEFAULT_PROVIDER_COLORS, type Location, type MagicLinkStep, type MyBookingActionResult, type PackageRecord, type Provider, type ProviderColor, type RegisterFormValues, type SelectedClassSlot, type Service, type Slot, type SubmitBookingOverrides, type UseBookingFlowOptions, type UseClassScheduleOptions, type UseClientAccountOptions, type UseMagicLinkAuthOptions, type UseRegisterFormOptions, type WaitlistEntry, cancelMyBooking, clearClientToken, createBooking, createClient, dateStr, fetchMe, formatMonthRange, getAvailableSlots, getClientToken, getMe, getMondayOf, getMyBookings, getMyPackages, getMyWaitlist, joinWaitlist, leaveWaitlist, listLocations, listProviders, listServices, loginWithGoogle, register, rescheduleMyBooking, sendMagicLink, setClientToken, useBookingFlow, useClassSchedule, useClientAccount, useMagicLinkAuth, useRegisterForm, verifyMagicLink };
|
package/dist/index.d.ts
CHANGED
|
@@ -33,12 +33,25 @@ interface Slot {
|
|
|
33
33
|
enrolled_count?: number;
|
|
34
34
|
class_session_id?: string;
|
|
35
35
|
}
|
|
36
|
+
interface WaitlistEntry {
|
|
37
|
+
id: string;
|
|
38
|
+
service_id: string;
|
|
39
|
+
service_name: string;
|
|
40
|
+
provider_id: string | null;
|
|
41
|
+
provider_name: string | null;
|
|
42
|
+
location_id: string;
|
|
43
|
+
class_session_id: string | null;
|
|
44
|
+
date: string | null;
|
|
45
|
+
notified_at: string | null;
|
|
46
|
+
created_at: string;
|
|
47
|
+
}
|
|
36
48
|
interface ClientProfile {
|
|
37
49
|
id: string;
|
|
38
50
|
first_name: string;
|
|
39
51
|
last_name: string;
|
|
40
52
|
email: string | null;
|
|
41
53
|
phone: string | null;
|
|
54
|
+
rut: string | null;
|
|
42
55
|
}
|
|
43
56
|
interface BookingRecord {
|
|
44
57
|
id: string;
|
|
@@ -51,7 +64,8 @@ interface BookingRecord {
|
|
|
51
64
|
status?: {
|
|
52
65
|
id: string;
|
|
53
66
|
name: string;
|
|
54
|
-
};
|
|
67
|
+
} | number;
|
|
68
|
+
status_name?: string;
|
|
55
69
|
approval_status?: string | null;
|
|
56
70
|
}
|
|
57
71
|
interface PackageRecord {
|
|
@@ -141,6 +155,27 @@ declare function getMyBookings(apiBase: string, token: string, params?: {
|
|
|
141
155
|
perPage?: number;
|
|
142
156
|
}): Promise<BookingRecord[]>;
|
|
143
157
|
declare function getMyPackages(apiBase: string, token: string): Promise<PackageRecord[]>;
|
|
158
|
+
/** Result of a client cancelling/rescheduling their own booking — callers
|
|
159
|
+
* branch on `.ok` to show the server's specific reason (cutoff window,
|
|
160
|
+
* overlap, etc.) instead of a generic error. */
|
|
161
|
+
type MyBookingActionResult = {
|
|
162
|
+
ok: true;
|
|
163
|
+
booking: BookingRecord;
|
|
164
|
+
} | {
|
|
165
|
+
ok: false;
|
|
166
|
+
message: string;
|
|
167
|
+
};
|
|
168
|
+
declare function cancelMyBooking(apiBase: string, token: string, bookingId: string): Promise<MyBookingActionResult>;
|
|
169
|
+
declare function rescheduleMyBooking(apiBase: string, token: string, bookingId: string, startTime: string, endTime: string): Promise<MyBookingActionResult>;
|
|
170
|
+
declare function joinWaitlist(apiBase: string, token: string, params: {
|
|
171
|
+
serviceId: string;
|
|
172
|
+
locationId: string;
|
|
173
|
+
providerId?: string;
|
|
174
|
+
date?: string;
|
|
175
|
+
classSessionId?: string;
|
|
176
|
+
}): Promise<WaitlistEntry>;
|
|
177
|
+
declare function getMyWaitlist(apiBase: string, token: string): Promise<WaitlistEntry[]>;
|
|
178
|
+
declare function leaveWaitlist(apiBase: string, token: string, entryId: string): Promise<void>;
|
|
144
179
|
|
|
145
180
|
declare function getClientToken(): string | null;
|
|
146
181
|
declare function setClientToken(token: string): void;
|
|
@@ -312,6 +347,11 @@ declare function useClientAccount(opts: UseClientAccountOptions): {
|
|
|
312
347
|
loading: boolean;
|
|
313
348
|
totalCredits: number;
|
|
314
349
|
handleLogout: () => void;
|
|
350
|
+
cancelBooking: (bookingId: string) => Promise<void>;
|
|
351
|
+
rescheduleBooking: (bookingId: string, startTime: string, endTime: string) => Promise<boolean>;
|
|
352
|
+
actionError: string;
|
|
353
|
+
setActionError: react.Dispatch<react.SetStateAction<string>>;
|
|
354
|
+
actionLoading: boolean;
|
|
315
355
|
};
|
|
316
356
|
|
|
317
|
-
export { type ApiConfig, type BookingCreateInput, type BookingRecord, type BookingResult, type ClientFormValues, type ClientProfile, DAY_NAMES, DAY_NAMES_FULL, DEFAULT_PROVIDER_COLORS, type Location, type MagicLinkStep, type PackageRecord, type Provider, type ProviderColor, type RegisterFormValues, type SelectedClassSlot, type Service, type Slot, type SubmitBookingOverrides, type UseBookingFlowOptions, type UseClassScheduleOptions, type UseClientAccountOptions, type UseMagicLinkAuthOptions, type UseRegisterFormOptions, clearClientToken, createBooking, createClient, dateStr, fetchMe, formatMonthRange, getAvailableSlots, getClientToken, getMe, getMondayOf, getMyBookings, getMyPackages, listLocations, listProviders, listServices, loginWithGoogle, register, sendMagicLink, setClientToken, useBookingFlow, useClassSchedule, useClientAccount, useMagicLinkAuth, useRegisterForm, verifyMagicLink };
|
|
357
|
+
export { type ApiConfig, type BookingCreateInput, type BookingRecord, type BookingResult, type ClientFormValues, type ClientProfile, DAY_NAMES, DAY_NAMES_FULL, DEFAULT_PROVIDER_COLORS, type Location, type MagicLinkStep, type MyBookingActionResult, type PackageRecord, type Provider, type ProviderColor, type RegisterFormValues, type SelectedClassSlot, type Service, type Slot, type SubmitBookingOverrides, type UseBookingFlowOptions, type UseClassScheduleOptions, type UseClientAccountOptions, type UseMagicLinkAuthOptions, type UseRegisterFormOptions, type WaitlistEntry, cancelMyBooking, clearClientToken, createBooking, createClient, dateStr, fetchMe, formatMonthRange, getAvailableSlots, getClientToken, getMe, getMondayOf, getMyBookings, getMyPackages, getMyWaitlist, joinWaitlist, leaveWaitlist, listLocations, listProviders, listServices, loginWithGoogle, register, rescheduleMyBooking, sendMagicLink, setClientToken, useBookingFlow, useClassSchedule, useClientAccount, useMagicLinkAuth, useRegisterForm, verifyMagicLink };
|
package/dist/index.js
CHANGED
|
@@ -133,6 +133,52 @@ async function getMyPackages(apiBase, token) {
|
|
|
133
133
|
const data = await res.json();
|
|
134
134
|
return data.data || [];
|
|
135
135
|
}
|
|
136
|
+
async function cancelMyBooking(apiBase, token, bookingId) {
|
|
137
|
+
const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/cancel`, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: authHeaders(token)
|
|
140
|
+
});
|
|
141
|
+
const data = await res.json().catch(() => ({}));
|
|
142
|
+
if (!res.ok) return { ok: false, message: data.detail || "No se pudo cancelar la reserva." };
|
|
143
|
+
return { ok: true, booking: data };
|
|
144
|
+
}
|
|
145
|
+
async function rescheduleMyBooking(apiBase, token, bookingId, startTime, endTime) {
|
|
146
|
+
const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/reschedule`, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: authHeaders(token, true),
|
|
149
|
+
body: JSON.stringify({ start_time: startTime, end_time: endTime })
|
|
150
|
+
});
|
|
151
|
+
const data = await res.json().catch(() => ({}));
|
|
152
|
+
if (!res.ok) return { ok: false, message: data.detail || "No se pudo reprogramar la reserva." };
|
|
153
|
+
return { ok: true, booking: data };
|
|
154
|
+
}
|
|
155
|
+
async function joinWaitlist(apiBase, token, params) {
|
|
156
|
+
const res = await fetch(`${apiBase}/v1/me/waitlist`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: authHeaders(token, true),
|
|
159
|
+
body: JSON.stringify({
|
|
160
|
+
service_id: params.serviceId,
|
|
161
|
+
location_id: params.locationId,
|
|
162
|
+
provider_id: params.providerId,
|
|
163
|
+
date: params.date,
|
|
164
|
+
class_session_id: params.classSessionId
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
const data = await res.json();
|
|
168
|
+
if (!res.ok) throw new Error(data.detail || "No se pudo unir a la lista de espera.");
|
|
169
|
+
return data;
|
|
170
|
+
}
|
|
171
|
+
async function getMyWaitlist(apiBase, token) {
|
|
172
|
+
const res = await fetch(`${apiBase}/v1/me/waitlist`, { headers: authHeaders(token) });
|
|
173
|
+
const data = await res.json();
|
|
174
|
+
return data.data || [];
|
|
175
|
+
}
|
|
176
|
+
async function leaveWaitlist(apiBase, token, entryId) {
|
|
177
|
+
await fetch(`${apiBase}/v1/me/waitlist/${entryId}`, {
|
|
178
|
+
method: "DELETE",
|
|
179
|
+
headers: authHeaders(token)
|
|
180
|
+
});
|
|
181
|
+
}
|
|
136
182
|
|
|
137
183
|
// src/clientAuth.ts
|
|
138
184
|
var TOKEN_KEY = "client_token";
|
|
@@ -187,11 +233,14 @@ var DEFAULT_PROVIDER_COLORS = [
|
|
|
187
233
|
// src/hooks/useBookingFlow.ts
|
|
188
234
|
import { useEffect, useState } from "react";
|
|
189
235
|
var EMPTY_FORM = { firstName: "", lastName: "", email: "", phone: "", rut: "" };
|
|
236
|
+
function isValidPhone(phone) {
|
|
237
|
+
return phone.replace(/[^0-9]/g, "").length >= 8;
|
|
238
|
+
}
|
|
190
239
|
function validateClientForm(form) {
|
|
191
240
|
if (!form.firstName.trim()) return "Por favor ingresa tu nombre.";
|
|
192
241
|
if (!form.lastName.trim()) return "Por favor ingresa tu apellido.";
|
|
193
242
|
if (!form.email.trim() || !/\S+@\S+\.\S+/.test(form.email)) return "Por favor ingresa un email v\xE1lido.";
|
|
194
|
-
if (!form.phone.trim()) return "Por favor ingresa
|
|
243
|
+
if (!form.phone.trim() || !isValidPhone(form.phone)) return "Por favor ingresa un tel\xE9fono v\xE1lido.";
|
|
195
244
|
if (!form.rut.trim()) return "Por favor ingresa tu RUT.";
|
|
196
245
|
return null;
|
|
197
246
|
}
|
|
@@ -242,7 +291,8 @@ function useBookingFlow(opts) {
|
|
|
242
291
|
firstName: profile.first_name || f.firstName,
|
|
243
292
|
lastName: profile.last_name || f.lastName,
|
|
244
293
|
email: profile.email || f.email,
|
|
245
|
-
phone: profile.phone || f.phone
|
|
294
|
+
phone: profile.phone || f.phone,
|
|
295
|
+
rut: profile.rut || f.rut
|
|
246
296
|
}));
|
|
247
297
|
});
|
|
248
298
|
}, []);
|
|
@@ -341,11 +391,14 @@ function useBookingFlow(opts) {
|
|
|
341
391
|
// src/hooks/useClassSchedule.ts
|
|
342
392
|
import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
|
|
343
393
|
var EMPTY_FORM2 = { firstName: "", lastName: "", email: "", phone: "", rut: "" };
|
|
394
|
+
function isValidPhone2(phone) {
|
|
395
|
+
return phone.replace(/[^0-9]/g, "").length >= 8;
|
|
396
|
+
}
|
|
344
397
|
function validateClassForm(form) {
|
|
345
398
|
if (!form.firstName.trim()) return "Por favor ingresa tu nombre.";
|
|
346
399
|
if (!form.lastName.trim()) return "Por favor ingresa tu apellido.";
|
|
347
400
|
if (!form.email.trim() || !/\S+@\S+\.\S+/.test(form.email)) return "Por favor ingresa un email v\xE1lido.";
|
|
348
|
-
if (!form.phone.trim()) return "Por favor ingresa
|
|
401
|
+
if (!form.phone.trim() || !isValidPhone2(form.phone)) return "Por favor ingresa un tel\xE9fono v\xE1lido.";
|
|
349
402
|
return null;
|
|
350
403
|
}
|
|
351
404
|
function buildClassBookingPayload(form, client, selectedSlot, svc, locationId) {
|
|
@@ -397,7 +450,8 @@ function useClassSchedule(opts) {
|
|
|
397
450
|
firstName: profile.first_name || f.firstName,
|
|
398
451
|
lastName: profile.last_name || f.lastName,
|
|
399
452
|
email: profile.email || f.email,
|
|
400
|
-
phone: profile.phone || f.phone
|
|
453
|
+
phone: profile.phone || f.phone,
|
|
454
|
+
rut: profile.rut || f.rut
|
|
401
455
|
}));
|
|
402
456
|
});
|
|
403
457
|
}, []);
|
|
@@ -615,7 +669,9 @@ function useClientAccount(opts) {
|
|
|
615
669
|
const [history, setHistory] = useState5([]);
|
|
616
670
|
const [packages, setPackages] = useState5([]);
|
|
617
671
|
const [loading, setLoading] = useState5(true);
|
|
618
|
-
|
|
672
|
+
const [actionError, setActionError] = useState5("");
|
|
673
|
+
const [actionLoading, setActionLoading] = useState5(false);
|
|
674
|
+
const loadAll = () => {
|
|
619
675
|
const token = getClientToken();
|
|
620
676
|
if (!token) {
|
|
621
677
|
opts.onUnauthenticated();
|
|
@@ -633,18 +689,63 @@ function useClientAccount(opts) {
|
|
|
633
689
|
setPackages(pkgs);
|
|
634
690
|
setLoading(false);
|
|
635
691
|
}).catch(() => opts.onUnauthenticated());
|
|
692
|
+
};
|
|
693
|
+
useEffect3(() => {
|
|
694
|
+
loadAll();
|
|
636
695
|
}, []);
|
|
637
696
|
const handleLogout = () => {
|
|
638
697
|
clearClientToken();
|
|
639
698
|
opts.onLoggedOut();
|
|
640
699
|
};
|
|
700
|
+
const cancelBooking = async (bookingId) => {
|
|
701
|
+
const token = getClientToken();
|
|
702
|
+
if (!token) return;
|
|
703
|
+
setActionLoading(true);
|
|
704
|
+
setActionError("");
|
|
705
|
+
const result = await cancelMyBooking(opts.apiBase, token, bookingId);
|
|
706
|
+
if (!result.ok) {
|
|
707
|
+
setActionError(result.message);
|
|
708
|
+
setActionLoading(false);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
loadAll();
|
|
712
|
+
setActionLoading(false);
|
|
713
|
+
};
|
|
714
|
+
const rescheduleBooking = async (bookingId, startTime, endTime) => {
|
|
715
|
+
const token = getClientToken();
|
|
716
|
+
if (!token) return false;
|
|
717
|
+
setActionLoading(true);
|
|
718
|
+
setActionError("");
|
|
719
|
+
const result = await rescheduleMyBooking(opts.apiBase, token, bookingId, startTime, endTime);
|
|
720
|
+
setActionLoading(false);
|
|
721
|
+
if (!result.ok) {
|
|
722
|
+
setActionError(result.message);
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
loadAll();
|
|
726
|
+
return true;
|
|
727
|
+
};
|
|
641
728
|
const totalCredits = packages.reduce((s, p) => s + p.remaining_credits, 0);
|
|
642
|
-
return {
|
|
729
|
+
return {
|
|
730
|
+
profile,
|
|
731
|
+
upcoming,
|
|
732
|
+
history,
|
|
733
|
+
packages,
|
|
734
|
+
loading,
|
|
735
|
+
totalCredits,
|
|
736
|
+
handleLogout,
|
|
737
|
+
cancelBooking,
|
|
738
|
+
rescheduleBooking,
|
|
739
|
+
actionError,
|
|
740
|
+
setActionError,
|
|
741
|
+
actionLoading
|
|
742
|
+
};
|
|
643
743
|
}
|
|
644
744
|
export {
|
|
645
745
|
DAY_NAMES,
|
|
646
746
|
DAY_NAMES_FULL,
|
|
647
747
|
DEFAULT_PROVIDER_COLORS,
|
|
748
|
+
cancelMyBooking,
|
|
648
749
|
clearClientToken,
|
|
649
750
|
createBooking,
|
|
650
751
|
createClient,
|
|
@@ -657,11 +758,15 @@ export {
|
|
|
657
758
|
getMondayOf,
|
|
658
759
|
getMyBookings,
|
|
659
760
|
getMyPackages,
|
|
761
|
+
getMyWaitlist,
|
|
762
|
+
joinWaitlist,
|
|
763
|
+
leaveWaitlist,
|
|
660
764
|
listLocations,
|
|
661
765
|
listProviders,
|
|
662
766
|
listServices,
|
|
663
767
|
loginWithGoogle,
|
|
664
768
|
register,
|
|
769
|
+
rescheduleMyBooking,
|
|
665
770
|
sendMagicLink,
|
|
666
771
|
setClientToken,
|
|
667
772
|
useBookingFlow,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api.ts","../src/clientAuth.ts","../src/dateUtils.ts","../src/hooks/useBookingFlow.ts","../src/hooks/useClassSchedule.ts","../src/hooks/useMagicLinkAuth.ts","../src/hooks/useRegisterForm.ts","../src/hooks/useClientAccount.ts"],"sourcesContent":["import type {\n ApiConfig,\n BookingCreateInput,\n BookingRecord,\n BookingResult,\n ClientFormValues,\n ClientProfile,\n Location,\n PackageRecord,\n Provider,\n Service,\n Slot,\n} from \"./types\";\n\nfunction headers(cfg: ApiConfig, withJson = false): Record<string, string> {\n const h: Record<string, string> = { \"X-API-Key\": cfg.apiKey };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nfunction authHeaders(token: string, withJson = false): Record<string, string> {\n const h: Record<string, string> = { Authorization: `Bearer ${token}` };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nexport async function listLocations(cfg: ApiConfig): Promise<Location[]> {\n const res = await fetch(`${cfg.apiBase}/v1/locations`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listServices(cfg: ApiConfig, perPage = 100): Promise<Service[]> {\n const res = await fetch(`${cfg.apiBase}/v1/services?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listProviders(cfg: ApiConfig, perPage = 100): Promise<Provider[]> {\n const res = await fetch(`${cfg.apiBase}/v1/providers?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getAvailableSlots(\n cfg: ApiConfig,\n params: { locationId: string; startDate: string; serviceId?: string; providerId?: string }\n): Promise<Slot[]> {\n const q = new URLSearchParams({ location_id: params.locationId, start_date: params.startDate });\n if (params.serviceId) q.set(\"service_id\", params.serviceId);\n if (params.providerId) q.set(\"provider_id\", params.providerId);\n const res = await fetch(`${cfg.apiBase}/v1/available-slots?${q.toString()}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data?.slots || [];\n}\n\nexport async function createClient(cfg: ApiConfig, form: ClientFormValues): Promise<{ id: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/clients`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone,\n rut: form.rut || undefined,\n }),\n });\n if (!res.ok) throw new Error(\"Error registrando datos\");\n return res.json();\n}\n\nexport async function createBooking(\n cfg: ApiConfig,\n input: BookingCreateInput,\n clientToken?: string\n): Promise<BookingResult> {\n // A logged-in client's token takes priority so the server attributes the\n // booking to their account rather than treating it as anonymous — see\n // create_booking in routes/bookings.py, which ignores any client_id sent\n // by an unauthenticated caller.\n const reqHeaders = headers(cfg, true);\n if (clientToken) reqHeaders.Authorization = `Bearer ${clientToken}`;\n const res = await fetch(`${cfg.apiBase}/v1/bookings`, {\n method: \"POST\",\n headers: reqHeaders,\n body: JSON.stringify(input),\n });\n if (res.status === 409) {\n const data = await res.json().catch(() => ({}));\n return { ok: false, conflict: true, message: data.detail || \"Ese horario ya no está disponible.\" };\n }\n if (!res.ok) {\n return { ok: false, conflict: false, message: \"Error creando la reserva. Por favor intenta nuevamente.\" };\n }\n return { ok: true, booking: await res.json() };\n}\n\nexport async function sendMagicLink(\n cfg: ApiConfig,\n email: string,\n frontendUrl: string,\n profile?: { firstName?: string; lastName?: string; phone?: string; rut?: string }\n): Promise<void> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n frontend_url: frontendUrl,\n first_name: profile?.firstName,\n last_name: profile?.lastName,\n phone: profile?.phone,\n rut: profile?.rut,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error enviando el link.\");\n}\n\nexport async function verifyMagicLink(cfg: ApiConfig, token: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link/verify`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ token }),\n });\n const data = await res.json();\n if (!data.access_token) throw new Error(data.detail || \"Link inválido.\");\n return data;\n}\n\nexport async function loginWithGoogle(cfg: ApiConfig, credential: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/google`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ credential }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error con Google.\");\n return data;\n}\n\nexport async function register(\n cfg: ApiConfig,\n form: { firstName: string; lastName: string; email: string; phone?: string; password: string }\n): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/register`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone || undefined,\n password: form.password,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error al registrarse.\");\n return data;\n}\n\nexport async function getMe(apiBase: string, token: string): Promise<ClientProfile> {\n const res = await fetch(`${apiBase}/v1/me`, { headers: authHeaders(token) });\n return res.json();\n}\n\nexport async function getMyBookings(\n apiBase: string,\n token: string,\n params?: { upcoming?: boolean; perPage?: number }\n): Promise<BookingRecord[]> {\n const q = new URLSearchParams();\n if (params?.upcoming) q.set(\"upcoming\", \"true\");\n q.set(\"per_page\", String(params?.perPage ?? 20));\n const res = await fetch(`${apiBase}/v1/me/bookings?${q.toString()}`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getMyPackages(apiBase: string, token: string): Promise<PackageRecord[]> {\n const res = await fetch(`${apiBase}/v1/me/packages`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n","import { getMe } from \"./api\";\nimport type { ClientProfile } from \"./types\";\n\nconst TOKEN_KEY = \"client_token\";\n\nexport function getClientToken(): string | null {\n if (typeof window === \"undefined\") return null;\n return localStorage.getItem(TOKEN_KEY);\n}\n\nexport function setClientToken(token: string): void {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(TOKEN_KEY, token);\n}\n\nexport function clearClientToken(): void {\n if (typeof window === \"undefined\") return;\n localStorage.removeItem(TOKEN_KEY);\n}\n\nexport async function fetchMe(apiBase: string, token: string): Promise<ClientProfile | null> {\n try {\n return await getMe(apiBase, token);\n } catch {\n return null;\n }\n}\n","export function getMondayOf(date: Date): Date {\n const d = new Date(date);\n const day = (d.getDay() + 6) % 7;\n d.setDate(d.getDate() - day);\n d.setHours(0, 0, 0, 0);\n return d;\n}\n\nexport function dateStr(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n}\n\nexport function formatMonthRange(monday: Date): string {\n const sunday = new Date(monday);\n sunday.setDate(monday.getDate() + 6);\n const opts: Intl.DateTimeFormatOptions = { day: \"numeric\", month: \"long\" };\n const start = monday.toLocaleDateString(\"es-CL\", opts);\n const end = sunday.toLocaleDateString(\"es-CL\", opts);\n return `${start} – ${end}`;\n}\n\nexport const DAY_NAMES = [\"Lun\", \"Mar\", \"Mié\", \"Jue\", \"Vie\", \"Sáb\", \"Dom\"];\nexport const DAY_NAMES_FULL = [\"Lunes\", \"Martes\", \"Miércoles\", \"Jueves\", \"Viernes\", \"Sábado\", \"Domingo\"];\n\nexport interface ProviderColor {\n bg: string;\n border: string;\n text: string;\n}\n\nexport const DEFAULT_PROVIDER_COLORS: ProviderColor[] = [\n { bg: \"#e8f5f0\", border: \"#2bbf8e\", text: \"#1a7a5a\" },\n { bg: \"#eef0fb\", border: \"#6b7de8\", text: \"#3d4fa8\" },\n { bg: \"#fdf3e8\", border: \"#e8a24b\", text: \"#a0600a\" },\n { bg: \"#fdeef4\", border: \"#e86b9a\", text: \"#a03060\" },\n];\n","import { useEffect, useState } from \"react\";\nimport {\n createBooking,\n getAvailableSlots,\n listLocations,\n listProviders,\n listServices,\n} from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport type {\n ApiConfig,\n BookingRecord,\n ClientFormValues,\n ClientProfile,\n Location,\n Provider,\n Service,\n Slot,\n} from \"../types\";\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction validateClientForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim()) return \"Por favor ingresa tu teléfono.\";\n if (!form.rut.trim()) return \"Por favor ingresa tu RUT.\";\n return null;\n}\n\ninterface BuildBookingPayloadArgs {\n form: ClientFormValues;\n client: ClientProfile | null;\n selectedDate: string;\n selectedSlot: Slot;\n selectedSvc: string;\n selectedLoc: string;\n svcPrice?: string;\n defaultNotes?: string;\n overrides?: SubmitBookingOverrides;\n}\n\nfunction buildBookingPayload(args: BuildBookingPayloadArgs) {\n const { form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc } = args;\n const price = args.overrides?.price ?? args.svcPrice ?? \"0\";\n const notes = args.overrides?.notes ?? args.defaultNotes;\n return {\n start_time: `${selectedDate}T${selectedSlot.start_time}:00-04:00`,\n end_time: `${selectedDate}T${selectedSlot.end_time}:00-04:00`,\n service_id: selectedSvc,\n service_provider_id: selectedSlot.provider_id,\n location_id: selectedLoc,\n price,\n notes,\n // Ignored server-side unless a client/staff auth token proves the caller's\n // identity; for a logged-in client it's redundant but harmless to include.\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nexport interface UseBookingFlowOptions extends ApiConfig {\n onBookingComplete: (booking: BookingRecord) => void;\n /** Default notes attached to every booking (e.g. \"Reserva realizada desde sitio web X\"). */\n defaultNotes?: string;\n}\n\nexport interface SubmitBookingOverrides {\n /** Override the service's list price (e.g. magnolia's price-variant step). */\n price?: string;\n /** Override defaultNotes for this specific submission. */\n notes?: string;\n}\n\n/**\n * Shared booking-flow logic: catalog fetching (locations/services/providers/\n * slots), logged-in-client prefill, form validation, and submit-with-409-\n * handling. Does NOT own step navigation or app-specific filtering (e.g.\n * magnolia's category/price-variant logic) — those stay in the consuming\n * component, layered on top of the `services`/`providers` this hook returns.\n */\nexport function useBookingFlow(opts: UseBookingFlowOptions) {\n const [locations, setLocations] = useState<Location[]>([]);\n const [services, setServices] = useState<Service[]>([]);\n const [providers, setProviders] = useState<Provider[]>([]);\n const [slots, setSlots] = useState<Slot[]>([]);\n\n const [selectedLoc, setSelectedLoc] = useState(\"\");\n const [selectedSvc, setSelectedSvc] = useState(\"\");\n const [selectedProv, setSelectedProv] = useState(\"\");\n const [selectedDate, setSelectedDate] = useState(\"\");\n const [selectedSlot, setSelectedSlot] = useState<Slot | null>(null);\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n // Prefill from a logged-in client session, if any.\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n listLocations(opts)\n .then(setLocations)\n .catch(() => setError(\"Error cargando sucursales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (!selectedLoc) return;\n listServices(opts)\n .then(setServices)\n .catch(() => setError(\"Error cargando servicios\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc]);\n\n useEffect(() => {\n if (!selectedSvc) return;\n listProviders(opts)\n .then(setProviders)\n .catch(() => setError(\"Error cargando profesionales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedSvc]);\n\n useEffect(() => {\n if (!selectedLoc || !selectedDate) return;\n setLoading(true);\n getAvailableSlots(opts, {\n locationId: selectedLoc,\n startDate: selectedDate,\n serviceId: selectedSvc || undefined,\n providerId: selectedProv || undefined,\n })\n .then(s => { setSlots(s); setLoading(false); })\n .catch(() => { setError(\"Error cargando horarios\"); setLoading(false); });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc, selectedSvc, selectedProv, selectedDate]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (overrides?: SubmitBookingOverrides) => {\n if (!selectedSlot) return;\n const validationError = validateClientForm(form);\n if (validationError) { setError(validationError); return; }\n\n setLoading(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.id === selectedSvc);\n const token = getClientToken();\n const payload = buildBookingPayload({\n form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc, overrides,\n svcPrice: svc?.price, defaultNotes: opts.defaultNotes,\n });\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar la reserva\");\n setLoading(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(result.message);\n if (result.conflict) setSelectedSlot(null);\n setLoading(false);\n return;\n }\n opts.onBookingComplete(result.booking);\n }\n\n return {\n locations, services, providers, slots,\n selectedLoc, setSelectedLoc,\n selectedSvc, setSelectedSvc,\n selectedProv, setSelectedProv,\n selectedDate, setSelectedDate,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n loading, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { createBooking, getAvailableSlots, listLocations, listServices } from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport { dateStr, DEFAULT_PROVIDER_COLORS, getMondayOf, type ProviderColor } from \"../dateUtils\";\nimport type { ApiConfig, ClientFormValues, ClientProfile, Service, Slot } from \"../types\";\n\nexport interface SelectedClassSlot {\n slot: Slot;\n date: string;\n}\n\nexport interface UseClassScheduleOptions extends ApiConfig {\n onBookingComplete: () => void;\n colors?: ProviderColor[];\n}\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction validateClassForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim()) return \"Por favor ingresa tu teléfono.\";\n return null;\n}\n\nfunction buildClassBookingPayload(\n form: ClientFormValues,\n client: ClientProfile | null,\n selectedSlot: SelectedClassSlot,\n svc: Service | undefined,\n locationId: string\n) {\n return {\n start_time: `${selectedSlot.date}T${selectedSlot.slot.start_time}:00-04:00`,\n end_time: `${selectedSlot.date}T${selectedSlot.slot.end_time}:00-04:00`,\n service_id: svc?.id ?? \"\",\n service_provider_id: selectedSlot.slot.provider_id,\n location_id: locationId,\n price: svc?.price || \"0\",\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nfunction resolveConflictMessage(result: { conflict: boolean; message: string }): string {\n return result.conflict\n ? \"Ese cupo ya fue tomado por otra persona. Cierra esta ventana y elige otro horario.\"\n : result.message;\n}\n\n/**\n * Shared weekly group-class schedule logic: week-of-slots fetching, provider\n * color-palette assignment, and submit-with-409-handling. The week-grid table\n * JSX and modal markup stay in the consuming component — this hook only owns\n * data and state transitions.\n */\nexport function useClassSchedule(opts: UseClassScheduleOptions) {\n const colors = opts.colors ?? DEFAULT_PROVIDER_COLORS;\n\n const [monday, setMonday] = useState<Date>(() => getMondayOf(new Date()));\n const [slotsByDay, setSlotsByDay] = useState<Record<string, Slot[]>>({});\n const [services, setServices] = useState<Service[]>([]);\n const [locationId, setLocationId] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [selectedSlot, setSelectedSlot] = useState<SelectedClassSlot | null>(null);\n const [providerColorMap, setProviderColorMap] = useState<Record<string, ProviderColor>>({});\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n useEffect(() => {\n Promise.all([listLocations(opts), listServices(opts, 50)])\n .then(([locs, svcs]) => {\n if (locs.length) setLocationId(locs[0].id);\n setServices(svcs);\n })\n .catch(() => setError(\"Error cargando datos\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const loadWeek = useCallback(async (mon: Date, locId: string) => {\n if (!locId) return;\n setLoading(true);\n const days = Array.from({ length: 7 }, (_, i) => {\n const d = new Date(mon);\n d.setDate(mon.getDate() + i);\n return d;\n });\n\n const results = await Promise.all(\n days.map(async d => {\n const ds = dateStr(d);\n try {\n const slots = await getAvailableSlots(opts, { locationId: locId, startDate: ds });\n return { date: ds, slots };\n } catch {\n return { date: ds, slots: [] as Slot[] };\n }\n })\n );\n\n const map: Record<string, Slot[]> = {};\n const colorMap: Record<string, ProviderColor> = {};\n let colorIdx = 0;\n results.forEach(({ date, slots }) => {\n map[date] = slots;\n slots.forEach(s => {\n if (!colorMap[s.provider_id]) {\n colorMap[s.provider_id] = colors[colorIdx % colors.length];\n colorIdx++;\n }\n });\n });\n\n setSlotsByDay(map);\n setProviderColorMap(prev => ({ ...prev, ...colorMap }));\n setLoading(false);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (locationId) loadWeek(monday, locationId);\n }, [monday, locationId, loadWeek]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!selectedSlot) return;\n const validationError = validateClassForm(form);\n if (validationError) { setError(validationError); return; }\n setSubmitting(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.name === selectedSlot.slot.service_name);\n const token = getClientToken();\n const payload = buildClassBookingPayload(form, client, selectedSlot, svc, locationId);\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar\");\n setSubmitting(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(resolveConflictMessage(result));\n setSubmitting(false);\n return;\n }\n opts.onBookingComplete();\n }\n\n return {\n monday, setMonday,\n slotsByDay, loading, providerColorMap,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n submitting, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useState } from \"react\";\nimport { loginWithGoogle, sendMagicLink } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\ndeclare global {\n interface Window {\n google?: {\n accounts: {\n id: {\n initialize: (config: { client_id: string; callback: (response: { credential: string }) => void }) => void;\n prompt: () => void;\n };\n };\n };\n }\n}\n\nexport interface UseMagicLinkAuthOptions extends ApiConfig {\n googleClientId?: string;\n frontendUrl: string;\n onGoogleLoggedIn: () => void;\n}\n\nexport type MagicLinkStep = \"email\" | \"sent\";\n\nexport function useMagicLinkAuth(opts: UseMagicLinkAuthOptions) {\n const [email, setEmail] = useState(\"\");\n // Optional — only relevant on a signup-style form (e.g. registro pages)\n // that collects a bit more than login does. Login pages can ignore these.\n const [firstName, setFirstName] = useState(\"\");\n const [lastName, setLastName] = useState(\"\");\n const [phone, setPhone] = useState(\"\");\n const [rut, setRut] = useState(\"\");\n const [step, setStep] = useState<MagicLinkStep>(\"email\");\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const handleMagicLink = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!email.trim() || !/\\S+@\\S+\\.\\S+/.test(email)) {\n setError(\"Ingresa un email válido.\");\n return;\n }\n setLoading(true);\n setError(\"\");\n try {\n await sendMagicLink(opts, email, opts.frontendUrl, {\n firstName: firstName || undefined,\n lastName: lastName || undefined,\n phone: phone || undefined,\n rut: rut || undefined,\n });\n setStep(\"sent\");\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error enviando el link.\");\n } finally {\n setLoading(false);\n }\n };\n\n const handleGoogle = () => {\n if (!opts.googleClientId || typeof window === \"undefined\" || !window.google) return;\n window.google.accounts.id.initialize({\n client_id: opts.googleClientId,\n callback: async (response: { credential: string }) => {\n setLoading(true);\n try {\n const data = await loginWithGoogle(opts, response.credential);\n setClientToken(data.access_token);\n opts.onGoogleLoggedIn();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error con Google.\");\n setLoading(false);\n }\n },\n });\n window.google.accounts.id.prompt();\n };\n\n const reset = () => {\n setStep(\"email\");\n setError(\"\");\n };\n\n return {\n email, setEmail,\n firstName, setFirstName,\n lastName, setLastName,\n phone, setPhone,\n rut, setRut,\n step, error, loading, handleMagicLink, handleGoogle, reset,\n };\n}\n","import { useState } from \"react\";\nimport { register } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\nexport interface RegisterFormValues {\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n password: string;\n confirm: string;\n}\n\nconst EMPTY: RegisterFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", password: \"\", confirm: \"\" };\n\nexport interface UseRegisterFormOptions extends ApiConfig {\n onRegistered: () => void;\n}\n\nexport function useRegisterForm(opts: UseRegisterFormOptions) {\n const [form, setForm] = useState<RegisterFormValues>(EMPTY);\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const updateField = (fields: Partial<RegisterFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const handleSubmit = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!form.firstName.trim()) { setError(\"Ingresa tu nombre.\"); return; }\n if (!form.lastName.trim()) { setError(\"Ingresa tu apellido.\"); return; }\n if (!form.email.trim()) { setError(\"Ingresa tu email.\"); return; }\n if (form.password.length < 6) { setError(\"La contraseña debe tener al menos 6 caracteres.\"); return; }\n if (form.password !== form.confirm) { setError(\"Las contraseñas no coinciden.\"); return; }\n setLoading(true);\n setError(\"\");\n try {\n const data = await register(opts, form);\n setClientToken(data.access_token);\n opts.onRegistered();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al registrarse.\");\n } finally {\n setLoading(false);\n }\n };\n\n return { form, updateField, error, loading, handleSubmit };\n}\n","import { useEffect, useState } from \"react\";\nimport { getMe, getMyBookings, getMyPackages } from \"../api\";\nimport { clearClientToken, getClientToken } from \"../clientAuth\";\nimport type { BookingRecord, ClientProfile, PackageRecord } from \"../types\";\n\nexport interface UseClientAccountOptions {\n apiBase: string;\n onUnauthenticated: () => void;\n onLoggedOut: () => void;\n}\n\nexport function useClientAccount(opts: UseClientAccountOptions) {\n const [profile, setProfile] = useState<ClientProfile | null>(null);\n const [upcoming, setUpcoming] = useState<BookingRecord[]>([]);\n const [history, setHistory] = useState<BookingRecord[]>([]);\n const [packages, setPackages] = useState<PackageRecord[]>([]);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n const token = getClientToken();\n if (!token) {\n opts.onUnauthenticated();\n return;\n }\n Promise.all([\n getMe(opts.apiBase, token),\n getMyBookings(opts.apiBase, token, { upcoming: true, perPage: 10 }),\n getMyBookings(opts.apiBase, token, { perPage: 20 }),\n getMyPackages(opts.apiBase, token),\n ])\n .then(([prof, up, hist, pkgs]) => {\n setProfile(prof);\n setUpcoming(up);\n setHistory(hist);\n setPackages(pkgs);\n setLoading(false);\n })\n .catch(() => opts.onUnauthenticated());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const handleLogout = () => {\n clearClientToken();\n opts.onLoggedOut();\n };\n\n const totalCredits = packages.reduce((s, p) => s + p.remaining_credits, 0);\n\n return { profile, upcoming, history, packages, loading, totalCredits, handleLogout };\n}\n"],"mappings":"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","names":["useEffect","useState","EMPTY_FORM","useState","useEffect","useState","useState","useState","useState","useEffect","useState","useState","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/api.ts","../src/clientAuth.ts","../src/dateUtils.ts","../src/hooks/useBookingFlow.ts","../src/hooks/useClassSchedule.ts","../src/hooks/useMagicLinkAuth.ts","../src/hooks/useRegisterForm.ts","../src/hooks/useClientAccount.ts"],"sourcesContent":["import type {\n ApiConfig,\n BookingCreateInput,\n BookingRecord,\n BookingResult,\n ClientFormValues,\n ClientProfile,\n Location,\n PackageRecord,\n Provider,\n Service,\n Slot,\n WaitlistEntry,\n} from \"./types\";\n\nfunction headers(cfg: ApiConfig, withJson = false): Record<string, string> {\n const h: Record<string, string> = { \"X-API-Key\": cfg.apiKey };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nfunction authHeaders(token: string, withJson = false): Record<string, string> {\n const h: Record<string, string> = { Authorization: `Bearer ${token}` };\n if (withJson) h[\"Content-Type\"] = \"application/json\";\n return h;\n}\n\nexport async function listLocations(cfg: ApiConfig): Promise<Location[]> {\n const res = await fetch(`${cfg.apiBase}/v1/locations`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listServices(cfg: ApiConfig, perPage = 100): Promise<Service[]> {\n const res = await fetch(`${cfg.apiBase}/v1/services?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function listProviders(cfg: ApiConfig, perPage = 100): Promise<Provider[]> {\n const res = await fetch(`${cfg.apiBase}/v1/providers?per_page=${perPage}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getAvailableSlots(\n cfg: ApiConfig,\n params: { locationId: string; startDate: string; serviceId?: string; providerId?: string }\n): Promise<Slot[]> {\n const q = new URLSearchParams({ location_id: params.locationId, start_date: params.startDate });\n if (params.serviceId) q.set(\"service_id\", params.serviceId);\n if (params.providerId) q.set(\"provider_id\", params.providerId);\n const res = await fetch(`${cfg.apiBase}/v1/available-slots?${q.toString()}`, { headers: headers(cfg) });\n const data = await res.json();\n return data.data?.slots || [];\n}\n\nexport async function createClient(cfg: ApiConfig, form: ClientFormValues): Promise<{ id: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/clients`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone,\n rut: form.rut || undefined,\n }),\n });\n if (!res.ok) throw new Error(\"Error registrando datos\");\n return res.json();\n}\n\nexport async function createBooking(\n cfg: ApiConfig,\n input: BookingCreateInput,\n clientToken?: string\n): Promise<BookingResult> {\n // A logged-in client's token takes priority so the server attributes the\n // booking to their account rather than treating it as anonymous — see\n // create_booking in routes/bookings.py, which ignores any client_id sent\n // by an unauthenticated caller.\n const reqHeaders = headers(cfg, true);\n if (clientToken) reqHeaders.Authorization = `Bearer ${clientToken}`;\n const res = await fetch(`${cfg.apiBase}/v1/bookings`, {\n method: \"POST\",\n headers: reqHeaders,\n body: JSON.stringify(input),\n });\n if (res.status === 409) {\n const data = await res.json().catch(() => ({}));\n return { ok: false, conflict: true, message: data.detail || \"Ese horario ya no está disponible.\" };\n }\n if (!res.ok) {\n return { ok: false, conflict: false, message: \"Error creando la reserva. Por favor intenta nuevamente.\" };\n }\n return { ok: true, booking: await res.json() };\n}\n\nexport async function sendMagicLink(\n cfg: ApiConfig,\n email: string,\n frontendUrl: string,\n profile?: { firstName?: string; lastName?: string; phone?: string; rut?: string }\n): Promise<void> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n frontend_url: frontendUrl,\n first_name: profile?.firstName,\n last_name: profile?.lastName,\n phone: profile?.phone,\n rut: profile?.rut,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error enviando el link.\");\n}\n\nexport async function verifyMagicLink(cfg: ApiConfig, token: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/magic-link/verify`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ token }),\n });\n const data = await res.json();\n if (!data.access_token) throw new Error(data.detail || \"Link inválido.\");\n return data;\n}\n\nexport async function loginWithGoogle(cfg: ApiConfig, credential: string): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/google`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({ credential }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error con Google.\");\n return data;\n}\n\nexport async function register(\n cfg: ApiConfig,\n form: { firstName: string; lastName: string; email: string; phone?: string; password: string }\n): Promise<{ access_token: string }> {\n const res = await fetch(`${cfg.apiBase}/v1/client-auth/register`, {\n method: \"POST\",\n headers: headers(cfg, true),\n body: JSON.stringify({\n first_name: form.firstName,\n last_name: form.lastName,\n email: form.email,\n phone: form.phone || undefined,\n password: form.password,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"Error al registrarse.\");\n return data;\n}\n\nexport async function getMe(apiBase: string, token: string): Promise<ClientProfile> {\n const res = await fetch(`${apiBase}/v1/me`, { headers: authHeaders(token) });\n return res.json();\n}\n\nexport async function getMyBookings(\n apiBase: string,\n token: string,\n params?: { upcoming?: boolean; perPage?: number }\n): Promise<BookingRecord[]> {\n const q = new URLSearchParams();\n if (params?.upcoming) q.set(\"upcoming\", \"true\");\n q.set(\"per_page\", String(params?.perPage ?? 20));\n const res = await fetch(`${apiBase}/v1/me/bookings?${q.toString()}`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function getMyPackages(apiBase: string, token: string): Promise<PackageRecord[]> {\n const res = await fetch(`${apiBase}/v1/me/packages`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\n/** Result of a client cancelling/rescheduling their own booking — callers\n * branch on `.ok` to show the server's specific reason (cutoff window,\n * overlap, etc.) instead of a generic error. */\nexport type MyBookingActionResult =\n | { ok: true; booking: BookingRecord }\n | { ok: false; message: string };\n\nexport async function cancelMyBooking(\n apiBase: string, token: string, bookingId: string\n): Promise<MyBookingActionResult> {\n const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/cancel`, {\n method: \"POST\",\n headers: authHeaders(token),\n });\n const data = await res.json().catch(() => ({}));\n if (!res.ok) return { ok: false, message: data.detail || \"No se pudo cancelar la reserva.\" };\n return { ok: true, booking: data };\n}\n\nexport async function rescheduleMyBooking(\n apiBase: string, token: string, bookingId: string, startTime: string, endTime: string\n): Promise<MyBookingActionResult> {\n const res = await fetch(`${apiBase}/v1/me/bookings/${bookingId}/reschedule`, {\n method: \"POST\",\n headers: authHeaders(token, true),\n body: JSON.stringify({ start_time: startTime, end_time: endTime }),\n });\n const data = await res.json().catch(() => ({}));\n if (!res.ok) return { ok: false, message: data.detail || \"No se pudo reprogramar la reserva.\" };\n return { ok: true, booking: data };\n}\n\nexport async function joinWaitlist(\n apiBase: string,\n token: string,\n params: { serviceId: string; locationId: string; providerId?: string; date?: string; classSessionId?: string }\n): Promise<WaitlistEntry> {\n const res = await fetch(`${apiBase}/v1/me/waitlist`, {\n method: \"POST\",\n headers: authHeaders(token, true),\n body: JSON.stringify({\n service_id: params.serviceId,\n location_id: params.locationId,\n provider_id: params.providerId,\n date: params.date,\n class_session_id: params.classSessionId,\n }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.detail || \"No se pudo unir a la lista de espera.\");\n return data;\n}\n\nexport async function getMyWaitlist(apiBase: string, token: string): Promise<WaitlistEntry[]> {\n const res = await fetch(`${apiBase}/v1/me/waitlist`, { headers: authHeaders(token) });\n const data = await res.json();\n return data.data || [];\n}\n\nexport async function leaveWaitlist(apiBase: string, token: string, entryId: string): Promise<void> {\n await fetch(`${apiBase}/v1/me/waitlist/${entryId}`, {\n method: \"DELETE\",\n headers: authHeaders(token),\n });\n}\n","import { getMe } from \"./api\";\nimport type { ClientProfile } from \"./types\";\n\nconst TOKEN_KEY = \"client_token\";\n\nexport function getClientToken(): string | null {\n if (typeof window === \"undefined\") return null;\n return localStorage.getItem(TOKEN_KEY);\n}\n\nexport function setClientToken(token: string): void {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(TOKEN_KEY, token);\n}\n\nexport function clearClientToken(): void {\n if (typeof window === \"undefined\") return;\n localStorage.removeItem(TOKEN_KEY);\n}\n\nexport async function fetchMe(apiBase: string, token: string): Promise<ClientProfile | null> {\n try {\n return await getMe(apiBase, token);\n } catch {\n return null;\n }\n}\n","export function getMondayOf(date: Date): Date {\n const d = new Date(date);\n const day = (d.getDay() + 6) % 7;\n d.setDate(d.getDate() - day);\n d.setHours(0, 0, 0, 0);\n return d;\n}\n\nexport function dateStr(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, \"0\")}-${String(d.getDate()).padStart(2, \"0\")}`;\n}\n\nexport function formatMonthRange(monday: Date): string {\n const sunday = new Date(monday);\n sunday.setDate(monday.getDate() + 6);\n const opts: Intl.DateTimeFormatOptions = { day: \"numeric\", month: \"long\" };\n const start = monday.toLocaleDateString(\"es-CL\", opts);\n const end = sunday.toLocaleDateString(\"es-CL\", opts);\n return `${start} – ${end}`;\n}\n\nexport const DAY_NAMES = [\"Lun\", \"Mar\", \"Mié\", \"Jue\", \"Vie\", \"Sáb\", \"Dom\"];\nexport const DAY_NAMES_FULL = [\"Lunes\", \"Martes\", \"Miércoles\", \"Jueves\", \"Viernes\", \"Sábado\", \"Domingo\"];\n\nexport interface ProviderColor {\n bg: string;\n border: string;\n text: string;\n}\n\nexport const DEFAULT_PROVIDER_COLORS: ProviderColor[] = [\n { bg: \"#e8f5f0\", border: \"#2bbf8e\", text: \"#1a7a5a\" },\n { bg: \"#eef0fb\", border: \"#6b7de8\", text: \"#3d4fa8\" },\n { bg: \"#fdf3e8\", border: \"#e8a24b\", text: \"#a0600a\" },\n { bg: \"#fdeef4\", border: \"#e86b9a\", text: \"#a03060\" },\n];\n","import { useEffect, useState } from \"react\";\nimport {\n createBooking,\n getAvailableSlots,\n listLocations,\n listProviders,\n listServices,\n} from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport type {\n ApiConfig,\n BookingRecord,\n ClientFormValues,\n ClientProfile,\n Location,\n Provider,\n Service,\n Slot,\n} from \"../types\";\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction isValidPhone(phone: string): boolean {\n return phone.replace(/[^0-9]/g, \"\").length >= 8;\n}\n\nfunction validateClientForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim() || !isValidPhone(form.phone)) return \"Por favor ingresa un teléfono válido.\";\n if (!form.rut.trim()) return \"Por favor ingresa tu RUT.\";\n return null;\n}\n\ninterface BuildBookingPayloadArgs {\n form: ClientFormValues;\n client: ClientProfile | null;\n selectedDate: string;\n selectedSlot: Slot;\n selectedSvc: string;\n selectedLoc: string;\n svcPrice?: string;\n defaultNotes?: string;\n overrides?: SubmitBookingOverrides;\n}\n\nfunction buildBookingPayload(args: BuildBookingPayloadArgs) {\n const { form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc } = args;\n const price = args.overrides?.price ?? args.svcPrice ?? \"0\";\n const notes = args.overrides?.notes ?? args.defaultNotes;\n return {\n start_time: `${selectedDate}T${selectedSlot.start_time}:00-04:00`,\n end_time: `${selectedDate}T${selectedSlot.end_time}:00-04:00`,\n service_id: selectedSvc,\n service_provider_id: selectedSlot.provider_id,\n location_id: selectedLoc,\n price,\n notes,\n // Ignored server-side unless a client/staff auth token proves the caller's\n // identity; for a logged-in client it's redundant but harmless to include.\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nexport interface UseBookingFlowOptions extends ApiConfig {\n onBookingComplete: (booking: BookingRecord) => void;\n /** Default notes attached to every booking (e.g. \"Reserva realizada desde sitio web X\"). */\n defaultNotes?: string;\n}\n\nexport interface SubmitBookingOverrides {\n /** Override the service's list price (e.g. magnolia's price-variant step). */\n price?: string;\n /** Override defaultNotes for this specific submission. */\n notes?: string;\n}\n\n/**\n * Shared booking-flow logic: catalog fetching (locations/services/providers/\n * slots), logged-in-client prefill, form validation, and submit-with-409-\n * handling. Does NOT own step navigation or app-specific filtering (e.g.\n * magnolia's category/price-variant logic) — those stay in the consuming\n * component, layered on top of the `services`/`providers` this hook returns.\n */\nexport function useBookingFlow(opts: UseBookingFlowOptions) {\n const [locations, setLocations] = useState<Location[]>([]);\n const [services, setServices] = useState<Service[]>([]);\n const [providers, setProviders] = useState<Provider[]>([]);\n const [slots, setSlots] = useState<Slot[]>([]);\n\n const [selectedLoc, setSelectedLoc] = useState(\"\");\n const [selectedSvc, setSelectedSvc] = useState(\"\");\n const [selectedProv, setSelectedProv] = useState(\"\");\n const [selectedDate, setSelectedDate] = useState(\"\");\n const [selectedSlot, setSelectedSlot] = useState<Slot | null>(null);\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n // Prefill from a logged-in client session, if any.\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n rut: profile.rut || f.rut,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n listLocations(opts)\n .then(setLocations)\n .catch(() => setError(\"Error cargando sucursales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (!selectedLoc) return;\n listServices(opts)\n .then(setServices)\n .catch(() => setError(\"Error cargando servicios\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc]);\n\n useEffect(() => {\n if (!selectedSvc) return;\n listProviders(opts)\n .then(setProviders)\n .catch(() => setError(\"Error cargando profesionales\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedSvc]);\n\n useEffect(() => {\n if (!selectedLoc || !selectedDate) return;\n setLoading(true);\n getAvailableSlots(opts, {\n locationId: selectedLoc,\n startDate: selectedDate,\n serviceId: selectedSvc || undefined,\n providerId: selectedProv || undefined,\n })\n .then(s => { setSlots(s); setLoading(false); })\n .catch(() => { setError(\"Error cargando horarios\"); setLoading(false); });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedLoc, selectedSvc, selectedProv, selectedDate]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (overrides?: SubmitBookingOverrides) => {\n if (!selectedSlot) return;\n const validationError = validateClientForm(form);\n if (validationError) { setError(validationError); return; }\n\n setLoading(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.id === selectedSvc);\n const token = getClientToken();\n const payload = buildBookingPayload({\n form, client, selectedDate, selectedSlot, selectedSvc, selectedLoc, overrides,\n svcPrice: svc?.price, defaultNotes: opts.defaultNotes,\n });\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar la reserva\");\n setLoading(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(result.message);\n if (result.conflict) setSelectedSlot(null);\n setLoading(false);\n return;\n }\n opts.onBookingComplete(result.booking);\n }\n\n return {\n locations, services, providers, slots,\n selectedLoc, setSelectedLoc,\n selectedSvc, setSelectedSvc,\n selectedProv, setSelectedProv,\n selectedDate, setSelectedDate,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n loading, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport { createBooking, getAvailableSlots, listLocations, listServices } from \"../api\";\nimport { fetchMe, getClientToken } from \"../clientAuth\";\nimport { dateStr, DEFAULT_PROVIDER_COLORS, getMondayOf, type ProviderColor } from \"../dateUtils\";\nimport type { ApiConfig, ClientFormValues, ClientProfile, Service, Slot } from \"../types\";\n\nexport interface SelectedClassSlot {\n slot: Slot;\n date: string;\n}\n\nexport interface UseClassScheduleOptions extends ApiConfig {\n onBookingComplete: () => void;\n colors?: ProviderColor[];\n}\n\nconst EMPTY_FORM: ClientFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", rut: \"\" };\n\nfunction isValidPhone(phone: string): boolean {\n return phone.replace(/[^0-9]/g, \"\").length >= 8;\n}\n\nfunction validateClassForm(form: ClientFormValues): string | null {\n if (!form.firstName.trim()) return \"Por favor ingresa tu nombre.\";\n if (!form.lastName.trim()) return \"Por favor ingresa tu apellido.\";\n if (!form.email.trim() || !/\\S+@\\S+\\.\\S+/.test(form.email)) return \"Por favor ingresa un email válido.\";\n if (!form.phone.trim() || !isValidPhone(form.phone)) return \"Por favor ingresa un teléfono válido.\";\n return null;\n}\n\nfunction buildClassBookingPayload(\n form: ClientFormValues,\n client: ClientProfile | null,\n selectedSlot: SelectedClassSlot,\n svc: Service | undefined,\n locationId: string\n) {\n return {\n start_time: `${selectedSlot.date}T${selectedSlot.slot.start_time}:00-04:00`,\n end_time: `${selectedSlot.date}T${selectedSlot.slot.end_time}:00-04:00`,\n service_id: svc?.id ?? \"\",\n service_provider_id: selectedSlot.slot.provider_id,\n location_id: locationId,\n price: svc?.price || \"0\",\n client_id: client?.id,\n client_first_name: form.firstName,\n client_last_name: form.lastName,\n client_email: form.email,\n client_phone: form.phone,\n client_rut: form.rut || undefined,\n };\n}\n\nfunction resolveConflictMessage(result: { conflict: boolean; message: string }): string {\n return result.conflict\n ? \"Ese cupo ya fue tomado por otra persona. Cierra esta ventana y elige otro horario.\"\n : result.message;\n}\n\n/**\n * Shared weekly group-class schedule logic: week-of-slots fetching, provider\n * color-palette assignment, and submit-with-409-handling. The week-grid table\n * JSX and modal markup stay in the consuming component — this hook only owns\n * data and state transitions.\n */\nexport function useClassSchedule(opts: UseClassScheduleOptions) {\n const colors = opts.colors ?? DEFAULT_PROVIDER_COLORS;\n\n const [monday, setMonday] = useState<Date>(() => getMondayOf(new Date()));\n const [slotsByDay, setSlotsByDay] = useState<Record<string, Slot[]>>({});\n const [services, setServices] = useState<Service[]>([]);\n const [locationId, setLocationId] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [selectedSlot, setSelectedSlot] = useState<SelectedClassSlot | null>(null);\n const [providerColorMap, setProviderColorMap] = useState<Record<string, ProviderColor>>({});\n\n const [form, setForm] = useState<ClientFormValues>(EMPTY_FORM);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState(\"\");\n const [client, setClient] = useState<ClientProfile | null>(null);\n\n useEffect(() => {\n Promise.all([listLocations(opts), listServices(opts, 50)])\n .then(([locs, svcs]) => {\n if (locs.length) setLocationId(locs[0].id);\n setServices(svcs);\n })\n .catch(() => setError(\"Error cargando datos\"));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const token = getClientToken();\n if (!token) return;\n fetchMe(opts.apiBase, token).then(profile => {\n if (!profile) return;\n setClient(profile);\n setForm(f => ({\n ...f,\n firstName: profile.first_name || f.firstName,\n lastName: profile.last_name || f.lastName,\n email: profile.email || f.email,\n phone: profile.phone || f.phone,\n rut: profile.rut || f.rut,\n }));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const loadWeek = useCallback(async (mon: Date, locId: string) => {\n if (!locId) return;\n setLoading(true);\n const days = Array.from({ length: 7 }, (_, i) => {\n const d = new Date(mon);\n d.setDate(mon.getDate() + i);\n return d;\n });\n\n const results = await Promise.all(\n days.map(async d => {\n const ds = dateStr(d);\n try {\n const slots = await getAvailableSlots(opts, { locationId: locId, startDate: ds });\n return { date: ds, slots };\n } catch {\n return { date: ds, slots: [] as Slot[] };\n }\n })\n );\n\n const map: Record<string, Slot[]> = {};\n const colorMap: Record<string, ProviderColor> = {};\n let colorIdx = 0;\n results.forEach(({ date, slots }) => {\n map[date] = slots;\n slots.forEach(s => {\n if (!colorMap[s.provider_id]) {\n colorMap[s.provider_id] = colors[colorIdx % colors.length];\n colorIdx++;\n }\n });\n });\n\n setSlotsByDay(map);\n setProviderColorMap(prev => ({ ...prev, ...colorMap }));\n setLoading(false);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (locationId) loadWeek(monday, locationId);\n }, [monday, locationId, loadWeek]);\n\n const updateForm = (fields: Partial<ClientFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const submitBooking = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!selectedSlot) return;\n const validationError = validateClassForm(form);\n if (validationError) { setError(validationError); return; }\n setSubmitting(true);\n setError(\"\");\n try {\n const svc = services.find(s => s.name === selectedSlot.slot.service_name);\n const token = getClientToken();\n const payload = buildClassBookingPayload(form, client, selectedSlot, svc, locationId);\n const result = await createBooking(opts, payload, token || undefined);\n handleSubmitResult(result);\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al procesar\");\n setSubmitting(false);\n }\n };\n\n function handleSubmitResult(result: Awaited<ReturnType<typeof createBooking>>) {\n if (!result.ok) {\n setError(resolveConflictMessage(result));\n setSubmitting(false);\n return;\n }\n opts.onBookingComplete();\n }\n\n return {\n monday, setMonday,\n slotsByDay, loading, providerColorMap,\n selectedSlot, setSelectedSlot,\n form, updateForm,\n submitting, error, setError,\n client,\n submitBooking,\n };\n}\n","import { useState } from \"react\";\nimport { loginWithGoogle, sendMagicLink } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\ndeclare global {\n interface Window {\n google?: {\n accounts: {\n id: {\n initialize: (config: { client_id: string; callback: (response: { credential: string }) => void }) => void;\n prompt: () => void;\n };\n };\n };\n }\n}\n\nexport interface UseMagicLinkAuthOptions extends ApiConfig {\n googleClientId?: string;\n frontendUrl: string;\n onGoogleLoggedIn: () => void;\n}\n\nexport type MagicLinkStep = \"email\" | \"sent\";\n\nexport function useMagicLinkAuth(opts: UseMagicLinkAuthOptions) {\n const [email, setEmail] = useState(\"\");\n // Optional — only relevant on a signup-style form (e.g. registro pages)\n // that collects a bit more than login does. Login pages can ignore these.\n const [firstName, setFirstName] = useState(\"\");\n const [lastName, setLastName] = useState(\"\");\n const [phone, setPhone] = useState(\"\");\n const [rut, setRut] = useState(\"\");\n const [step, setStep] = useState<MagicLinkStep>(\"email\");\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const handleMagicLink = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!email.trim() || !/\\S+@\\S+\\.\\S+/.test(email)) {\n setError(\"Ingresa un email válido.\");\n return;\n }\n setLoading(true);\n setError(\"\");\n try {\n await sendMagicLink(opts, email, opts.frontendUrl, {\n firstName: firstName || undefined,\n lastName: lastName || undefined,\n phone: phone || undefined,\n rut: rut || undefined,\n });\n setStep(\"sent\");\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error enviando el link.\");\n } finally {\n setLoading(false);\n }\n };\n\n const handleGoogle = () => {\n if (!opts.googleClientId || typeof window === \"undefined\" || !window.google) return;\n window.google.accounts.id.initialize({\n client_id: opts.googleClientId,\n callback: async (response: { credential: string }) => {\n setLoading(true);\n try {\n const data = await loginWithGoogle(opts, response.credential);\n setClientToken(data.access_token);\n opts.onGoogleLoggedIn();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error con Google.\");\n setLoading(false);\n }\n },\n });\n window.google.accounts.id.prompt();\n };\n\n const reset = () => {\n setStep(\"email\");\n setError(\"\");\n };\n\n return {\n email, setEmail,\n firstName, setFirstName,\n lastName, setLastName,\n phone, setPhone,\n rut, setRut,\n step, error, loading, handleMagicLink, handleGoogle, reset,\n };\n}\n","import { useState } from \"react\";\nimport { register } from \"../api\";\nimport { setClientToken } from \"../clientAuth\";\nimport type { ApiConfig } from \"../types\";\n\nexport interface RegisterFormValues {\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n password: string;\n confirm: string;\n}\n\nconst EMPTY: RegisterFormValues = { firstName: \"\", lastName: \"\", email: \"\", phone: \"\", password: \"\", confirm: \"\" };\n\nexport interface UseRegisterFormOptions extends ApiConfig {\n onRegistered: () => void;\n}\n\nexport function useRegisterForm(opts: UseRegisterFormOptions) {\n const [form, setForm] = useState<RegisterFormValues>(EMPTY);\n const [error, setError] = useState(\"\");\n const [loading, setLoading] = useState(false);\n\n const updateField = (fields: Partial<RegisterFormValues>) => setForm(f => ({ ...f, ...fields }));\n\n const handleSubmit = async (e: { preventDefault: () => void }) => {\n e.preventDefault();\n if (!form.firstName.trim()) { setError(\"Ingresa tu nombre.\"); return; }\n if (!form.lastName.trim()) { setError(\"Ingresa tu apellido.\"); return; }\n if (!form.email.trim()) { setError(\"Ingresa tu email.\"); return; }\n if (form.password.length < 6) { setError(\"La contraseña debe tener al menos 6 caracteres.\"); return; }\n if (form.password !== form.confirm) { setError(\"Las contraseñas no coinciden.\"); return; }\n setLoading(true);\n setError(\"\");\n try {\n const data = await register(opts, form);\n setClientToken(data.access_token);\n opts.onRegistered();\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : \"Error al registrarse.\");\n } finally {\n setLoading(false);\n }\n };\n\n return { form, updateField, error, loading, handleSubmit };\n}\n","import { useEffect, useState } from \"react\";\nimport { cancelMyBooking, getMe, getMyBookings, getMyPackages, rescheduleMyBooking } from \"../api\";\nimport { clearClientToken, getClientToken } from \"../clientAuth\";\nimport type { BookingRecord, ClientProfile, PackageRecord } from \"../types\";\n\nexport interface UseClientAccountOptions {\n apiBase: string;\n onUnauthenticated: () => void;\n onLoggedOut: () => void;\n}\n\nexport function useClientAccount(opts: UseClientAccountOptions) {\n const [profile, setProfile] = useState<ClientProfile | null>(null);\n const [upcoming, setUpcoming] = useState<BookingRecord[]>([]);\n const [history, setHistory] = useState<BookingRecord[]>([]);\n const [packages, setPackages] = useState<PackageRecord[]>([]);\n const [loading, setLoading] = useState(true);\n const [actionError, setActionError] = useState(\"\");\n const [actionLoading, setActionLoading] = useState(false);\n\n const loadAll = () => {\n const token = getClientToken();\n if (!token) {\n opts.onUnauthenticated();\n return;\n }\n Promise.all([\n getMe(opts.apiBase, token),\n getMyBookings(opts.apiBase, token, { upcoming: true, perPage: 10 }),\n getMyBookings(opts.apiBase, token, { perPage: 20 }),\n getMyPackages(opts.apiBase, token),\n ])\n .then(([prof, up, hist, pkgs]) => {\n setProfile(prof);\n setUpcoming(up);\n setHistory(hist);\n setPackages(pkgs);\n setLoading(false);\n })\n .catch(() => opts.onUnauthenticated());\n };\n\n useEffect(() => {\n loadAll();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const handleLogout = () => {\n clearClientToken();\n opts.onLoggedOut();\n };\n\n const cancelBooking = async (bookingId: string) => {\n const token = getClientToken();\n if (!token) return;\n setActionLoading(true);\n setActionError(\"\");\n const result = await cancelMyBooking(opts.apiBase, token, bookingId);\n if (!result.ok) {\n setActionError(result.message);\n setActionLoading(false);\n return;\n }\n loadAll();\n setActionLoading(false);\n };\n\n const rescheduleBooking = async (bookingId: string, startTime: string, endTime: string) => {\n const token = getClientToken();\n if (!token) return false;\n setActionLoading(true);\n setActionError(\"\");\n const result = await rescheduleMyBooking(opts.apiBase, token, bookingId, startTime, endTime);\n setActionLoading(false);\n if (!result.ok) {\n setActionError(result.message);\n return false;\n }\n loadAll();\n return true;\n };\n\n const totalCredits = packages.reduce((s, p) => s + p.remaining_credits, 0);\n\n return {\n profile, upcoming, history, packages, loading, totalCredits, handleLogout,\n cancelBooking, rescheduleBooking, actionError, setActionError, actionLoading,\n };\n}\n"],"mappings":"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","names":["useEffect","useState","EMPTY_FORM","isValidPhone","useState","useEffect","useState","useState","useState","useState","useEffect","useState","useState","useEffect"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bookr-cl/widget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Shared booking-flow logic (API client, auth, hooks) for Bookr client widgets.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "./dist/index.cjs",
|
|
7
8
|
"module": "./dist/index.js",
|
|
@@ -18,7 +19,9 @@
|
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|
|
20
21
|
"build": "tsup",
|
|
21
|
-
"dev": "tsup --watch"
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"typecheck": "tsc --noEmit"
|
|
22
25
|
},
|
|
23
26
|
"peerDependencies": {
|
|
24
27
|
"react": ">=19",
|