@mrcharles/sdk 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,133 @@
1
- # Temporary Holding Version
1
+ # @mrcharles/sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Put Mr Charles booking on any website: studio rooms (guests, no account) and
4
+ teacher lessons (hand-off to the app). Zero dependencies, ~6 kB min+gz, works
5
+ in the browser, in Astro/Next/SvelteKit server code, and as a plain `<script>`.
6
+
7
+ Docs: https://developers.mrcharles.app/integrate/sdk/
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm i @mrcharles/sdk
13
+ ```
14
+
15
+ or without a bundler:
16
+
17
+ ```html
18
+ <script src="https://developers.mrcharles.app/sdk/v0/mrcharles.min.js"></script>
19
+ <!-- exposes window.MrCharles -->
20
+ ```
21
+
22
+ ## 30-second version: embed our booking screen
23
+
24
+ ```js
25
+ import { createClient } from '@mrcharles/sdk';
26
+
27
+ const mc = createClient();
28
+ mc.mountRoomBooking('#book', {
29
+ slug: 'studio-uno-madrid',
30
+ roomId: 'd9a36c09-d151-5d4a-a077-296293b07be4',
31
+ lang: 'es',
32
+ onBooked: ({ reference, manageUrl }) => console.log('booked', reference, manageUrl),
33
+ });
34
+ ```
35
+
36
+ The iframe resizes to its content, takes card deposits, sends the SMS code,
37
+ and shows the manage link. You write nothing else.
38
+
39
+ ## Your own UI: the headless flow
40
+
41
+ ```js
42
+ const flow = mc.roomBooking({ slug, roomId, onChange: render });
43
+
44
+ await flow.loadSite(); // rooms, prices, timezone, captcha/stripe keys
45
+ await flow.loadAvailability({ days: 14 }); // free spans, studio's zone
46
+ flow.startTimesFor('2026-10-20', 120); // → ['09:00', '10:00', …]
47
+ flow.durationsFor('2026-10-20', '10:00'); // → [60, 120, 180]
48
+
49
+ await flow.hold({ date, startTime, durationMinutes, guestName, channel: 'phone', contact: '+34600111222' });
50
+ // state.step === 'verify' · flow.remainingMs() counts down from 12 min
51
+ await flow.verify(code); // 6 digits; flow.resend() after 60 s
52
+ await flow.confirm({ paymentMode: 'on_site', notes }); // state.step === 'done'
53
+ flow.state.confirmation.manageUrl; // hand this to the guest
54
+ ```
55
+
56
+ Every error is a `MrCharlesError` with a stable `code` (`SLOT_TAKEN`,
57
+ `HOLD_EXPIRED`, `CODE_INVALID`, …) and three helpers: `slotIsGone`,
58
+ `holdIsDead`, `isTransient`. The flow rotates the idempotency key when a hold
59
+ dies and keeps it across network retries, so you can simply call `hold()` again.
60
+
61
+ ## Lessons with a teacher
62
+
63
+ ```js
64
+ const t = await mc.teachers.profile(teacherUserId);
65
+ const offers = await mc.teachers.offers(teacherUserId);
66
+ const slots = await mc.teachers.slots({ instructorUserId: t.user.id, offerId: offers[0].id, date: '2026-10-13', days: 7 });
67
+ // <a href={mc.links.bookLesson({ offerId: offers[0].id, instructorId: t.user.id })}>Book</a>
68
+ ```
69
+
70
+ Students book inside the Mr Charles app (payment, reminders, cancellations),
71
+ so the SDK gives you the reads and the link, not a guest checkout.
72
+
73
+ ## Astro
74
+
75
+ ```astro
76
+ ---
77
+ import { createClient } from '@mrcharles/sdk';
78
+ const mc = createClient();
79
+ const site = await mc.sites.get('studio-uno-madrid'); // at build / on request
80
+ ---
81
+ <h1>{site.studio.name}</h1>
82
+ {site.rooms.filter((r) => r.bookable).map((r) => <a href={mc.links.bookRoom({ slug: site.studio.slug, roomId: r.id })}>{r.name}</a>)}
83
+ <div id="book"></div>
84
+ <script>
85
+ import { createClient } from '@mrcharles/sdk';
86
+ createClient().mountRoomBooking('#book', { slug: 'studio-uno-madrid', roomId: '…' });
87
+ </script>
88
+ ```
89
+
90
+ Availability and holds must run in the browser (or forward the guest's IP in
91
+ `headers: { 'X-Forwarded-For': ip }` when you proxy them).
92
+
93
+ ## Languages
94
+
95
+ One `locale` on the client drives everything: the hosted screen's language, the guest's SMS/email, `Accept-Language`, the SDK's own strings and its formatting.
96
+
97
+ ```js
98
+ const mc = createClient({ locale: 'ca' }); // any BCP-47 tag: 'ca-ES', 'pt-BR'…
99
+ mc.mountRoomBooking('#book', { slug, roomId }); // screen in Catalan, code in Catalan
100
+ mc.t('action.confirm'); // "Confirmar la reserva"
101
+ mc.format.price(2500, 'EUR'); // "25,00 €"
102
+ mc.format.date('2026-10-20'); // "dimarts, 20 d’octubre"
103
+ mc.format.duration(90); // "1 h 30 min"
104
+ mc.pick(site.site.content.tagline, site.site.defaultLocale);
105
+ mc.setLocale('en'); // a language switcher: links, embeds, new flows follow
106
+ ```
107
+
108
+ - **Hosted screen and messages** speak **es, ca, en** (what the studio's site offers).
109
+ - **The SDK** speaks **es, ca, en, fr, pt, it, de**: labels and error sentences for a custom UI (`flow.t()`, `flow.state.errorMessage`, `mc.errorMessage(err)`, `messageFor()`), formatting for any tag. A French site gets French labels; the guest's code arrives in English, the closest hosted language.
110
+ - No `locale` → each site's default language, so a Spanish studio stays Spanish without configuration.
111
+
112
+ ## API surface
113
+
114
+ | | |
115
+ |---|---|
116
+ | `createClient({ fetch, locale, headers })` | one client; `setLocale`, `t`, `format`, `pick`, `errorMessage` |
117
+ | `sites.get / resolve / availability` | studio payload, custom-domain lookup, free spans |
118
+ | `holds.create / verify / resend / confirm` | the raw calls, if you manage state yourself |
119
+ | `bookings.get / cancel` | a guest's booking by manage token |
120
+ | `teachers.profile / offers / offer / windows / policy / slots` | lesson reads |
121
+ | `links.site / bookRoom / manage / bookLesson / teacherProfile` | URLs for every hand-off |
122
+ | `mountRoomBooking(target, opts)` | iframe embed with resize + booked events |
123
+ | `roomBooking(opts)` | the stateful flow above |
124
+ | `startTimes / durationOptions / formatPrice / dateKey / addDays` | helpers |
125
+
126
+ ## Testing without bothering anyone
127
+
128
+ There is one API. To develop, point the SDK at a studio site in **sandbox
129
+ mode** — the platform's `demo-studio`, or your own site with *Modo pruebas*
130
+ switched on in the dashboard. A sandbox site returns the verification code
131
+ as `hold.devCode`, sends no SMS or email, offers `on_site` only, and purges
132
+ its bookings after seven days. `site.site.sandbox` tells you which mode you
133
+ are in; switch the slug to the real one at launch and nothing else changes.