@hanzo/booker 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +78 -0
  2. package/dist/cjs/Booker.cjs +153 -0
  3. package/dist/cjs/Booker.native.js +158 -0
  4. package/dist/cjs/Booker.native.js.map +1 -0
  5. package/dist/cjs/BookingForm.cjs +197 -0
  6. package/dist/cjs/BookingForm.native.js +203 -0
  7. package/dist/cjs/BookingForm.native.js.map +1 -0
  8. package/dist/cjs/CalendarEmbed.cjs +121 -0
  9. package/dist/cjs/CalendarEmbed.native.js +125 -0
  10. package/dist/cjs/CalendarEmbed.native.js.map +1 -0
  11. package/dist/cjs/Confirmation.cjs +177 -0
  12. package/dist/cjs/Confirmation.native.js +185 -0
  13. package/dist/cjs/Confirmation.native.js.map +1 -0
  14. package/dist/cjs/EventHeader.cjs +112 -0
  15. package/dist/cjs/EventHeader.native.js +118 -0
  16. package/dist/cjs/EventHeader.native.js.map +1 -0
  17. package/dist/cjs/MonthCalendar.cjs +177 -0
  18. package/dist/cjs/MonthCalendar.native.js +191 -0
  19. package/dist/cjs/MonthCalendar.native.js.map +1 -0
  20. package/dist/cjs/TimeSlots.cjs +119 -0
  21. package/dist/cjs/TimeSlots.native.js +126 -0
  22. package/dist/cjs/TimeSlots.native.js.map +1 -0
  23. package/dist/cjs/client.cjs +136 -0
  24. package/dist/cjs/client.native.js +267 -0
  25. package/dist/cjs/client.native.js.map +1 -0
  26. package/dist/cjs/hooks.cjs +193 -0
  27. package/dist/cjs/hooks.native.js +221 -0
  28. package/dist/cjs/hooks.native.js.map +1 -0
  29. package/dist/cjs/index.cjs +71 -0
  30. package/dist/cjs/index.native.js +74 -0
  31. package/dist/cjs/index.native.js.map +1 -0
  32. package/dist/cjs/time.cjs +145 -0
  33. package/dist/cjs/time.native.js +150 -0
  34. package/dist/cjs/time.native.js.map +1 -0
  35. package/dist/cjs/useBooker.cjs +151 -0
  36. package/dist/cjs/useBooker.native.js +173 -0
  37. package/dist/cjs/useBooker.native.js.map +1 -0
  38. package/dist/esm/Booker.mjs +128 -0
  39. package/dist/esm/Booker.mjs.map +1 -0
  40. package/dist/esm/Booker.native.js +130 -0
  41. package/dist/esm/Booker.native.js.map +1 -0
  42. package/dist/esm/BookingForm.mjs +172 -0
  43. package/dist/esm/BookingForm.mjs.map +1 -0
  44. package/dist/esm/BookingForm.native.js +175 -0
  45. package/dist/esm/BookingForm.native.js.map +1 -0
  46. package/dist/esm/CalendarEmbed.mjs +96 -0
  47. package/dist/esm/CalendarEmbed.mjs.map +1 -0
  48. package/dist/esm/CalendarEmbed.native.js +97 -0
  49. package/dist/esm/CalendarEmbed.native.js.map +1 -0
  50. package/dist/esm/Confirmation.mjs +152 -0
  51. package/dist/esm/Confirmation.mjs.map +1 -0
  52. package/dist/esm/Confirmation.native.js +157 -0
  53. package/dist/esm/Confirmation.native.js.map +1 -0
  54. package/dist/esm/EventHeader.mjs +87 -0
  55. package/dist/esm/EventHeader.mjs.map +1 -0
  56. package/dist/esm/EventHeader.native.js +90 -0
  57. package/dist/esm/EventHeader.native.js.map +1 -0
  58. package/dist/esm/MonthCalendar.mjs +152 -0
  59. package/dist/esm/MonthCalendar.mjs.map +1 -0
  60. package/dist/esm/MonthCalendar.native.js +163 -0
  61. package/dist/esm/MonthCalendar.native.js.map +1 -0
  62. package/dist/esm/TimeSlots.mjs +94 -0
  63. package/dist/esm/TimeSlots.mjs.map +1 -0
  64. package/dist/esm/TimeSlots.native.js +98 -0
  65. package/dist/esm/TimeSlots.native.js.map +1 -0
  66. package/dist/esm/client.mjs +104 -0
  67. package/dist/esm/client.mjs.map +1 -0
  68. package/dist/esm/client.native.js +232 -0
  69. package/dist/esm/client.native.js.map +1 -0
  70. package/dist/esm/hooks.mjs +165 -0
  71. package/dist/esm/hooks.mjs.map +1 -0
  72. package/dist/esm/hooks.native.js +190 -0
  73. package/dist/esm/hooks.native.js.map +1 -0
  74. package/dist/esm/index.js +13 -0
  75. package/dist/esm/index.js.map +1 -0
  76. package/dist/esm/index.mjs +13 -0
  77. package/dist/esm/index.mjs.map +1 -0
  78. package/dist/esm/index.native.js +13 -0
  79. package/dist/esm/index.native.js.map +1 -0
  80. package/dist/esm/time.mjs +107 -0
  81. package/dist/esm/time.mjs.map +1 -0
  82. package/dist/esm/time.native.js +109 -0
  83. package/dist/esm/time.native.js.map +1 -0
  84. package/dist/esm/useBooker.mjs +126 -0
  85. package/dist/esm/useBooker.mjs.map +1 -0
  86. package/dist/esm/useBooker.native.js +145 -0
  87. package/dist/esm/useBooker.native.js.map +1 -0
  88. package/package.json +61 -0
  89. package/src/Booker.tsx +152 -0
  90. package/src/BookingForm.tsx +180 -0
  91. package/src/CalendarEmbed.tsx +111 -0
  92. package/src/Confirmation.tsx +145 -0
  93. package/src/EventHeader.tsx +85 -0
  94. package/src/MonthCalendar.tsx +179 -0
  95. package/src/TimeSlots.tsx +102 -0
  96. package/src/client.ts +218 -0
  97. package/src/hooks.ts +180 -0
  98. package/src/index.ts +72 -0
  99. package/src/time.ts +131 -0
  100. package/src/useBooker.ts +187 -0
  101. package/types/Booker.d.ts +16 -0
  102. package/types/Booker.d.ts.map +1 -0
  103. package/types/BookingForm.d.ts +13 -0
  104. package/types/BookingForm.d.ts.map +1 -0
  105. package/types/CalendarEmbed.d.ts +15 -0
  106. package/types/CalendarEmbed.d.ts.map +1 -0
  107. package/types/Confirmation.d.ts +12 -0
  108. package/types/Confirmation.d.ts.map +1 -0
  109. package/types/EventHeader.d.ts +10 -0
  110. package/types/EventHeader.d.ts.map +1 -0
  111. package/types/MonthCalendar.d.ts +14 -0
  112. package/types/MonthCalendar.d.ts.map +1 -0
  113. package/types/TimeSlots.d.ts +15 -0
  114. package/types/TimeSlots.d.ts.map +1 -0
  115. package/types/client.d.ts +77 -0
  116. package/types/client.d.ts.map +1 -0
  117. package/types/hooks.d.ts +43 -0
  118. package/types/hooks.d.ts.map +1 -0
  119. package/types/index.d.ts +12 -0
  120. package/types/index.d.ts.map +1 -0
  121. package/types/time.d.ts +38 -0
  122. package/types/time.d.ts.map +1 -0
  123. package/types/useBooker.d.ts +41 -0
  124. package/types/useBooker.d.ts.map +1 -0
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @hanzo/calendar
2
+
3
+ Universal (web + native) scheduling UI for the [Hanzo Calendar](https://api.hanzo.ai/v1/calendar), built with [hanzogui](https://github.com/hanzoai/gui) primitives.
4
+
5
+ One booking flow — event header → month grid → time slots → details → confirmation — that runs unchanged on the web (DOM) and React Native (iOS / Android). No raw HTML, no ad-hoc CSS: every surface is a hanzogui/Tamagui component styled with theme tokens, so it inherits the host app's theme, dark mode, and design system.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ bun add @hanzo/calendar
11
+ # peer deps: hanzogui, react (and react-native for native targets)
12
+ ```
13
+
14
+ Render inside a `GuiProvider` (from `hanzogui`) so tokens and themes resolve.
15
+
16
+ ## Usage
17
+
18
+ ```tsx
19
+ import { GuiProvider } from 'hanzogui'
20
+ import { Booker } from '@hanzo/calendar'
21
+ import config from './gui.config' // your createGui(...) config
22
+
23
+ export function BookMeeting() {
24
+ return (
25
+ <GuiProvider config={config} defaultTheme="light">
26
+ <Booker
27
+ host="hanzo"
28
+ eventSlug="intro"
29
+ onBooked={(booking) => console.log('booked', booking.uid)}
30
+ />
31
+ </GuiProvider>
32
+ )
33
+ }
34
+ ```
35
+
36
+ That's the whole integration. `Booker` fetches the public event type, renders a navigable month calendar, loads availability for the visible range, converts UTC slots into the visitor's time zone (`Intl.DateTimeFormat().resolvedOptions().timeZone`), collects attendee details, and posts the booking — with loading, error, empty, and "slot just taken" (HTTP 409) states handled throughout.
37
+
38
+ ## Components
39
+
40
+ | Component | Purpose |
41
+ | ---------------- | ----------------------------------------------------------------------- |
42
+ | `Booker` | Full scheduling flow: event → month → times → details → confirmation. |
43
+ | `CalendarEmbed` | Compact card for a marketing hero — soonest availability inline. |
44
+ | `Confirmation` | Booking confirmation card; pass a `booking` or a `uid` to fetch one. |
45
+ | `EventHeader`, `MonthCalendar`, `TimeSlots`, `BookingForm` | Composable sub-views for a custom flow. |
46
+
47
+ ### Props
48
+
49
+ `Booker` and `CalendarEmbed`:
50
+
51
+ - `host` — calendar owner's username, e.g. `"hanzo"`.
52
+ - `eventSlug` — event-type slug, e.g. `"intro"`.
53
+ - `apiUrl?` — API base; defaults to `https://api.hanzo.ai/v1/calendar`.
54
+ - `timeZone?` — visitor IANA zone; defaults to the browser/device zone.
55
+ - `weekStartsOn?` — `0` (Sunday, default) or `1` (Monday).
56
+ - `onBooked?(booking)` — called with the created booking.
57
+
58
+ `Confirmation`:
59
+
60
+ - `booking?` — a booking object, or
61
+ - `uid?` — a booking id to fetch (with `apiUrl?`, `timeZone?`, `onDone?`).
62
+
63
+ ## Data & time helpers
64
+
65
+ The pure, framework-free data plane (`fetchEventType`, `fetchSlots`, `createBooking`, `fetchBooking`, `cancelBooking`) and time plane (`visitorTimeZone`, `formatSlotTime`, `monthMatrix`, …) are exported for building a bespoke flow or a headless integration. The `useBooker` hook exposes the entire state machine.
66
+
67
+ ## Backend
68
+
69
+ Speaks the Hanzo Calendar API (Cal API-v2 shapes):
70
+
71
+ - `GET /atoms/event-types/{slug}/public?username={host}`
72
+ - `GET /slots/available?eventTypeSlug=&usernameList=&startTime=&endTime=&timeZone=`
73
+ - `POST /bookings` (`cal-api-version: 2024-08-13`)
74
+ - `GET /bookings/{uid}` · `POST /bookings/{uid}/cancel`
75
+
76
+ ## License
77
+
78
+ BSD-3-Clause © Hanzo AI
@@ -0,0 +1,153 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
14
+ get: () => from[key],
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
21
+ value: true
22
+ }), mod);
23
+ var Booker_exports = {};
24
+ __export(Booker_exports, {
25
+ Booker: () => Booker
26
+ });
27
+ module.exports = __toCommonJS(Booker_exports);
28
+ var import_hanzogui = require("hanzogui");
29
+ var import_BookingForm = require("./BookingForm.cjs");
30
+ var import_Confirmation = require("./Confirmation.cjs");
31
+ var import_EventHeader = require("./EventHeader.cjs");
32
+ var import_MonthCalendar = require("./MonthCalendar.cjs");
33
+ var import_TimeSlots = require("./TimeSlots.cjs");
34
+ var import_useBooker = require("./useBooker.cjs");
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ function Booker(props) {
37
+ const m = (0, import_useBooker.useBooker)(props);
38
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
39
+ backgroundColor: "$background",
40
+ borderColor: "$borderColor",
41
+ borderWidth: 1,
42
+ borderRadius: "$6",
43
+ padding: "$4",
44
+ gap: "$4",
45
+ width: "100%",
46
+ maxWidth: 860,
47
+ alignSelf: "center",
48
+ children: m.event.loading ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(StatePanel, {
49
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.Spinner, {
50
+ size: "large",
51
+ color: "$color9"
52
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
53
+ size: "$3",
54
+ color: "$color10",
55
+ children: "Loading\u2026"
56
+ })]
57
+ }) : m.event.error || !m.event.data ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(StatePanel, {
58
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
59
+ size: "$5",
60
+ fontWeight: "600",
61
+ color: "$color12",
62
+ children: "Couldn't load this event"
63
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
64
+ size: "$3",
65
+ color: "$color10",
66
+ textAlign: "center",
67
+ children: m.event.error ?? "The event type was not found."
68
+ })]
69
+ }) : m.phase === "done" && m.booking ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_Confirmation.Confirmation, {
70
+ booking: m.booking,
71
+ timeZone: m.timeZone,
72
+ onDone: m.reset
73
+ }) : /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.YStack, {
74
+ gap: "$4",
75
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_EventHeader.EventHeader, {
76
+ event: m.event.data,
77
+ host: props.host,
78
+ timeZone: m.timeZone,
79
+ compact: m.phase === "form"
80
+ }), m.phase === "form" && m.selectedSlot ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_BookingForm.BookingForm, {
81
+ slotIso: m.selectedSlot,
82
+ timeZone: m.timeZone,
83
+ eventLength: m.event.data.length,
84
+ submitting: m.submitting,
85
+ error: m.error,
86
+ onSubmit: m.submit,
87
+ onBack: m.back
88
+ }) : /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, {
89
+ children: [m.conflict ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(JustTakenBanner, {}) : null, m.slots.error ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.SizableText, {
90
+ size: "$2",
91
+ color: "$red10",
92
+ children: ["Couldn't load availability: ", m.slots.error]
93
+ }) : null, /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.XStack, {
94
+ gap: "$5",
95
+ flexDirection: "column",
96
+ $sm: {
97
+ flexDirection: "row"
98
+ },
99
+ alignItems: "flex-start",
100
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_MonthCalendar.MonthCalendar, {
101
+ viewDate: m.viewDate,
102
+ availableKeys: m.availableKeys,
103
+ selectedDay: m.selectedDay,
104
+ onSelectDay: m.selectDay,
105
+ onPrev: m.goPrevMonth,
106
+ onNext: m.goNextMonth,
107
+ atFirstMonth: m.atFirstMonth,
108
+ weekStartsOn: m.weekStartsOn,
109
+ loading: m.slots.loading
110
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_TimeSlots.TimeSlots, {
111
+ dayKey: m.selectedDay,
112
+ slots: m.daySlots,
113
+ timeZone: m.timeZone,
114
+ onPick: m.selectSlot,
115
+ selectedIso: m.selectedSlot,
116
+ loading: m.slots.loading && !m.selectedDay
117
+ })]
118
+ }), m.availableKeys.size === 0 && !m.slots.loading && !m.slots.error ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
119
+ size: "$3",
120
+ color: "$color10",
121
+ textAlign: "center",
122
+ children: "No availability this month \u2014 try the next month."
123
+ }) : null]
124
+ })]
125
+ })
126
+ });
127
+ }
128
+ function StatePanel({
129
+ children
130
+ }) {
131
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
132
+ alignItems: "center",
133
+ justifyContent: "center",
134
+ gap: "$3",
135
+ padding: "$8",
136
+ minHeight: 220,
137
+ children
138
+ });
139
+ }
140
+ function JustTakenBanner() {
141
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
142
+ backgroundColor: "$yellow3",
143
+ borderColor: "$yellow6",
144
+ borderWidth: 1,
145
+ borderRadius: "$4",
146
+ padding: "$2.5",
147
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
148
+ size: "$2",
149
+ color: "$yellow11",
150
+ children: "That time was just taken. We refreshed the calendar \u2014 please pick another."
151
+ })
152
+ });
153
+ }
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: () => from[key],
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
+ };
22
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
23
+ value: true
24
+ }), mod);
25
+ var Booker_exports = {};
26
+ __export(Booker_exports, {
27
+ Booker: () => Booker
28
+ });
29
+ module.exports = __toCommonJS(Booker_exports);
30
+ var import_jsx_runtime = require("react/jsx-runtime");
31
+ var import_hanzogui = require("hanzogui");
32
+ var import_BookingForm = require("./BookingForm.native.js");
33
+ var import_Confirmation = require("./Confirmation.native.js");
34
+ var import_EventHeader = require("./EventHeader.native.js");
35
+ var import_MonthCalendar = require("./MonthCalendar.native.js");
36
+ var import_TimeSlots = require("./TimeSlots.native.js");
37
+ var import_useBooker = require("./useBooker.native.js");
38
+ function Booker(props) {
39
+ var m = (0, import_useBooker.useBooker)(props);
40
+ var _m_event_error;
41
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
42
+ backgroundColor: "$background",
43
+ borderColor: "$borderColor",
44
+ borderWidth: 1,
45
+ borderRadius: "$6",
46
+ padding: "$4",
47
+ gap: "$4",
48
+ width: "100%",
49
+ maxWidth: 860,
50
+ alignSelf: "center",
51
+ children: m.event.loading ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(StatePanel, {
52
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.Spinner, {
53
+ size: "large",
54
+ color: "$color9"
55
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
56
+ size: "$3",
57
+ color: "$color10",
58
+ children: "Loading\u2026"
59
+ })]
60
+ }) : m.event.error || !m.event.data ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(StatePanel, {
61
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
62
+ size: "$5",
63
+ fontWeight: "600",
64
+ color: "$color12",
65
+ children: "Couldn't load this event"
66
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
67
+ size: "$3",
68
+ color: "$color10",
69
+ textAlign: "center",
70
+ children: (_m_event_error = m.event.error) !== null && _m_event_error !== void 0 ? _m_event_error : "The event type was not found."
71
+ })]
72
+ }) : m.phase === "done" && m.booking ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_Confirmation.Confirmation, {
73
+ booking: m.booking,
74
+ timeZone: m.timeZone,
75
+ onDone: m.reset
76
+ }) : /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.YStack, {
77
+ gap: "$4",
78
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_EventHeader.EventHeader, {
79
+ event: m.event.data,
80
+ host: props.host,
81
+ timeZone: m.timeZone,
82
+ compact: m.phase === "form"
83
+ }), m.phase === "form" && m.selectedSlot ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_BookingForm.BookingForm, {
84
+ slotIso: m.selectedSlot,
85
+ timeZone: m.timeZone,
86
+ eventLength: m.event.data.length,
87
+ submitting: m.submitting,
88
+ error: m.error,
89
+ onSubmit: m.submit,
90
+ onBack: m.back
91
+ }) : /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, {
92
+ children: [m.conflict ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(JustTakenBanner, {}) : null, m.slots.error ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.SizableText, {
93
+ size: "$2",
94
+ color: "$red10",
95
+ children: ["Couldn't load availability: ", m.slots.error]
96
+ }) : null, /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.XStack, {
97
+ gap: "$5",
98
+ flexDirection: "column",
99
+ $sm: {
100
+ flexDirection: "row"
101
+ },
102
+ alignItems: "flex-start",
103
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_MonthCalendar.MonthCalendar, {
104
+ viewDate: m.viewDate,
105
+ availableKeys: m.availableKeys,
106
+ selectedDay: m.selectedDay,
107
+ onSelectDay: m.selectDay,
108
+ onPrev: m.goPrevMonth,
109
+ onNext: m.goNextMonth,
110
+ atFirstMonth: m.atFirstMonth,
111
+ weekStartsOn: m.weekStartsOn,
112
+ loading: m.slots.loading
113
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_TimeSlots.TimeSlots, {
114
+ dayKey: m.selectedDay,
115
+ slots: m.daySlots,
116
+ timeZone: m.timeZone,
117
+ onPick: m.selectSlot,
118
+ selectedIso: m.selectedSlot,
119
+ loading: m.slots.loading && !m.selectedDay
120
+ })]
121
+ }), m.availableKeys.size === 0 && !m.slots.loading && !m.slots.error ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
122
+ size: "$3",
123
+ color: "$color10",
124
+ textAlign: "center",
125
+ children: "No availability this month \u2014 try the next month."
126
+ }) : null]
127
+ })]
128
+ })
129
+ });
130
+ }
131
+ function StatePanel(param) {
132
+ var {
133
+ children
134
+ } = param;
135
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
136
+ alignItems: "center",
137
+ justifyContent: "center",
138
+ gap: "$3",
139
+ padding: "$8",
140
+ minHeight: 220,
141
+ children
142
+ });
143
+ }
144
+ function JustTakenBanner() {
145
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
146
+ backgroundColor: "$yellow3",
147
+ borderColor: "$yellow6",
148
+ borderWidth: 1,
149
+ borderRadius: "$4",
150
+ padding: "$2.5",
151
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
152
+ size: "$2",
153
+ color: "$yellow11",
154
+ children: "That time was just taken. We refreshed the calendar \u2014 please pick another."
155
+ })
156
+ });
157
+ }
158
+ //# sourceMappingURL=Booker.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["__toCommonJS","mod","__copyProps","__defProp","value","Booker_exports","__export","Booker","module","exports","import_jsx_runtime","require","import_hanzogui","import_BookingForm","import_Confirmation","import_EventHeader","import_MonthCalendar","import_TimeSlots","import_useBooker","props","m","useBooker","_m_event_error","jsx","YStack","backgroundColor","borderColor","borderWidth","borderRadius","padding","gap","width","maxWidth","alignSelf","children","event","loading","jsxs","StatePanel","Spinner","size","color","SizableText","error","data","fontWeight","textAlign","phase","booking","Confirmation","timeZone","onDone","reset","EventHeader","host","compact","selectedSlot","BookingForm","slotIso","eventLength","length","submitting","onSubmit","submit","onBack","back","Fragment","conflict","JustTakenBanner","slots","XStack","flexDirection","$sm","alignItems","MonthCalendar","viewDate","availableKeys","selectedDay","onSelectDay","selectDay","onPrev","goPrevMonth","onNext","goNextMonth","atFirstMonth","weekStartsOn","TimeSlots","dayKey","daySlots","onPick","selectSlot","selectedIso","param"],"sources":["../../src/Booker.tsx"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,197 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
14
+ get: () => from[key],
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
21
+ value: true
22
+ }), mod);
23
+ var BookingForm_exports = {};
24
+ __export(BookingForm_exports, {
25
+ BookingForm: () => BookingForm
26
+ });
27
+ module.exports = __toCommonJS(BookingForm_exports);
28
+ var import_react = require("react");
29
+ var import_hanzogui = require("hanzogui");
30
+ var import_time = require("./time.cjs");
31
+ var import_jsx_runtime = require("react/jsx-runtime");
32
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
33
+ function BookingForm({
34
+ slotIso,
35
+ timeZone,
36
+ eventLength,
37
+ submitting,
38
+ error,
39
+ onSubmit,
40
+ onBack
41
+ }) {
42
+ const [name, setName] = (0, import_react.useState)("");
43
+ const [email, setEmail] = (0, import_react.useState)("");
44
+ const [notes, setNotes] = (0, import_react.useState)("");
45
+ const [touched, setTouched] = (0, import_react.useState)(false);
46
+ const nameValid = name.trim().length > 0;
47
+ const emailValid = EMAIL_RE.test(email.trim());
48
+ const valid = nameValid && emailValid;
49
+ const submit = () => {
50
+ setTouched(true);
51
+ if (!valid || submitting) return;
52
+ onSubmit({
53
+ name: name.trim(),
54
+ email: email.trim(),
55
+ notes: notes.trim() || void 0
56
+ });
57
+ };
58
+ return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.YStack, {
59
+ gap: "$3",
60
+ flex: 1,
61
+ minWidth: 240,
62
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.YStack, {
63
+ gap: "$1",
64
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.XStack, {
65
+ alignItems: "center",
66
+ gap: "$2",
67
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(BackLink, {
68
+ onPress: onBack
69
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
70
+ size: "$5",
71
+ fontWeight: "700",
72
+ color: "$color12",
73
+ children: "Enter details"
74
+ })]
75
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.SizableText, {
76
+ size: "$3",
77
+ color: "$color11",
78
+ children: [(0, import_time.formatBookingWhen)(slotIso, timeZone), " \xB7 ", eventLength, " min"]
79
+ })]
80
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(Field, {
81
+ label: "Name",
82
+ required: true,
83
+ invalid: touched && !nameValid,
84
+ hint: "Please enter your name",
85
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.Input, {
86
+ value: name,
87
+ onChangeText: setName,
88
+ placeholder: "Ada Lovelace",
89
+ autoComplete: "name",
90
+ size: "$4"
91
+ })
92
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(Field, {
93
+ label: "Email",
94
+ required: true,
95
+ invalid: touched && !emailValid,
96
+ hint: "Please enter a valid email",
97
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.Input, {
98
+ value: email,
99
+ onChangeText: setEmail,
100
+ placeholder: "ada@example.com",
101
+ keyboardType: "email-address",
102
+ autoCapitalize: "none",
103
+ autoComplete: "email",
104
+ size: "$4"
105
+ })
106
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(Field, {
107
+ label: "Notes",
108
+ hint: "Optional",
109
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.TextArea, {
110
+ value: notes,
111
+ onChangeText: setNotes,
112
+ placeholder: "Anything that would help prepare?",
113
+ numberOfLines: 3,
114
+ size: "$4"
115
+ })
116
+ }), error ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
117
+ backgroundColor: "$red3",
118
+ borderColor: "$red6",
119
+ borderWidth: 1,
120
+ borderRadius: "$4",
121
+ padding: "$2.5",
122
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
123
+ size: "$2",
124
+ color: "$red11",
125
+ children: error
126
+ })
127
+ }) : null, /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.XStack, {
128
+ gap: "$2",
129
+ justifyContent: "flex-end",
130
+ marginTop: "$1",
131
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.Button, {
132
+ size: "$4",
133
+ chromeless: true,
134
+ onPress: onBack,
135
+ disabled: submitting,
136
+ children: "Back"
137
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.Button, {
138
+ size: "$4",
139
+ theme: "blue",
140
+ onPress: submit,
141
+ disabled: submitting || touched && !valid,
142
+ opacity: submitting ? 0.7 : 1,
143
+ children: submitting ? "Booking\u2026" : "Confirm booking"
144
+ })]
145
+ })]
146
+ });
147
+ }
148
+ function Field({
149
+ label,
150
+ required,
151
+ invalid,
152
+ hint,
153
+ children
154
+ }) {
155
+ return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.YStack, {
156
+ gap: "$1.5",
157
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_hanzogui.SizableText, {
158
+ size: "$2",
159
+ color: "$color11",
160
+ fontWeight: "600",
161
+ children: [label, required ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
162
+ color: "$red10",
163
+ children: " *"
164
+ }) : null]
165
+ }), children, invalid && hint ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
166
+ size: "$1",
167
+ color: "$red10",
168
+ children: hint
169
+ }) : null]
170
+ });
171
+ }
172
+ function BackLink({
173
+ onPress
174
+ }) {
175
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.YStack, {
176
+ width: 30,
177
+ height: 30,
178
+ alignItems: "center",
179
+ justifyContent: "center",
180
+ borderRadius: "$4",
181
+ cursor: "pointer",
182
+ hoverStyle: {
183
+ backgroundColor: "$color4"
184
+ },
185
+ pressStyle: {
186
+ backgroundColor: "$color6"
187
+ },
188
+ onPress,
189
+ "aria-label": "Back",
190
+ role: "button",
191
+ children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_hanzogui.SizableText, {
192
+ size: "$6",
193
+ color: "$color11",
194
+ children: "\u2039"
195
+ })
196
+ });
197
+ }