@mylikita/booking-widget 0.1.0 → 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/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @mylikita/booking-widget
2
2
 
3
+ **Live on npm:** [`@mylikita/booking-widget`](https://www.npmjs.com/package/@mylikita/booking-widget) ·
4
+ [unpkg CDN](https://unpkg.com/@mylikita/booking-widget) ·
5
+ [React wrapper `@mylikita/booking-widget-react`](https://www.npmjs.com/package/@mylikita/booking-widget-react)
6
+
3
7
  Drop-in appointment booking for hospital websites built on MyLikita. A
4
8
  zero-dependency vanilla-JS widget (no framework required) that:
5
9
 
@@ -8,8 +12,9 @@ zero-dependency vanilla-JS widget (no framework required) that:
8
12
  3. polls the booking (`GET /v1/bookings/:ref`) until the hospital confirms,
9
13
  cancels, reschedules, marks no-show, or the request expires.
10
14
 
11
- It implements the [MyLikita Website Booking API (v1)](/backend/WEBSITE_BOOKING_API.md)
12
- exactly — same auth, same fields, same idempotency rules, same statuses.
15
+ It implements the **MyLikita Website Booking API (v1)** exactly — same auth,
16
+ same fields, same idempotency rules, same statuses — so the hospital side
17
+ needs no custom integration work.
13
18
 
14
19
  ---
15
20
 
@@ -40,7 +45,7 @@ import { createBookingWidget } from '@mylikita/booking-widget';
40
45
  createBookingWidget(document.getElementById('booking'), { relayUrl, websiteKey, facilityId });
41
46
  ```
42
47
 
43
- **React sites:** use [`@mylikita/booking-widget-react`](/packages/booking-widget-react) — the
48
+ **React sites:** use [`@mylikita/booking-widget-react`](https://www.npmjs.com/package/@mylikita/booking-widget-react) — the
44
49
  same widget as a `<BookingWidget relayUrl=… websiteKey=… facilityId=… />`
45
50
  component (all options are props, SSR-safe, imperative ref).
46
51
 
@@ -52,7 +57,7 @@ component (all options are props, SSR-safe, imperative ref).
52
57
  | `websiteKey` | string | — | **required** — your public client id (Bearer on every request) |
53
58
  | `facilityId` | string | — | **required** — the hospital's public facility id |
54
59
  | `providers` | array | `[]` | `[{ external_id, label }]` — shown as a "Preferred doctor" select; unmapped slugs simply arrive unassigned (never block on them). When provided, it wins and no fetch happens |
55
- | `loadProviders` | bool | `false` | **Phase C2/C3** — fetch the facility's mapped providers from the relay (`GET /v1/providers`) on mount and populate the doctor dropdown automatically. Ignored when `providers` is non-empty. A fetch failure is non-fatal: the widget keeps "No preference" and still works |
60
+ | `loadProviders` | bool | `false` | fetch the facility's mapped providers from the relay (`GET /v1/providers`) on mount and populate the doctor dropdown automatically. Ignored when `providers` is non-empty. A fetch failure is non-fatal: the widget keeps "No preference" and still works |
56
61
  | `services` | array | `[]` | `['General consultation', …]` — shown as a service select (free-text input otherwise) |
57
62
  | `durationMins` | number | `30` | sent with the booking |
58
63
  | `pollIntervalMs` | number | `5000` | how often to poll the booking status |
@@ -140,25 +145,11 @@ createBookingWidget(el, {
140
145
  });
141
146
  ```
142
147
 
143
- ## Demo
144
-
145
- `demo/demo.html` runs the widget **fully offline** against an in-page mock of
146
- the relay — submit a booking and watch it flip to *confirmed* after two polls.
147
- Open it in a browser directly:
148
-
149
- ```bash
150
- open packages/booking-widget/demo/demo.html
151
- ```
152
-
153
- ## Development
154
-
155
- ```bash
156
- npm run build # esbuild → dist/ (iife, iife.min, esm, cjs)
157
- npm test # mocked-fetch suite for the core logic
158
- ```
148
+ ## Support
159
149
 
160
- `dist/` is committed so the package works before any install; rebuild after
161
- touching `src/`.
150
+ - **Issues:** report through your MyLikita account manager or the MyLikita
151
+ support channel you were onboarded with.
152
+ - **The React wrapper:** [`@mylikita/booking-widget-react`](https://www.npmjs.com/package/@mylikita/booking-widget-react).
162
153
 
163
154
  ## Security notes
164
155
 
@@ -1,4 +1,4 @@
1
- /*! @mylikita/booking-widget v0.1.0 | MIT */
1
+ /*! @mylikita/booking-widget v0.1.1 | MIT */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -1,4 +1,4 @@
1
- /*! @mylikita/booking-widget v0.1.0 | MIT */
1
+ /*! @mylikita/booking-widget v0.1.1 | MIT */
2
2
 
3
3
  // src/client.js
4
4
  async function createBooking({ relayUrl, websiteKey, payload, signal }) {
@@ -1,4 +1,4 @@
1
- /*! @mylikita/booking-widget v0.1.0 | MIT */
1
+ /*! @mylikita/booking-widget v0.1.1 | MIT */
2
2
  var MyLikitaBookingWidget = (() => {
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1,4 +1,4 @@
1
- /*! @mylikita/booking-widget v0.1.0 | MIT */
1
+ /*! @mylikita/booking-widget v0.1.1 | MIT */
2
2
  var MyLikitaBookingWidget=(()=>{var Y=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var fe=Object.getOwnPropertyNames;var ge=Object.prototype.hasOwnProperty;var we=(i,e)=>{for(var t in e)Y(i,t,{get:e[t],enumerable:!0})},ye=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of fe(e))!ge.call(i,a)&&a!==t&&Y(i,a,{get:()=>e[a],enumerable:!(r=pe(e,a))||r.enumerable});return i};var _e=i=>ye(Y({},"__esModule",{value:!0}),i);var Ee={};we(Ee,{DEFAULT_THEME:()=>W,STATUS_COPY:()=>C,TERMINAL_STATUSES:()=>ie,createBooking:()=>S,createBookingWidget:()=>oe,fetchProviders:()=>E,fetchStatus:()=>T,newExternalRef:()=>P,pollStatus:()=>N,resolveTheme:()=>$,statusCopy:()=>A});async function S({relayUrl:i,websiteKey:e,payload:t,signal:r}){let a=await fetch(`${H(i)}/v1/bookings`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify(t),signal:r}),o=await F(a);if(a.status===409)return{ok:!0,duplicate:!0,booking_ref:o?.booking_ref,error:o?.message};if(!a.ok)throw O(a.status,o,"create");return{ok:!0,duplicate:!1,booking_ref:o?.booking_ref,status:o?.status||"pending_confirmation"}}async function E({relayUrl:i,websiteKey:e,signal:t}){let r=await fetch(`${H(i)}/v1/providers`,{headers:{Authorization:`Bearer ${e}`},signal:t}),a=await F(r);if(!r.ok)throw O(r.status,a,"providers");return(Array.isArray(a?.providers)?a.providers:[]).map(d=>({external_id:d.external_id,name:d.name||d.external_id,specialty:d.specialty||null,module:d.module||"general"}))}async function T({relayUrl:i,websiteKey:e,bookingRef:t,signal:r}){let a=await fetch(`${H(i)}/v1/bookings/${encodeURIComponent(t)}`,{headers:{Authorization:`Bearer ${e}`},signal:r}),o=await F(a);if(!a.ok)throw O(a.status,o,"status");return{booking_ref:o?.booking_ref,status:o?.status||"pending_confirmation",appt_ref:o?.appt_ref||null}}async function N(i,{intervalMs:e=5e3,maxTries:t=12,signal:r}={}){for(let a=0;a<t;a++){if(r?.aborted)return{status:"aborted",resolved:!1,data:null};let o=await i();if(o.status!=="pending_confirmation")return{status:o.status,resolved:!0,data:o};a<t-1&&await be(e,r)}return{status:"pending_confirmation",resolved:!1,data:null}}function P(){return`BK-${Date.now()}-${Math.random().toString(36).slice(2,6)}`}function H(i){return String(i||"").replace(/\/+$/,"")}function O(i,e,t){let r=new Error(e?.message||e?.error||`Relay ${t} failed (HTTP ${i})`);return r.code=e?.error||"http_error",r.status=i,r}async function F(i){try{return await i.json()}catch{return null}}function be(i,e){return new Promise(t=>{if(e?.aborted)return t();let r=()=>{clearTimeout(a),t()},a=setTimeout(()=>{e?.removeEventListener("abort",r),t()},i);e?.addEventListener("abort",r,{once:!0})})}var C={pending_confirmation:{title:"Request received",message:"We've received your booking request \u2014 we'll confirm shortly.",kind:"info"},confirmed:{title:"Appointment confirmed",message:"Your appointment is confirmed. See you at the clinic!",kind:"success"},cancelled:{title:"Appointment cancelled",message:"This appointment was cancelled. Please contact the clinic if this was unexpected.",kind:"danger"},rescheduled:{title:"Appointment rescheduled",message:"This appointment was moved \u2014 the new time was sent to you.",kind:"info"},no_show:{title:"Missed appointment",message:"This appointment was marked as missed.",kind:"danger"},expired:{title:"Request expired",message:"This booking request expired \u2014 please call the clinic to book.",kind:"danger"},poll_error:{title:"Something went wrong",message:"We could not check your booking right now. Please try again shortly.",kind:"danger"}};function A(i){return C[i]||C.pending_confirmation}var ie=["confirmed","cancelled","rescheduled","no_show","expired"];var W={primary:"#0d6efd",primaryDark:"#0b5ed7",primaryText:"#ffffff",bg:"#ffffff",text:"#1e293b",muted:"#64748b",border:"#e2e8f0",danger:"#dc3545",success:"#15803d",radius:10,font:"system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif"};function $(i={}){let e={...W,...i||{}};return{"--mlw-primary":e.primary,"--mlw-primary-dark":e.primaryDark,"--mlw-primary-text":e.primaryText,"--mlw-bg":e.bg,"--mlw-text":e.text,"--mlw-muted":e.muted,"--mlw-border":e.border,"--mlw-danger":e.danger,"--mlw-success":e.success,"--mlw-radius":`${e.radius}px`,"--mlw-font":e.font}}var re=`
3
3
  .mylikita-widget {
4
4
  --mlw-primary: #0d6efd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mylikita/booking-widget",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Drop-in appointment booking widget for MyLikita hospital websites. Submits bookings through the MyLikita relay (POST /v1/bookings) and polls status (GET /v1/bookings/:ref). Themeable via CSS variables.",
5
5
  "type": "module",
6
6
  "main": "dist/mylikita-booking-widget.cjs",