@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 +132 -2
- package/dist/mrcharles.esm.js +1175 -0
- package/dist/mrcharles.esm.js.map +21 -0
- package/dist/mrcharles.min.js +4 -0
- package/dist/mrcharles.min.js.map +22 -0
- package/dist/types/client.d.ts +79 -0
- package/dist/types/embed.d.ts +28 -0
- package/dist/types/env.d.ts +36 -0
- package/dist/types/errors.d.ts +15 -0
- package/dist/types/format.d.ts +24 -0
- package/dist/types/http.d.ts +7 -0
- package/dist/types/i18n.d.ts +50 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/links.d.ts +34 -0
- package/dist/types/rooms.d.ts +76 -0
- package/dist/types/slots.d.ts +19 -0
- package/dist/types/types.d.ts +340 -0
- package/dist/types/uuid.d.ts +2 -0
- package/package.json +55 -4
package/README.md
CHANGED
|
@@ -1,3 +1,133 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mrcharles/sdk
|
|
2
2
|
|
|
3
|
-
|
|
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.
|