@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 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 tu tel\xE9fono.";
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 tu tel\xE9fono.";
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.useEffect)(() => {
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 { profile, upcoming, history, packages, loading, totalCredits, handleLogout };
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,
@@ -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":"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","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 tu tel\xE9fono.";
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 tu tel\xE9fono.";
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
- useEffect3(() => {
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 { profile, upcoming, history, packages, loading, totalCredits, handleLogout };
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.0.1",
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",