@medalsocial/booking 0.0.0-placeholder.0 → 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/CHANGELOG.md +27 -0
- package/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +220 -1
- package/dist/core/age.d.mts +34 -0
- package/dist/core/age.mjs +95 -0
- package/dist/core/age.mjs.map +1 -0
- package/dist/core/attempt-store.d.mts +111 -0
- package/dist/core/attempt-store.mjs +159 -0
- package/dist/core/attempt-store.mjs.map +1 -0
- package/dist/core/categories.d.mts +27 -0
- package/dist/core/categories.mjs +42 -0
- package/dist/core/categories.mjs.map +1 -0
- package/dist/core/clock.d.mts +33 -0
- package/dist/core/clock.mjs +251 -0
- package/dist/core/clock.mjs.map +1 -0
- package/dist/core/config.d.mts +178 -0
- package/dist/core/config.mjs +394 -0
- package/dist/core/config.mjs.map +1 -0
- package/dist/core/consent.d.mts +14 -0
- package/dist/core/consent.mjs +17 -0
- package/dist/core/consent.mjs.map +1 -0
- package/dist/core/deep-link.d.mts +40 -0
- package/dist/core/deep-link.mjs +160 -0
- package/dist/core/deep-link.mjs.map +1 -0
- package/dist/core/display-name.d.mts +18 -0
- package/dist/core/display-name.mjs +30 -0
- package/dist/core/display-name.mjs.map +1 -0
- package/dist/core/draft-store.d.mts +89 -0
- package/dist/core/draft-store.mjs +131 -0
- package/dist/core/draft-store.mjs.map +1 -0
- package/dist/core/dto.d.mts +46 -0
- package/dist/core/dto.mjs +157 -0
- package/dist/core/dto.mjs.map +1 -0
- package/dist/core/ics.d.mts +63 -0
- package/dist/core/ics.mjs +130 -0
- package/dist/core/ics.mjs.map +1 -0
- package/dist/core/index.d.mts +35 -0
- package/dist/core/index.mjs +32 -0
- package/dist/core/labels.d.mts +88 -0
- package/dist/core/labels.mjs +93 -0
- package/dist/core/labels.mjs.map +1 -0
- package/dist/core/machine.d.mts +645 -0
- package/dist/core/machine.mjs +988 -0
- package/dist/core/machine.mjs.map +1 -0
- package/dist/core/money.d.mts +12 -0
- package/dist/core/money.mjs +51 -0
- package/dist/core/money.mjs.map +1 -0
- package/dist/core/next-available.d.mts +20 -0
- package/dist/core/next-available.mjs +36 -0
- package/dist/core/next-available.mjs.map +1 -0
- package/dist/core/next-free.d.mts +25 -0
- package/dist/core/next-free.mjs +44 -0
- package/dist/core/next-free.mjs.map +1 -0
- package/dist/core/party-slots.d.mts +79 -0
- package/dist/core/party-slots.mjs +229 -0
- package/dist/core/party-slots.mjs.map +1 -0
- package/dist/core/paths.d.mts +11 -0
- package/dist/core/paths.mjs +56 -0
- package/dist/core/paths.mjs.map +1 -0
- package/dist/core/phone.d.mts +17 -0
- package/dist/core/phone.mjs +58 -0
- package/dist/core/phone.mjs.map +1 -0
- package/dist/core/portal/cookies.d.mts +29 -0
- package/dist/core/portal/dto.d.mts +140 -0
- package/dist/core/portal/dto.mjs +142 -0
- package/dist/core/portal/dto.mjs.map +1 -0
- package/dist/core/portal/family.d.mts +39 -0
- package/dist/core/portal/family.mjs +39 -0
- package/dist/core/portal/family.mjs.map +1 -0
- package/dist/core/portal/login-input.d.mts +29 -0
- package/dist/core/portal/login-input.mjs +29 -0
- package/dist/core/portal/login-input.mjs.map +1 -0
- package/dist/core/portal/next-path.d.mts +12 -0
- package/dist/core/portal/next-path.mjs +98 -0
- package/dist/core/portal/next-path.mjs.map +1 -0
- package/dist/core/portal/return-path.d.mts +7 -0
- package/dist/core/portal/return-path.mjs +49 -0
- package/dist/core/portal/return-path.mjs.map +1 -0
- package/dist/core/portal/session-cookie.d.mts +27 -0
- package/dist/core/portal/session-cookie.mjs +30 -0
- package/dist/core/portal/session-cookie.mjs.map +1 -0
- package/dist/core/portal/vipps-link.d.mts +56 -0
- package/dist/core/portal/vipps-link.mjs +136 -0
- package/dist/core/portal/vipps-link.mjs.map +1 -0
- package/dist/core/portal/vipps-return.d.mts +39 -0
- package/dist/core/portal/vipps-return.mjs +69 -0
- package/dist/core/portal/vipps-return.mjs.map +1 -0
- package/dist/core/rebook-store.d.mts +28 -0
- package/dist/core/rebook-store.mjs +60 -0
- package/dist/core/rebook-store.mjs.map +1 -0
- package/dist/core/restore-gate.d.mts +40 -0
- package/dist/core/restore-gate.mjs +96 -0
- package/dist/core/restore-gate.mjs.map +1 -0
- package/dist/core/stores.d.mts +8 -0
- package/dist/core/stores.mjs +23 -0
- package/dist/core/stores.mjs.map +1 -0
- package/dist/core/types.d.mts +245 -0
- package/dist/core/visit.d.mts +26 -0
- package/dist/core/visit.mjs +40 -0
- package/dist/core/visit.mjs.map +1 -0
- package/dist/core/wire.d.mts +37 -0
- package/dist/next/cache/memory.d.mts +14 -0
- package/dist/next/cache/memory.mjs +51 -0
- package/dist/next/cache/memory.mjs.map +1 -0
- package/dist/next/cache/next-data.d.mts +6 -0
- package/dist/next/cache/next-data.mjs +35 -0
- package/dist/next/cache/next-data.mjs.map +1 -0
- package/dist/next/cache/noop.d.mts +6 -0
- package/dist/next/cache/noop.mjs +15 -0
- package/dist/next/cache/noop.mjs.map +1 -0
- package/dist/next/cache/workers.d.mts +29 -0
- package/dist/next/cache/workers.mjs +74 -0
- package/dist/next/cache/workers.mjs.map +1 -0
- package/dist/next/catalogue.d.mts +38 -0
- package/dist/next/catalogue.mjs +215 -0
- package/dist/next/catalogue.mjs.map +1 -0
- package/dist/next/handler.d.mts +11 -0
- package/dist/next/handler.mjs +150 -0
- package/dist/next/handler.mjs.map +1 -0
- package/dist/next/index.d.mts +17 -0
- package/dist/next/index.mjs +14 -0
- package/dist/next/loaders.d.mts +79 -0
- package/dist/next/loaders.mjs +195 -0
- package/dist/next/loaders.mjs.map +1 -0
- package/dist/next/manage-dto.d.mts +6 -0
- package/dist/next/manage-dto.mjs +28 -0
- package/dist/next/manage-dto.mjs.map +1 -0
- package/dist/next/medal.d.mts +105 -0
- package/dist/next/medal.mjs +155 -0
- package/dist/next/medal.mjs.map +1 -0
- package/dist/next/options.d.mts +158 -0
- package/dist/next/options.mjs +65 -0
- package/dist/next/options.mjs.map +1 -0
- package/dist/next/portal/guardian.d.mts +42 -0
- package/dist/next/portal/guardian.mjs +76 -0
- package/dist/next/portal/guardian.mjs.map +1 -0
- package/dist/next/portal/medal-portal.d.mts +161 -0
- package/dist/next/portal/medal-portal.mjs +825 -0
- package/dist/next/portal/medal-portal.mjs.map +1 -0
- package/dist/next/portal/session.d.mts +38 -0
- package/dist/next/portal/session.mjs +40 -0
- package/dist/next/portal/session.mjs.map +1 -0
- package/dist/next/portal/vipps-flash.d.mts +14 -0
- package/dist/next/portal/vipps-flash.mjs +41 -0
- package/dist/next/portal/vipps-flash.mjs.map +1 -0
- package/dist/next/portal.d.mts +205 -0
- package/dist/next/portal.mjs +469 -0
- package/dist/next/portal.mjs.map +1 -0
- package/dist/next/request.d.mts +51 -0
- package/dist/next/request.mjs +123 -0
- package/dist/next/request.mjs.map +1 -0
- package/dist/next/routes/catalogue-routes.mjs +344 -0
- package/dist/next/routes/catalogue-routes.mjs.map +1 -0
- package/dist/next/routes/create.mjs +460 -0
- package/dist/next/routes/create.mjs.map +1 -0
- package/dist/next/routes/manage.mjs +191 -0
- package/dist/next/routes/manage.mjs.map +1 -0
- package/dist/next/routes/portal-routes.mjs +312 -0
- package/dist/next/routes/portal-routes.mjs.map +1 -0
- package/dist/next/routes/shared.mjs +162 -0
- package/dist/next/routes/shared.mjs.map +1 -0
- package/dist/next/routes/vipps-return.mjs +166 -0
- package/dist/next/routes/vipps-return.mjs.map +1 -0
- package/dist/next/runtime.d.mts +49 -0
- package/dist/next/runtime.mjs +94 -0
- package/dist/next/runtime.mjs.map +1 -0
- package/dist/next/seed.d.mts +71 -0
- package/dist/next/seed.mjs +495 -0
- package/dist/next/seed.mjs.map +1 -0
- package/dist/next/server.d.mts +26 -0
- package/dist/next/server.mjs +63 -0
- package/dist/next/server.mjs.map +1 -0
- package/dist/react/BookingLink.d.mts +61 -0
- package/dist/react/BookingLink.mjs +102 -0
- package/dist/react/BookingLink.mjs.map +1 -0
- package/dist/react/BookingWizard.d.mts +49 -0
- package/dist/react/BookingWizard.mjs +598 -0
- package/dist/react/BookingWizard.mjs.map +1 -0
- package/dist/react/LoginPageQuery.d.mts +37 -0
- package/dist/react/LoginPageQuery.mjs +106 -0
- package/dist/react/LoginPageQuery.mjs.map +1 -0
- package/dist/react/LoginSheet.d.mts +49 -0
- package/dist/react/LoginSheet.mjs +98 -0
- package/dist/react/LoginSheet.mjs.map +1 -0
- package/dist/react/ManageBooking.d.mts +50 -0
- package/dist/react/ManageBooking.mjs +197 -0
- package/dist/react/ManageBooking.mjs.map +1 -0
- package/dist/react/PortalDashboard.d.mts +10 -0
- package/dist/react/Provider.d.mts +82 -0
- package/dist/react/Provider.mjs +132 -0
- package/dist/react/Provider.mjs.map +1 -0
- package/dist/react/actions.d.mts +133 -0
- package/dist/react/actions.mjs +90 -0
- package/dist/react/actions.mjs.map +1 -0
- package/dist/react/index.d.mts +28 -0
- package/dist/react/index.mjs +20 -0
- package/dist/react/kit.d.mts +54 -0
- package/dist/react/kit.mjs +127 -0
- package/dist/react/kit.mjs.map +1 -0
- package/dist/react/labels/index.d.mts +23 -0
- package/dist/react/labels/index.mjs +44 -0
- package/dist/react/labels/index.mjs.map +1 -0
- package/dist/react/labels/login.d.mts +10 -0
- package/dist/react/labels/login.mjs +13 -0
- package/dist/react/labels/login.mjs.map +1 -0
- package/dist/react/labels/manage.d.mts +15 -0
- package/dist/react/labels/manage.mjs +19 -0
- package/dist/react/labels/manage.mjs.map +1 -0
- package/dist/react/labels/portal.d.mts +25 -0
- package/dist/react/labels/portal.mjs +41 -0
- package/dist/react/labels/portal.mjs.map +1 -0
- package/dist/react/labels/screens.d.mts +11 -0
- package/dist/react/labels/screens.mjs +721 -0
- package/dist/react/labels/screens.mjs.map +1 -0
- package/dist/react/labels/wizard.d.mts +76 -0
- package/dist/react/labels/wizard.mjs +97 -0
- package/dist/react/labels/wizard.mjs.map +1 -0
- package/dist/react/link/index.d.mts +5 -0
- package/dist/react/link/index.mjs +5 -0
- package/dist/react/portal/SessionTouch.d.mts +19 -0
- package/dist/react/portal/SessionTouch.mjs +40 -0
- package/dist/react/portal/SessionTouch.mjs.map +1 -0
- package/dist/react/portal/age-prompt.d.mts +10 -0
- package/dist/react/portal/age-prompt.mjs +21 -0
- package/dist/react/portal/age-prompt.mjs.map +1 -0
- package/dist/react/portal/book-again.d.mts +18 -0
- package/dist/react/portal/book-again.mjs +21 -0
- package/dist/react/portal/book-again.mjs.map +1 -0
- package/dist/react/portal/child-summary.d.mts +17 -0
- package/dist/react/portal/child-summary.mjs +45 -0
- package/dist/react/portal/child-summary.mjs.map +1 -0
- package/dist/react/portal/dashboard.d.mts +74 -0
- package/dist/react/portal/dashboard.mjs +204 -0
- package/dist/react/portal/dashboard.mjs.map +1 -0
- package/dist/react/portal/leave.d.mts +17 -0
- package/dist/react/portal/leave.mjs +21 -0
- package/dist/react/portal/leave.mjs.map +1 -0
- package/dist/react/portal/sections.d.mts +12 -0
- package/dist/react/portal/sections.mjs +48 -0
- package/dist/react/portal/sections.mjs.map +1 -0
- package/dist/react/portal/tabs.d.mts +23 -0
- package/dist/react/portal/tabs.mjs +36 -0
- package/dist/react/portal/tabs.mjs.map +1 -0
- package/dist/react/portal/vipps-flash.d.mts +11 -0
- package/dist/react/portal/vipps-flash.mjs +18 -0
- package/dist/react/portal/vipps-flash.mjs.map +1 -0
- package/dist/react/portal/wired.mjs +202 -0
- package/dist/react/portal/wired.mjs.map +1 -0
- package/dist/react/screen-labels.mjs +14 -0
- package/dist/react/screen-labels.mjs.map +1 -0
- package/dist/react/shared.d.mts +13 -0
- package/dist/react/shared.mjs +7 -0
- package/dist/react/use-next-free.d.mts +19 -0
- package/dist/react/use-next-free.mjs +56 -0
- package/dist/react/use-next-free.mjs.map +1 -0
- package/dist/react/useBooking.d.mts +179 -0
- package/dist/react/useBooking.mjs +1331 -0
- package/dist/react/useBooking.mjs.map +1 -0
- package/dist/react/wizard/adapters.mjs +189 -0
- package/dist/react/wizard/adapters.mjs.map +1 -0
- package/dist/react/wizard/index.d.mts +11 -0
- package/dist/react/wizard/index.mjs +6 -0
- package/dist/react/wizard-error.mjs +29 -0
- package/dist/react/wizard-error.mjs.map +1 -0
- package/package.json +126 -5
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createBookingRuntime } from "./runtime.mjs";
|
|
2
|
+
import { createBookingHandlerFor } from "./handler.mjs";
|
|
3
|
+
import { loadBookingPage as loadBookingPage$1, loadManagePage as loadManagePage$1, loadPortalPage as loadPortalPage$1 } from "./loaders.mjs";
|
|
4
|
+
import { createPortalFor } from "./portal.mjs";
|
|
5
|
+
//#region src/next/server.ts
|
|
6
|
+
/**
|
|
7
|
+
* One call that wires a booking site's server half.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* // lib/booking/server.ts
|
|
11
|
+
* import 'server-only';
|
|
12
|
+
* import { createBookingServer } from '@medalsocial/booking/next';
|
|
13
|
+
* export const booking = createBookingServer({ config, medal, cache, … });
|
|
14
|
+
*
|
|
15
|
+
* // app/api/booking/[...path]/route.ts (and app/api/portal/[...path]/route.ts)
|
|
16
|
+
* export const { GET, POST, DELETE } = booking.handler;
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The handler, the loaders and the portal share one runtime, so in-flight
|
|
20
|
+
* refreshes and Medal clients are shared too. The standalone functions
|
|
21
|
+
* (`createBookingHandler`, `loadBookingPage`, …) reuse the runtime built for
|
|
22
|
+
* the same options object.
|
|
23
|
+
*/
|
|
24
|
+
const RUNTIMES = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
function runtimeFor(options) {
|
|
26
|
+
const known = RUNTIMES.get(options);
|
|
27
|
+
if (known) return known;
|
|
28
|
+
const built = createBookingRuntime(options);
|
|
29
|
+
RUNTIMES.set(options, built);
|
|
30
|
+
return built;
|
|
31
|
+
}
|
|
32
|
+
function createBookingServer(options) {
|
|
33
|
+
return serverFor(runtimeFor(options));
|
|
34
|
+
}
|
|
35
|
+
/** The same, over a runtime the caller built (tests, unusual wiring). */
|
|
36
|
+
function serverFor(rt) {
|
|
37
|
+
return {
|
|
38
|
+
handler: createBookingHandlerFor(rt),
|
|
39
|
+
portal: createPortalFor(rt),
|
|
40
|
+
loadBookingPage: (args) => loadBookingPage$1(rt, args),
|
|
41
|
+
loadManagePage: (token, switches) => loadManagePage$1(rt, token, switches),
|
|
42
|
+
loadPortalPage: (switches) => loadPortalPage$1(rt, switches)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function createBookingHandler(options) {
|
|
46
|
+
return createBookingHandlerFor(runtimeFor(options));
|
|
47
|
+
}
|
|
48
|
+
function createPortal(options) {
|
|
49
|
+
return createPortalFor(runtimeFor(options));
|
|
50
|
+
}
|
|
51
|
+
function loadBookingPage(options, args) {
|
|
52
|
+
return loadBookingPage$1(runtimeFor(options), args);
|
|
53
|
+
}
|
|
54
|
+
function loadManagePage(options, token, switches) {
|
|
55
|
+
return loadManagePage$1(runtimeFor(options), token, switches);
|
|
56
|
+
}
|
|
57
|
+
function loadPortalPage(options, switches) {
|
|
58
|
+
return loadPortalPage$1(runtimeFor(options), switches);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { createBookingHandler, createBookingServer, createPortal, loadBookingPage, loadManagePage, loadPortalPage, serverFor };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["loadBookingPageFor","loadManagePageFor","loadPortalPageFor"],"sources":["../../src/next/server.ts"],"sourcesContent":["/**\n * One call that wires a booking site's server half.\n *\n * ```ts\n * // lib/booking/server.ts\n * import 'server-only';\n * import { createBookingServer } from '@medalsocial/booking/next';\n * export const booking = createBookingServer({ config, medal, cache, … });\n *\n * // app/api/booking/[...path]/route.ts (and app/api/portal/[...path]/route.ts)\n * export const { GET, POST, DELETE } = booking.handler;\n * ```\n *\n * The handler, the loaders and the portal share one runtime, so in-flight\n * refreshes and Medal clients are shared too. The standalone functions\n * (`createBookingHandler`, `loadBookingPage`, …) reuse the runtime built for\n * the same options object.\n */\n\nimport { createBookingHandlerFor } from './handler';\nimport {\n type BookingPageResult,\n loadBookingPage as loadBookingPageFor,\n loadManagePage as loadManagePageFor,\n loadPortalPage as loadPortalPageFor,\n type ManagePageResult,\n type PageSwitches,\n type PortalPageResult,\n} from './loaders';\nimport type { BookingServerOptions } from './options';\nimport { createPortalFor, type Portal } from './portal';\nimport { type BookingRuntime, createBookingRuntime } from './runtime';\n\nconst RUNTIMES = new WeakMap<BookingServerOptions, BookingRuntime>();\n\nfunction runtimeFor(options: BookingServerOptions): BookingRuntime {\n const known = RUNTIMES.get(options);\n if (known) return known;\n const built = createBookingRuntime(options);\n RUNTIMES.set(options, built);\n return built;\n}\n\ntype SearchParams = Record<string, string | string[] | undefined>;\n\nexport interface BookingServer {\n handler: ReturnType<typeof createBookingHandlerFor>;\n portal: Portal;\n loadBookingPage(\n args?: PageSwitches & { searchParams?: SearchParams }\n ): Promise<BookingPageResult>;\n loadManagePage(token: string, switches?: PageSwitches): Promise<ManagePageResult>;\n loadPortalPage(switches?: PageSwitches): Promise<PortalPageResult>;\n}\n\nexport function createBookingServer(options: BookingServerOptions): BookingServer {\n return serverFor(runtimeFor(options));\n}\n\n/** The same, over a runtime the caller built (tests, unusual wiring). */\nexport function serverFor(rt: BookingRuntime): BookingServer {\n return {\n handler: createBookingHandlerFor(rt),\n portal: createPortalFor(rt),\n loadBookingPage: (args) => loadBookingPageFor(rt, args),\n loadManagePage: (token, switches) => loadManagePageFor(rt, token, switches),\n loadPortalPage: (switches) => loadPortalPageFor(rt, switches),\n };\n}\n\nexport function createBookingHandler(options: BookingServerOptions) {\n return createBookingHandlerFor(runtimeFor(options));\n}\n\nexport function createPortal(options: BookingServerOptions): Portal {\n return createPortalFor(runtimeFor(options));\n}\n\nexport function loadBookingPage(\n options: BookingServerOptions,\n args?: PageSwitches & { searchParams?: SearchParams }\n): Promise<BookingPageResult> {\n return loadBookingPageFor(runtimeFor(options), args);\n}\n\nexport function loadManagePage(\n options: BookingServerOptions,\n token: string,\n switches?: PageSwitches\n): Promise<ManagePageResult> {\n return loadManagePageFor(runtimeFor(options), token, switches);\n}\n\nexport function loadPortalPage(\n options: BookingServerOptions,\n switches?: PageSwitches\n): Promise<PortalPageResult> {\n return loadPortalPageFor(runtimeFor(options), switches);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAM,2BAAW,IAAI,QAA8C;AAEnE,SAAS,WAAW,SAA+C;CACjE,MAAM,QAAQ,SAAS,IAAI,OAAO;CAClC,IAAI,OAAO,OAAO;CAClB,MAAM,QAAQ,qBAAqB,OAAO;CAC1C,SAAS,IAAI,SAAS,KAAK;CAC3B,OAAO;AACT;AAcA,SAAgB,oBAAoB,SAA8C;CAChF,OAAO,UAAU,WAAW,OAAO,CAAC;AACtC;;AAGA,SAAgB,UAAU,IAAmC;CAC3D,OAAO;EACL,SAAS,wBAAwB,EAAE;EACnC,QAAQ,gBAAgB,EAAE;EAC1B,kBAAkB,SAASA,kBAAmB,IAAI,IAAI;EACtD,iBAAiB,OAAO,aAAaC,iBAAkB,IAAI,OAAO,QAAQ;EAC1E,iBAAiB,aAAaC,iBAAkB,IAAI,QAAQ;CAC9D;AACF;AAEA,SAAgB,qBAAqB,SAA+B;CAClE,OAAO,wBAAwB,WAAW,OAAO,CAAC;AACpD;AAEA,SAAgB,aAAa,SAAuC;CAClE,OAAO,gBAAgB,WAAW,OAAO,CAAC;AAC5C;AAEA,SAAgB,gBACd,SACA,MAC4B;CAC5B,OAAOF,kBAAmB,WAAW,OAAO,GAAG,IAAI;AACrD;AAEA,SAAgB,eACd,SACA,OACA,UAC2B;CAC3B,OAAOC,iBAAkB,WAAW,OAAO,GAAG,OAAO,QAAQ;AAC/D;AAEA,SAAgB,eACd,SACA,UAC2B;CAC3B,OAAOC,iBAAkB,WAAW,OAAO,GAAG,QAAQ;AACxD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BookingConfig } from "../core/config.mjs";
|
|
2
|
+
import { BookingOverrides } from "./Provider.mjs";
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { ComponentProps } from "react";
|
|
5
|
+
//#region src/react/BookingLink.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Every in-site link into the booking page, so the one tap a site exists for
|
|
8
|
+
* never waits on a blank screen.
|
|
9
|
+
*
|
|
10
|
+
* PREFETCH. The booking page is dynamic, and a dynamic route with no
|
|
11
|
+
* `loading.js` is prefetched only down to its layout. So the bare booking
|
|
12
|
+
* path is prefetched in FULL (`prefetch`), once per page view however many
|
|
13
|
+
* buttons point at it. A deep link (`?kategori=barn`) is a different URL and
|
|
14
|
+
* a different server render, and a home page may carry a dozen, so those
|
|
15
|
+
* switch to a full prefetch only on intent — hover, or the touch that starts a
|
|
16
|
+
* tap.
|
|
17
|
+
*
|
|
18
|
+
* FEEDBACK. A route `loading.tsx` would stream a shell before the page runs,
|
|
19
|
+
* and a booking page that hands off to another provider answers with a
|
|
20
|
+
* redirect: the shell would turn it into a client redirect after a flash. So
|
|
21
|
+
* `:active` presses the button in the tap's frame, and while the navigation is
|
|
22
|
+
* still pending `BookingPendingHost` draws the booking frame over the page.
|
|
23
|
+
*/
|
|
24
|
+
type PathsConfig = Pick<BookingConfig, 'paths'>;
|
|
25
|
+
/** A link into the booking page, with or without a query; `null` for anything else. */
|
|
26
|
+
export declare function bookingTarget(href: string, config: PathsConfig): 'bare' | 'deep' | null;
|
|
27
|
+
/** The `next/link` prefetch props for one href. */
|
|
28
|
+
export declare function bookingPrefetch(href: string, config: PathsConfig): {
|
|
29
|
+
prefetch?: boolean;
|
|
30
|
+
unstable_dynamicOnHover?: boolean;
|
|
31
|
+
};
|
|
32
|
+
/** Fired by a booking link on a plain tap; heard by `BookingPendingHost`. */
|
|
33
|
+
export declare const BOOKING_NAVIGATION_EVENT = "medal-booking:navigation";
|
|
34
|
+
/** A navigation that lands inside this never shows the frame at all. */
|
|
35
|
+
export declare const PENDING_DELAY_MS = 120;
|
|
36
|
+
/** A navigation that never lands (offline, aborted) stops covering the page. */
|
|
37
|
+
export declare const PENDING_TIMEOUT_MS = 12000;
|
|
38
|
+
/**
|
|
39
|
+
* Draws the booking frame (meda's `BookingSkeleton`) for a navigation into
|
|
40
|
+
* the booking page that is taking a moment, and removes it when the route
|
|
41
|
+
* lands.
|
|
42
|
+
*
|
|
43
|
+
* In the root layout, not in the link: the link that was tapped may be gone
|
|
44
|
+
* before the navigation is — a menu overlay closes on the same tap — and the
|
|
45
|
+
* frame has to outlive it. Shown only after `PENDING_DELAY_MS`, so a
|
|
46
|
+
* prefetched navigation (the usual case) never flashes it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function BookingPendingHost(props: BookingOverrides): import("react").JSX.Element | null;
|
|
49
|
+
export type BookingLinkProps = Omit<ComponentProps<typeof Link>, 'prefetch' | 'href'> & {
|
|
50
|
+
href: string;
|
|
51
|
+
/** The site's config; or the nearest `BookingProvider`'s. */
|
|
52
|
+
config?: Readonly<BookingConfig>;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* `next/link` for a booking destination. Anything that is not the booking
|
|
56
|
+
* page — a landing page, the price list — passes straight through, so callers
|
|
57
|
+
* can hand it whatever their CMS field holds.
|
|
58
|
+
*/
|
|
59
|
+
export declare function BookingLink({ href, children, onClick, target, config, ...rest }: BookingLinkProps): import("react").JSX.Element;
|
|
60
|
+
//#endregion
|
|
61
|
+
//# sourceMappingURL=BookingLink.d.mts.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useBookingKit } from "./Provider.mjs";
|
|
3
|
+
import { screenLabels } from "./screen-labels.mjs";
|
|
4
|
+
import { BookingSkeleton } from "@medalsocial/meda/booking";
|
|
5
|
+
import Link from "next/link";
|
|
6
|
+
import { usePathname } from "next/navigation";
|
|
7
|
+
import { useEffect, useRef, useState } from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
//#region src/react/BookingLink.tsx
|
|
10
|
+
/** A link into the booking page, with or without a query; `null` for anything else. */
|
|
11
|
+
function bookingTarget(href, config) {
|
|
12
|
+
const path = config.paths.booking;
|
|
13
|
+
if (href === path) return "bare";
|
|
14
|
+
return href.startsWith(`${path}?`) ? "deep" : null;
|
|
15
|
+
}
|
|
16
|
+
/** The `next/link` prefetch props for one href. */
|
|
17
|
+
function bookingPrefetch(href, config) {
|
|
18
|
+
const target = bookingTarget(href, config);
|
|
19
|
+
if (target === "bare") return { prefetch: true };
|
|
20
|
+
if (target === "deep") return { unstable_dynamicOnHover: true };
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
/** Fired by a booking link on a plain tap; heard by `BookingPendingHost`. */
|
|
24
|
+
const BOOKING_NAVIGATION_EVENT = "medal-booking:navigation";
|
|
25
|
+
/** A navigation that lands inside this never shows the frame at all. */
|
|
26
|
+
const PENDING_DELAY_MS = 120;
|
|
27
|
+
/** A navigation that never lands (offline, aborted) stops covering the page. */
|
|
28
|
+
const PENDING_TIMEOUT_MS = 12e3;
|
|
29
|
+
/** Only a tap the router will handle in this tab: not ⌘/ctrl/shift-click. */
|
|
30
|
+
function isPlainTap(event) {
|
|
31
|
+
return !event.defaultPrevented && event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Draws the booking frame (meda's `BookingSkeleton`) for a navigation into
|
|
35
|
+
* the booking page that is taking a moment, and removes it when the route
|
|
36
|
+
* lands.
|
|
37
|
+
*
|
|
38
|
+
* In the root layout, not in the link: the link that was tapped may be gone
|
|
39
|
+
* before the navigation is — a menu overlay closes on the same tap — and the
|
|
40
|
+
* frame has to outlive it. Shown only after `PENDING_DELAY_MS`, so a
|
|
41
|
+
* prefetched navigation (the usual case) never flashes it.
|
|
42
|
+
*/
|
|
43
|
+
function BookingPendingHost(props) {
|
|
44
|
+
const { kit, classNames } = useBookingKit(props);
|
|
45
|
+
const pathname = usePathname();
|
|
46
|
+
const [visible, setVisible] = useState(false);
|
|
47
|
+
const timers = useRef([]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const clear = () => {
|
|
50
|
+
for (const timer of timers.current) window.clearTimeout(timer);
|
|
51
|
+
timers.current = [];
|
|
52
|
+
};
|
|
53
|
+
const start = () => {
|
|
54
|
+
clear();
|
|
55
|
+
timers.current = [window.setTimeout(() => setVisible(true), 120), window.setTimeout(() => setVisible(false), PENDING_TIMEOUT_MS)];
|
|
56
|
+
};
|
|
57
|
+
const stop = () => {
|
|
58
|
+
clear();
|
|
59
|
+
setVisible(false);
|
|
60
|
+
};
|
|
61
|
+
window.addEventListener(BOOKING_NAVIGATION_EVENT, start);
|
|
62
|
+
window.addEventListener("popstate", stop);
|
|
63
|
+
return () => {
|
|
64
|
+
clear();
|
|
65
|
+
window.removeEventListener(BOOKING_NAVIGATION_EVENT, start);
|
|
66
|
+
window.removeEventListener("popstate", stop);
|
|
67
|
+
};
|
|
68
|
+
}, []);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
for (const timer of timers.current) window.clearTimeout(timer);
|
|
71
|
+
timers.current = [];
|
|
72
|
+
setVisible(false);
|
|
73
|
+
}, [pathname]);
|
|
74
|
+
return visible ? /* @__PURE__ */ jsx(BookingSkeleton, {
|
|
75
|
+
labels: screenLabels(kit.labels),
|
|
76
|
+
classNames: classNames.skeleton
|
|
77
|
+
}) : null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* `next/link` for a booking destination. Anything that is not the booking
|
|
81
|
+
* page — a landing page, the price list — passes straight through, so callers
|
|
82
|
+
* can hand it whatever their CMS field holds.
|
|
83
|
+
*/
|
|
84
|
+
function BookingLink({ href, children, onClick, target, config, ...rest }) {
|
|
85
|
+
const paths = useBookingKit({ config }).kit.config;
|
|
86
|
+
const booking = bookingTarget(href, paths) !== null;
|
|
87
|
+
return /* @__PURE__ */ jsx(Link, {
|
|
88
|
+
href,
|
|
89
|
+
target,
|
|
90
|
+
...bookingPrefetch(href, paths),
|
|
91
|
+
onClick: (event) => {
|
|
92
|
+
onClick?.(event);
|
|
93
|
+
if (booking && !target && isPlainTap(event) && window.location.pathname !== paths.paths.booking) window.dispatchEvent(new Event(BOOKING_NAVIGATION_EVENT));
|
|
94
|
+
},
|
|
95
|
+
...rest,
|
|
96
|
+
children
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { BOOKING_NAVIGATION_EVENT, BookingLink, BookingPendingHost, PENDING_DELAY_MS, PENDING_TIMEOUT_MS, bookingPrefetch, bookingTarget };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=BookingLink.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BookingLink.mjs","names":[],"sources":["../../src/react/BookingLink.tsx"],"sourcesContent":["import { BookingSkeleton } from '@medalsocial/meda/booking';\nimport Link from 'next/link';\nimport { usePathname } from 'next/navigation';\nimport { type ComponentProps, type MouseEvent, useEffect, useRef, useState } from 'react';\nimport type { BookingConfig } from '../core/config';\nimport { type BookingOverrides, useBookingKit } from './Provider';\nimport { screenLabels } from './screen-labels';\n\n/**\n * Every in-site link into the booking page, so the one tap a site exists for\n * never waits on a blank screen.\n *\n * PREFETCH. The booking page is dynamic, and a dynamic route with no\n * `loading.js` is prefetched only down to its layout. So the bare booking\n * path is prefetched in FULL (`prefetch`), once per page view however many\n * buttons point at it. A deep link (`?kategori=barn`) is a different URL and\n * a different server render, and a home page may carry a dozen, so those\n * switch to a full prefetch only on intent — hover, or the touch that starts a\n * tap.\n *\n * FEEDBACK. A route `loading.tsx` would stream a shell before the page runs,\n * and a booking page that hands off to another provider answers with a\n * redirect: the shell would turn it into a client redirect after a flash. So\n * `:active` presses the button in the tap's frame, and while the navigation is\n * still pending `BookingPendingHost` draws the booking frame over the page.\n */\n\ntype PathsConfig = Pick<BookingConfig, 'paths'>;\n\n/** A link into the booking page, with or without a query; `null` for anything else. */\nexport function bookingTarget(href: string, config: PathsConfig): 'bare' | 'deep' | null {\n const path = config.paths.booking;\n if (href === path) return 'bare';\n return href.startsWith(`${path}?`) ? 'deep' : null;\n}\n\n/** The `next/link` prefetch props for one href. */\nexport function bookingPrefetch(\n href: string,\n config: PathsConfig\n): { prefetch?: boolean; unstable_dynamicOnHover?: boolean } {\n const target = bookingTarget(href, config);\n if (target === 'bare') return { prefetch: true };\n if (target === 'deep') return { unstable_dynamicOnHover: true };\n return {};\n}\n\n/** Fired by a booking link on a plain tap; heard by `BookingPendingHost`. */\nexport const BOOKING_NAVIGATION_EVENT = 'medal-booking:navigation';\n/** A navigation that lands inside this never shows the frame at all. */\nexport const PENDING_DELAY_MS = 120;\n/** A navigation that never lands (offline, aborted) stops covering the page. */\nexport const PENDING_TIMEOUT_MS = 12_000;\n\n/** Only a tap the router will handle in this tab: not ⌘/ctrl/shift-click. */\nfunction isPlainTap(event: MouseEvent<HTMLAnchorElement>): boolean {\n return (\n !event.defaultPrevented &&\n event.button === 0 &&\n !event.metaKey &&\n !event.ctrlKey &&\n !event.shiftKey &&\n !event.altKey\n );\n}\n\n/**\n * Draws the booking frame (meda's `BookingSkeleton`) for a navigation into\n * the booking page that is taking a moment, and removes it when the route\n * lands.\n *\n * In the root layout, not in the link: the link that was tapped may be gone\n * before the navigation is — a menu overlay closes on the same tap — and the\n * frame has to outlive it. Shown only after `PENDING_DELAY_MS`, so a\n * prefetched navigation (the usual case) never flashes it.\n */\nexport function BookingPendingHost(props: BookingOverrides) {\n const { kit, classNames } = useBookingKit(props);\n const pathname = usePathname();\n const [visible, setVisible] = useState(false);\n const timers = useRef<number[]>([]);\n\n useEffect(() => {\n const clear = () => {\n for (const timer of timers.current) window.clearTimeout(timer);\n timers.current = [];\n };\n const start = () => {\n clear();\n timers.current = [\n window.setTimeout(() => setVisible(true), PENDING_DELAY_MS),\n window.setTimeout(() => setVisible(false), PENDING_TIMEOUT_MS),\n ];\n };\n const stop = () => {\n clear();\n setVisible(false);\n };\n window.addEventListener(BOOKING_NAVIGATION_EVENT, start);\n window.addEventListener('popstate', stop);\n return () => {\n clear();\n window.removeEventListener(BOOKING_NAVIGATION_EVENT, start);\n window.removeEventListener('popstate', stop);\n };\n }, []);\n\n // The route landed (or the visitor went elsewhere): the frame's job is done.\n // biome-ignore lint/correctness/useExhaustiveDependencies: runs on arrival, keyed by the path.\n useEffect(() => {\n for (const timer of timers.current) window.clearTimeout(timer);\n timers.current = [];\n setVisible(false);\n }, [pathname]);\n\n return visible ? (\n <BookingSkeleton labels={screenLabels(kit.labels)} classNames={classNames.skeleton} />\n ) : null;\n}\n\nexport type BookingLinkProps = Omit<ComponentProps<typeof Link>, 'prefetch' | 'href'> & {\n href: string;\n /** The site's config; or the nearest `BookingProvider`'s. */\n config?: Readonly<BookingConfig>;\n};\n\n/**\n * `next/link` for a booking destination. Anything that is not the booking\n * page — a landing page, the price list — passes straight through, so callers\n * can hand it whatever their CMS field holds.\n */\nexport function BookingLink({\n href,\n children,\n onClick,\n target,\n config,\n ...rest\n}: BookingLinkProps) {\n const paths = useBookingKit({ config }).kit.config;\n const booking = bookingTarget(href, paths) !== null;\n return (\n <Link\n href={href}\n target={target}\n {...bookingPrefetch(href, paths)}\n onClick={(event) => {\n onClick?.(event);\n // Unless we are already there: no navigation is coming to end it.\n if (\n booking &&\n !target &&\n isPlainTap(event) &&\n window.location.pathname !== paths.paths.booking\n ) {\n window.dispatchEvent(new Event(BOOKING_NAVIGATION_EVENT));\n }\n }}\n {...rest}\n >\n {children}\n </Link>\n );\n}\n"],"mappings":";;;;;;;;;;AA8BA,SAAgB,cAAc,MAAc,QAA6C;CACvF,MAAM,OAAO,OAAO,MAAM;CAC1B,IAAI,SAAS,MAAM,OAAO;CAC1B,OAAO,KAAK,WAAW,GAAG,KAAK,EAAE,IAAI,SAAS;AAChD;;AAGA,SAAgB,gBACd,MACA,QAC2D;CAC3D,MAAM,SAAS,cAAc,MAAM,MAAM;CACzC,IAAI,WAAW,QAAQ,OAAO,EAAE,UAAU,KAAK;CAC/C,IAAI,WAAW,QAAQ,OAAO,EAAE,yBAAyB,KAAK;CAC9D,OAAO,CAAC;AACV;;AAGA,MAAa,2BAA2B;;AAExC,MAAa,mBAAmB;;AAEhC,MAAa,qBAAqB;;AAGlC,SAAS,WAAW,OAA+C;CACjE,OACE,CAAC,MAAM,oBACP,MAAM,WAAW,KACjB,CAAC,MAAM,WACP,CAAC,MAAM,WACP,CAAC,MAAM,YACP,CAAC,MAAM;AAEX;;;;;;;;;;;AAYA,SAAgB,mBAAmB,OAAyB;CAC1D,MAAM,EAAE,KAAK,eAAe,cAAc,KAAK;CAC/C,MAAM,WAAW,YAAY;CAC7B,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,SAAS,OAAiB,CAAC,CAAC;CAElC,gBAAgB;EACd,MAAM,cAAc;GAClB,KAAK,MAAM,SAAS,OAAO,SAAS,OAAO,aAAa,KAAK;GAC7D,OAAO,UAAU,CAAC;EACpB;EACA,MAAM,cAAc;GAClB,MAAM;GACN,OAAO,UAAU,CACf,OAAO,iBAAiB,WAAW,IAAI,GAAA,GAAmB,GAC1D,OAAO,iBAAiB,WAAW,KAAK,GAAG,kBAAkB,CAC/D;EACF;EACA,MAAM,aAAa;GACjB,MAAM;GACN,WAAW,KAAK;EAClB;EACA,OAAO,iBAAiB,0BAA0B,KAAK;EACvD,OAAO,iBAAiB,YAAY,IAAI;EACxC,aAAa;GACX,MAAM;GACN,OAAO,oBAAoB,0BAA0B,KAAK;GAC1D,OAAO,oBAAoB,YAAY,IAAI;EAC7C;CACF,GAAG,CAAC,CAAC;CAIL,gBAAgB;EACd,KAAK,MAAM,SAAS,OAAO,SAAS,OAAO,aAAa,KAAK;EAC7D,OAAO,UAAU,CAAC;EAClB,WAAW,KAAK;CAClB,GAAG,CAAC,QAAQ,CAAC;CAEb,OAAO,UACL,oBAAC,iBAAD;EAAiB,QAAQ,aAAa,IAAI,MAAM;EAAG,YAAY,WAAW;CAAW,CAAA,IACnF;AACN;;;;;;AAaA,SAAgB,YAAY,EAC1B,MACA,UACA,SACA,QACA,QACA,GAAG,QACgB;CACnB,MAAM,QAAQ,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI;CAC5C,MAAM,UAAU,cAAc,MAAM,KAAK,MAAM;CAC/C,OACE,oBAAC,MAAD;EACQ;EACE;EACR,GAAI,gBAAgB,MAAM,KAAK;EAC/B,UAAU,UAAU;GAClB,UAAU,KAAK;GAEf,IACE,WACA,CAAC,UACD,WAAW,KAAK,KAChB,OAAO,SAAS,aAAa,MAAM,MAAM,SAEzC,OAAO,cAAc,IAAI,MAAM,wBAAwB,CAAC;EAE5D;EACA,GAAI;EAEH;CACG,CAAA;AAEV"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { WizardState, WizardStep } from "../core/machine.mjs";
|
|
2
|
+
import { PortalActions } from "./actions.mjs";
|
|
3
|
+
import { BookingOverrides } from "./Provider.mjs";
|
|
4
|
+
import { BookingSeed, useBooking } from "./useBooking.mjs";
|
|
5
|
+
//#region src/react/BookingWizard.d.ts
|
|
6
|
+
export interface BookingWizardProps extends BookingOverrides {
|
|
7
|
+
/** What the page prefetched (the `/next` loader's seed). */
|
|
8
|
+
seed: BookingSeed;
|
|
9
|
+
/** This request's phone and address, over `config.contact` (the loader's `contact`). */
|
|
10
|
+
contact?: {
|
|
11
|
+
phone?: string | null;
|
|
12
|
+
address?: string | null;
|
|
13
|
+
};
|
|
14
|
+
/** The parent the page found a portal session for; `null` for a guest. */
|
|
15
|
+
guardian?: BookingGuardianProp;
|
|
16
|
+
/**
|
|
17
|
+
* The app's server actions for the login sheet (Decision 8). Without them —
|
|
18
|
+
* or with `config.portal.enabled` off — no login is offered.
|
|
19
|
+
*/
|
|
20
|
+
actions?: Pick<PortalActions, 'startLogin' | 'startVipps'>;
|
|
21
|
+
/** How many days the window covers. Default `config.window.rangeDays`. */
|
|
22
|
+
rangeDays?: number;
|
|
23
|
+
/**
|
|
24
|
+
* The site's origin (`https://…`), so the manage links inside the calendar
|
|
25
|
+
* file are absolute. Absent, they stay root-relative.
|
|
26
|
+
*/
|
|
27
|
+
siteUrl?: string;
|
|
28
|
+
/** A monitoring tap: the step the visitor is on, and a submission's outcome. */
|
|
29
|
+
onEvent?: (event: BookingWizardEvent) => void;
|
|
30
|
+
}
|
|
31
|
+
type BookingGuardianProp = Parameters<typeof useBooking>[0]['guardian'];
|
|
32
|
+
export type BookingWizardEvent = {
|
|
33
|
+
type: 'step';
|
|
34
|
+
step: WizardStep | 'confirmed';
|
|
35
|
+
} | {
|
|
36
|
+
type: 'submit_ok';
|
|
37
|
+
bookings: number;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'submit_error';
|
|
40
|
+
code: NonNullable<WizardState['error']>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The wizard, behind its own Suspense boundary: `useSearchParams` throws to
|
|
44
|
+
* the nearest one during a static prerender, and without ours that is the
|
|
45
|
+
* root layout's — the whole page would ship empty until hydration.
|
|
46
|
+
*/
|
|
47
|
+
export declare function BookingWizard(props: BookingWizardProps): import("react").JSX.Element;
|
|
48
|
+
//#endregion
|
|
49
|
+
//# sourceMappingURL=BookingWizard.d.mts.map
|