@voyantjs/availability-react 0.106.0 → 0.108.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 (81) hide show
  1. package/README.md +161 -1
  2. package/dist/admin/availability-index-host.d.ts +12 -0
  3. package/dist/admin/availability-index-host.d.ts.map +1 -0
  4. package/dist/admin/availability-index-host.js +125 -0
  5. package/dist/admin/availability-page-data.d.ts +9 -0
  6. package/dist/admin/availability-page-data.d.ts.map +1 -0
  7. package/dist/admin/availability-page-data.js +25 -0
  8. package/dist/admin/index.d.ts +72 -0
  9. package/dist/admin/index.d.ts.map +1 -0
  10. package/dist/admin/index.js +132 -0
  11. package/dist/admin/option-resource-templates-panel.d.ts +22 -0
  12. package/dist/admin/option-resource-templates-panel.d.ts.map +1 -0
  13. package/dist/admin/option-resource-templates-panel.js +251 -0
  14. package/dist/admin/pages/availability-rule-detail-page.d.ts +9 -0
  15. package/dist/admin/pages/availability-rule-detail-page.d.ts.map +1 -0
  16. package/dist/admin/pages/availability-rule-detail-page.js +11 -0
  17. package/dist/admin/pages/availability-slot-detail-page.d.ts +9 -0
  18. package/dist/admin/pages/availability-slot-detail-page.d.ts.map +1 -0
  19. package/dist/admin/pages/availability-slot-detail-page.js +11 -0
  20. package/dist/admin/pages/availability-start-time-detail-page.d.ts +9 -0
  21. package/dist/admin/pages/availability-start-time-detail-page.d.ts.map +1 -0
  22. package/dist/admin/pages/availability-start-time-detail-page.js +11 -0
  23. package/dist/admin/rule-detail-host.d.ts +14 -0
  24. package/dist/admin/rule-detail-host.d.ts.map +1 -0
  25. package/dist/admin/rule-detail-host.js +27 -0
  26. package/dist/admin/slot-detail-host.d.ts +29 -0
  27. package/dist/admin/slot-detail-host.d.ts.map +1 -0
  28. package/dist/admin/slot-detail-host.js +110 -0
  29. package/dist/admin/start-time-detail-host.d.ts +15 -0
  30. package/dist/admin/start-time-detail-host.d.ts.map +1 -0
  31. package/dist/admin/start-time-detail-host.js +37 -0
  32. package/dist/components/availability-columns.d.ts +42 -0
  33. package/dist/components/availability-columns.d.ts.map +1 -0
  34. package/dist/components/availability-columns.js +182 -0
  35. package/dist/components/availability-dialogs.d.ts +236 -0
  36. package/dist/components/availability-dialogs.d.ts.map +1 -0
  37. package/dist/components/availability-dialogs.js +369 -0
  38. package/dist/components/availability-overview.d.ts +54 -0
  39. package/dist/components/availability-overview.d.ts.map +1 -0
  40. package/dist/components/availability-overview.js +50 -0
  41. package/dist/components/availability-page.d.ts +32 -0
  42. package/dist/components/availability-page.d.ts.map +1 -0
  43. package/dist/components/availability-page.js +128 -0
  44. package/dist/components/availability-rule-detail-page.d.ts +251 -0
  45. package/dist/components/availability-rule-detail-page.d.ts.map +1 -0
  46. package/dist/components/availability-rule-detail-page.js +74 -0
  47. package/dist/components/availability-section-header.d.ts +8 -0
  48. package/dist/components/availability-section-header.d.ts.map +1 -0
  49. package/dist/components/availability-section-header.js +7 -0
  50. package/dist/components/availability-skeletons.d.ts +6 -0
  51. package/dist/components/availability-skeletons.d.ts.map +1 -0
  52. package/dist/components/availability-skeletons.js +34 -0
  53. package/dist/components/availability-slot-detail-page.d.ts +974 -0
  54. package/dist/components/availability-slot-detail-page.d.ts.map +1 -0
  55. package/dist/components/availability-slot-detail-page.js +383 -0
  56. package/dist/components/availability-start-time-detail-page.d.ts +246 -0
  57. package/dist/components/availability-start-time-detail-page.d.ts.map +1 -0
  58. package/dist/components/availability-start-time-detail-page.js +83 -0
  59. package/dist/components/availability-tabs.d.ts +152 -0
  60. package/dist/components/availability-tabs.d.ts.map +1 -0
  61. package/dist/components/availability-tabs.js +192 -0
  62. package/dist/components/slot-status-tone.d.ts +15 -0
  63. package/dist/components/slot-status-tone.d.ts.map +1 -0
  64. package/dist/components/slot-status-tone.js +18 -0
  65. package/dist/form-resolver.d.ts +4 -0
  66. package/dist/form-resolver.d.ts.map +1 -0
  67. package/dist/form-resolver.js +40 -0
  68. package/dist/i18n/index.d.ts +2 -0
  69. package/dist/i18n/index.d.ts.map +1 -0
  70. package/dist/i18n/index.js +1 -0
  71. package/dist/i18n/provider.d.ts +2003 -0
  72. package/dist/i18n/provider.d.ts.map +1 -0
  73. package/dist/i18n/provider.js +102 -0
  74. package/dist/ui.d.ts +13 -0
  75. package/dist/ui.d.ts.map +1 -0
  76. package/dist/ui.js +12 -0
  77. package/dist/utils.d.ts +1 -0
  78. package/dist/utils.d.ts.map +1 -1
  79. package/dist/utils.js +3 -0
  80. package/package.json +92 -9
  81. package/src/styles.css +11 -0
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # @voyantjs/availability-react
2
2
 
3
- React runtime package for Voyant availability. Provides the shared availability provider, typed fetch client, query keys, constants, and TanStack Query hooks that power availability-focused frontend experiences.
3
+ The availability client tier: headless data hooks/clients plus the styled UI
4
+ primitives and page-level compositions (formerly `@voyantjs/availability-ui`).
5
+
6
+ Headless consumers (storefronts, portals) import from the root, `./hooks`,
7
+ `./client`, or `./query-keys` — these pull no styling peers. Styled surfaces
8
+ live under `./ui`, `./components/*`, `./admin`, `./i18n`, `./utils`, and
9
+ `./styles.css`, whose heavier peers (`@voyantjs/ui`, `@voyantjs/admin`,
10
+ `@tanstack/react-table`, `sonner`, `lucide-react`) are optional and only
11
+ needed when you import those subpaths.
4
12
 
5
13
  ## Install
6
14
 
@@ -27,6 +35,158 @@ function SlotsList() {
27
35
  }
28
36
  ```
29
37
 
38
+ ## UI components
39
+
40
+ Reusable availability UI primitives and page compositions for Voyant
41
+ operator/admin apps.
42
+
43
+ ### Exports
44
+
45
+ | Entry | Description |
46
+ | --- | --- |
47
+ | `./ui` | Barrel re-exports |
48
+ | `./i18n` | Availability UI message provider, defaults, and helpers |
49
+ | `./components/*` | Availability UI components |
50
+ | `./utils` | Small formatting helpers |
51
+
52
+ ### Surface
53
+
54
+ The package exports reusable pieces that keep app-owned routing and the
55
+ availability batch mutations injected through props:
56
+
57
+ - `AvailabilityPage`
58
+ - `AvailabilityRuleDetailPage`, `AvailabilitySlotDetailPage`,
59
+ `AvailabilityStartTimeDetailPage`
60
+ - `AvailabilityOverview`
61
+ - `AvailabilitySlotsTab`, `AvailabilityRulesTab`, `AvailabilityStartTimesTab`
62
+ - `AvailabilityCloseoutsTab`, `AvailabilityPickupPointsTab`
63
+ - `AvailabilityRuleDialog`, `AvailabilityStartTimeDialog`, `AvailabilitySlotDialog`
64
+ - `AvailabilityCloseoutDialog`, `AvailabilityPickupPointDialog`
65
+ - `AvailabilityPageSkeleton`, `AvailabilityBodySkeleton`, detail skeletons
66
+ - `availability*Columns` table column builders
67
+ - `AvailabilitySectionHeader`
68
+ - `AvailabilityUiMessagesProvider` and i18n helpers from `./i18n`
69
+ - `formatLocalizedSelectionLabel`
70
+
71
+ ### Usage
72
+
73
+ `AvailabilityPage` owns the common operator availability shell, data hooks,
74
+ filters, overview metrics, table tabs, calendar tab, and rule/slot/start-time
75
+ dialogs. Route navigation and batch mutations stay app-specific:
76
+
77
+ ```tsx
78
+ import { AvailabilityPage } from "@voyantjs/availability-react/ui"
79
+
80
+ <AvailabilityPage
81
+ onSlotOpen={(id) => navigate({ to: "/availability/$id", params: { id } })}
82
+ onRuleOpen={(id) => navigate({ to: "/availability/rules/$id", params: { id } })}
83
+ onStartTimeOpen={(id) =>
84
+ navigate({ to: "/availability/start-times/$id", params: { id } })
85
+ }
86
+ onProductOpen={(id) => navigate({ to: "/products/$id", params: { id } })}
87
+ onBulkUpdate={handleAvailabilityBulkUpdate}
88
+ onBulkDelete={handleAvailabilityBulkDelete}
89
+ />
90
+ ```
91
+
92
+ Closeout and pickup-point mutations are not owned by the headless tier yet.
93
+ Pass `onCloseoutSubmit` and `onPickupPointSubmit` to use the package dialogs, or
94
+ use the `slots.dialogs` escape hatch to render app-owned dialogs.
95
+
96
+ Detail pages expose query/loader helpers that accept the app's API client:
97
+
98
+ ```tsx
99
+ import { defaultFetcher } from "@voyantjs/availability-react"
100
+ import {
101
+ AvailabilitySlotDetailPage,
102
+ loadAvailabilitySlotDetailPage,
103
+ } from "@voyantjs/availability-react/ui"
104
+
105
+ const client = { baseUrl: getApiUrl(), fetcher: defaultFetcher }
106
+
107
+ export const Route = createFileRoute("/_workspace/availability/$id")({
108
+ loader: ({ context, params }) =>
109
+ loadAvailabilitySlotDetailPage(context.queryClient, client, params.id),
110
+ component: () => {
111
+ const { id } = Route.useParams()
112
+ return (
113
+ <AvailabilitySlotDetailPage
114
+ id={id}
115
+ onBack={() => navigate({ to: "/availability" })}
116
+ onOpenProduct={(productId) =>
117
+ navigate({ to: "/products/$id", params: { id: productId } })
118
+ }
119
+ onOpenStartTime={(startTimeId) =>
120
+ navigate({ to: "/availability/start-times/$id", params: { id: startTimeId } })
121
+ }
122
+ />
123
+ )
124
+ },
125
+ })
126
+ ```
127
+
128
+ Wrap consumers in `AvailabilityUiMessagesProvider` for package-level copy and
129
+ locale-aware formatting. Without a provider the package falls back to English.
130
+
131
+ ```tsx
132
+ import { AvailabilityUiMessagesProvider } from "@voyantjs/availability-react/i18n"
133
+
134
+ <AvailabilityUiMessagesProvider locale={resolvedLocale}>
135
+ <AvailabilityPage onBulkUpdate={handleBulkUpdate} onBulkDelete={handleBulkDelete} />
136
+ </AvailabilityUiMessagesProvider>
137
+ ```
138
+
139
+ Leaf components remain available for custom page shells:
140
+
141
+ ```tsx
142
+ import { AvailabilitySectionHeader } from "@voyantjs/availability-react/ui"
143
+
144
+ function SlotsHeader() {
145
+ return (
146
+ <AvailabilitySectionHeader
147
+ title="Slots"
148
+ description="Manage generated capacity."
149
+ actionLabel="Create slot"
150
+ onAction={() => setOpen(true)}
151
+ />
152
+ )
153
+ }
154
+ ```
155
+
156
+ Table column builders are available for apps that use `@voyantjs/ui`'s
157
+ `DataTable` and keep routing behavior app-owned:
158
+
159
+ ```tsx
160
+ import { availabilitySlotColumns } from "@voyantjs/availability-react/ui"
161
+
162
+ <DataTable
163
+ columns={availabilitySlotColumns(products, openSlotRoute, messages.availability)}
164
+ data={slots}
165
+ />
166
+ ```
167
+
168
+ Dialogs expose the reusable form UI and validation while the app decides how
169
+ to persist the payload:
170
+
171
+ ```tsx
172
+ import { AvailabilitySlotDialog } from "@voyantjs/availability-react/ui"
173
+
174
+ <AvailabilitySlotDialog
175
+ messages={messages.availability}
176
+ open={open}
177
+ onOpenChange={setOpen}
178
+ products={products}
179
+ rules={rules}
180
+ startTimes={startTimes}
181
+ onSubmit={(payload, context) =>
182
+ context.isEditing
183
+ ? api.patch(`/v1/availability/slots/${context.id}`, payload)
184
+ : api.post("/v1/availability/slots", payload)
185
+ }
186
+ onSuccess={refresh}
187
+ />
188
+ ```
189
+
30
190
  ## License
31
191
 
32
192
  Apache-2.0
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Packaged admin host for the availability index page (packaged-admin RFC
3
+ * Phase 3). Zero-prop: list/filter state stays component-local (no URL
4
+ * search contract), opening a slot resolves through the
5
+ * `availabilitySlot.detail` semantic destination, and the bulk
6
+ * update/delete handlers run through the typed batch mutation pairs from
7
+ * `@voyantjs/availability-react` (the `batch-update`/`batch-delete`
8
+ * endpoints) instead of an app RPC client. Slot create/edit submits through
9
+ * the package default (`useAvailabilitySlotMutation`).
10
+ */
11
+ export declare function AvailabilityIndexHost(): import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=availability-index-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability-index-host.d.ts","sourceRoot":"","sources":["../../src/admin/availability-index-host.tsx"],"names":[],"mappings":"AA0BA;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,4CA2KpC"}
@@ -0,0 +1,125 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useQueryClient } from "@tanstack/react-query";
4
+ import { formatMessage, useAdminNavigate, useOperatorAdminMessages } from "@voyantjs/admin";
5
+ import { useState } from "react";
6
+ import { toast } from "sonner";
7
+ import { AvailabilityPage, } from "../components/availability-page.js";
8
+ import { availabilityQueryKeys, useAvailabilityCloseoutBatchMutation, useAvailabilityPickupPointBatchMutation, useAvailabilityRuleBatchMutation, useAvailabilitySlotBatchMutation, useAvailabilityStartTimeBatchMutation, } from "../index.js";
9
+ import { formatLocalizedSelectionLabel } from "../utils.js";
10
+ /**
11
+ * Packaged admin host for the availability index page (packaged-admin RFC
12
+ * Phase 3). Zero-prop: list/filter state stays component-local (no URL
13
+ * search contract), opening a slot resolves through the
14
+ * `availabilitySlot.detail` semantic destination, and the bulk
15
+ * update/delete handlers run through the typed batch mutation pairs from
16
+ * `@voyantjs/availability-react` (the `batch-update`/`batch-delete`
17
+ * endpoints) instead of an app RPC client. Slot create/edit submits through
18
+ * the package default (`useAvailabilitySlotMutation`).
19
+ */
20
+ export function AvailabilityIndexHost() {
21
+ const messages = useOperatorAdminMessages();
22
+ const navigateTo = useAdminNavigate();
23
+ const queryClient = useQueryClient();
24
+ const [bulkActionTarget, setBulkActionTarget] = useState(null);
25
+ const ruleBatch = useAvailabilityRuleBatchMutation();
26
+ const startTimeBatch = useAvailabilityStartTimeBatchMutation();
27
+ const slotBatch = useAvailabilitySlotBatchMutation();
28
+ const closeoutBatch = useAvailabilityCloseoutBatchMutation();
29
+ const pickupPointBatch = useAvailabilityPickupPointBatchMutation();
30
+ const refreshAvailability = () => queryClient.invalidateQueries({ queryKey: availabilityQueryKeys.all });
31
+ // The tabs identify their batch endpoints by REST path; resolve each to
32
+ // the matching typed batch mutation pair. The page currently only emits
33
+ // the slots endpoint, but every availability list entity is covered so
34
+ // additional tabs slot in without touching the host.
35
+ const runBatchUpdate = (endpoint, ids, payload) => {
36
+ switch (endpoint) {
37
+ case "/v1/availability/rules":
38
+ return ruleBatch.batchUpdate.mutateAsync({
39
+ ids,
40
+ patch: payload,
41
+ });
42
+ case "/v1/availability/start-times":
43
+ return startTimeBatch.batchUpdate.mutateAsync({
44
+ ids,
45
+ patch: payload,
46
+ });
47
+ case "/v1/availability/closeouts":
48
+ return closeoutBatch.batchUpdate.mutateAsync({
49
+ ids,
50
+ patch: payload,
51
+ });
52
+ case "/v1/availability/pickup-points":
53
+ return pickupPointBatch.batchUpdate.mutateAsync({
54
+ ids,
55
+ patch: payload,
56
+ });
57
+ default:
58
+ return slotBatch.batchUpdate.mutateAsync({
59
+ ids,
60
+ patch: payload,
61
+ });
62
+ }
63
+ };
64
+ const runBatchDelete = (endpoint, ids) => {
65
+ switch (endpoint) {
66
+ case "/v1/availability/rules":
67
+ return ruleBatch.batchDelete.mutateAsync({ ids });
68
+ case "/v1/availability/start-times":
69
+ return startTimeBatch.batchDelete.mutateAsync({ ids });
70
+ case "/v1/availability/closeouts":
71
+ return closeoutBatch.batchDelete.mutateAsync({ ids });
72
+ case "/v1/availability/pickup-points":
73
+ return pickupPointBatch.batchDelete.mutateAsync({ ids });
74
+ default:
75
+ return slotBatch.batchDelete.mutateAsync({ ids });
76
+ }
77
+ };
78
+ const slotsNounSingular = messages.availability.tabs.slots.title;
79
+ const slotsNounPlural = messages.availability.tabs.slots.title;
80
+ const handleBulkUpdate = async ({ ids, endpoint, target, nounSingular, nounPlural, payload, successVerb, clearSelection, }) => {
81
+ if (ids.length === 0)
82
+ return;
83
+ setBulkActionTarget(target);
84
+ const result = await runBatchUpdate(endpoint, ids, payload);
85
+ await refreshAvailability();
86
+ clearSelection();
87
+ setBulkActionTarget(null);
88
+ const succeededSelection = formatLocalizedSelectionLabel(result.succeeded, nounSingular ?? slotsNounSingular, nounPlural ?? slotsNounPlural);
89
+ const totalSelection = formatLocalizedSelectionLabel(result.total, nounSingular ?? slotsNounSingular, nounPlural ?? slotsNounPlural);
90
+ if (result.failed.length === 0) {
91
+ toast.success(formatMessage(messages.availability.toasts.bulkUpdated, {
92
+ verb: successVerb,
93
+ selection: succeededSelection,
94
+ }));
95
+ return;
96
+ }
97
+ toast.error(formatMessage(messages.availability.toasts.bulkUpdatedPartial, {
98
+ verb: successVerb,
99
+ succeeded: result.succeeded,
100
+ selection: totalSelection,
101
+ }));
102
+ };
103
+ const handleBulkDelete = async ({ ids, endpoint, target, nounSingular, nounPlural, clearSelection, }) => {
104
+ if (ids.length === 0)
105
+ return;
106
+ setBulkActionTarget(target);
107
+ const result = await runBatchDelete(endpoint, ids);
108
+ await refreshAvailability();
109
+ clearSelection();
110
+ setBulkActionTarget(null);
111
+ const succeededSelection = formatLocalizedSelectionLabel(result.succeeded, nounSingular ?? slotsNounSingular, nounPlural ?? slotsNounPlural);
112
+ const totalSelection = formatLocalizedSelectionLabel(result.total, nounSingular ?? slotsNounSingular, nounPlural ?? slotsNounPlural);
113
+ if (result.failed.length === 0) {
114
+ toast.success(formatMessage(messages.availability.toasts.bulkDeleted, {
115
+ selection: succeededSelection,
116
+ }));
117
+ return;
118
+ }
119
+ toast.error(formatMessage(messages.availability.toasts.bulkDeletedPartial, {
120
+ succeeded: result.succeeded,
121
+ selection: totalSelection,
122
+ }));
123
+ };
124
+ return (_jsx(AvailabilityPage, { bulkActionTarget: bulkActionTarget, onBulkUpdate: handleBulkUpdate, onBulkDelete: handleBulkDelete, onSlotOpen: (slotId) => navigateTo("availabilitySlot.detail", { slotId }) }));
125
+ }
@@ -0,0 +1,9 @@
1
+ import type { QueryClient } from "@tanstack/react-query";
2
+ import { type VoyantAvailabilityContextValue } from "../index.js";
3
+ /**
4
+ * Index page loader: await only what the slots tab + the products picker
5
+ * (top of page) need for first paint; the rules/start-times dimensions that
6
+ * back the slot create/edit dialog prefetch in the background.
7
+ */
8
+ export declare function ensureAvailabilityPageData(queryClient: QueryClient, client: VoyantAvailabilityContextValue): Promise<void>;
9
+ //# sourceMappingURL=availability-page-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability-page-data.d.ts","sourceRoot":"","sources":["../../src/admin/availability-page-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAKL,KAAK,8BAA8B,EACpC,MAAM,aAAa,CAAA;AAcpB;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,8BAA8B,GACrC,OAAO,CAAC,IAAI,CAAC,CAYf"}
@@ -0,0 +1,25 @@
1
+ import { getProductsQueryOptions, getRulesQueryOptions, getSlotsQueryOptions, getStartTimesQueryOptions, } from "../index.js";
2
+ /**
3
+ * Canonical first-page list filters for the availability index page.
4
+ * `AvailabilityPage` hard-codes the same filters in its hooks, so the
5
+ * loader-seeded cache entries line up with the page's query keys.
6
+ */
7
+ const availabilityPageQueryFilters = {
8
+ products: { limit: 25, offset: 0 },
9
+ slots: { limit: 25, offset: 0 },
10
+ rules: { limit: 25, offset: 0 },
11
+ startTimes: { limit: 25, offset: 0 },
12
+ };
13
+ /**
14
+ * Index page loader: await only what the slots tab + the products picker
15
+ * (top of page) need for first paint; the rules/start-times dimensions that
16
+ * back the slot create/edit dialog prefetch in the background.
17
+ */
18
+ export async function ensureAvailabilityPageData(queryClient, client) {
19
+ await Promise.all([
20
+ queryClient.ensureQueryData(getSlotsQueryOptions(client, availabilityPageQueryFilters.slots)),
21
+ queryClient.ensureQueryData(getProductsQueryOptions(client, availabilityPageQueryFilters.products)),
22
+ ]);
23
+ void queryClient.prefetchQuery(getRulesQueryOptions(client, availabilityPageQueryFilters.rules));
24
+ void queryClient.prefetchQuery(getStartTimesQueryOptions(client, availabilityPageQueryFilters.startTimes));
25
+ }
@@ -0,0 +1,72 @@
1
+ import { type AdminExtension } from "@voyantjs/admin";
2
+ /**
3
+ * Semantic destinations the availability admin surfaces navigate to
4
+ * (packaged-admin RFC §4.7). Keys shared with other domains
5
+ * (`availabilitySlot.detail`, `booking.detail`, `product.detail`) come from
6
+ * the bookings-ui augmentation bound above; declared here are the
7
+ * availability-owned targets the packaged pages and breadcrumbs resolve
8
+ * through `useAdminHref`/`useAdminNavigate`.
9
+ */
10
+ declare module "@voyantjs/admin" {
11
+ interface AdminDestinations {
12
+ /** The availability landing page (slots list + calendar). */
13
+ "availabilitySlot.list": Record<string, never>;
14
+ /** An availability start time's detail page. */
15
+ "availabilityStartTime.detail": {
16
+ startTimeId: string;
17
+ };
18
+ }
19
+ }
20
+ export { AvailabilityIndexHost } from "./availability-index-host.js";
21
+ export { ensureAvailabilityPageData } from "./availability-page-data.js";
22
+ export { OptionResourceTemplatesPanel, type OptionResourceTemplatesPanelProps, } from "./option-resource-templates-panel.js";
23
+ export { AvailabilityRuleDetailHost, type AvailabilityRuleDetailHostProps, } from "./rule-detail-host.js";
24
+ export { AvailabilitySlotDetailHost, type AvailabilitySlotDetailHostProps, } from "./slot-detail-host.js";
25
+ export { AvailabilityStartTimeDetailHost, type AvailabilityStartTimeDetailHostProps, } from "./start-time-detail-host.js";
26
+ export interface CreateAvailabilityAdminExtensionOptions {
27
+ /** Mount path of the availability pages inside the admin workspace. Default `/availability`. */
28
+ basePath?: string;
29
+ /** Localized page titles. Defaults are the English operator nav labels. */
30
+ labels?: {
31
+ availability?: string;
32
+ };
33
+ }
34
+ /**
35
+ * The availability admin contribution (packaged-admin RFC Phase 3,
36
+ * `@voyantjs/<domain>-ui/admin` convention).
37
+ *
38
+ * NAVIGATION: deliberately none. The Availability nav item is part of the
39
+ * BASE operator navigation — see `createOperatorAdminNavigation` in
40
+ * `@voyantjs/admin` — so contributing a nav entry here would duplicate it.
41
+ * If the base nav ever drops the availability item, this extension is where
42
+ * the entry moves.
43
+ *
44
+ * ROUTES: contributions carry the FULL route implementation (packaged-admin
45
+ * RFC §4.2/§4.8) — lazy `page` module loaders, data loaders fed by the
46
+ * host-supplied {@link AdminRouteLoaderContext} (QueryClient + runtime +
47
+ * params), per-route SSR mode, and pending skeletons. Hosts bind them into
48
+ * their code-assembled admin route tree; no per-route host files needed.
49
+ * The pages stay code-split because each contribution's `page` dynamically
50
+ * imports the specific host/page module — never the admin barrel — so the
51
+ * heavy page chunks load on navigation, not with workspace chrome.
52
+ * {@link AvailabilityIndexHost} (the slots list + calendar landing page,
53
+ * with bulk update/delete running through the typed batch mutation hooks in
54
+ * `@voyantjs/availability-react`) mounts as a zero-prop page; the detail
55
+ * hosts {@link AvailabilitySlotDetailHost},
56
+ * {@link AvailabilityRuleDetailHost} and
57
+ * {@link AvailabilityStartTimeDetailHost} read their record id from
58
+ * `AdminRoutePageProps` via the default-exported wrappers in `./pages/`.
59
+ * The index host's SSR loader binding is no longer app-side:
60
+ * {@link ensureAvailabilityPageData} runs in the contribution's own loader
61
+ * against the host runtime's cookie-forwarding fetcher. The pages keep
62
+ * their filter state component-local, so there are no URL search contracts,
63
+ * and every cross-route link resolves through the semantic destinations
64
+ * declared above.
65
+ *
66
+ * WIDGETS: none. {@link OptionResourceTemplatesPanel} (the per-option
67
+ * resource templates editor the product editor embeds) ships from this
68
+ * entry as a directly importable component — the products admin host owns
69
+ * where it mounts.
70
+ */
71
+ export declare function createAvailabilityAdminExtension(options?: CreateAvailabilityAdminExtensionOptions): AdminExtension;
72
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAKpB,MAAM,iBAAiB,CAAA;AAoBxB;;;;;;;GAOG;AACH,OAAO,QAAQ,iBAAiB,CAAC;IAC/B,UAAU,iBAAiB;QACzB,6DAA6D;QAC7D,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAC9C,gDAAgD;QAChD,8BAA8B,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAA;KACxD;CACF;AAKD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAA;AACxE,OAAO,EACL,4BAA4B,EAC5B,KAAK,iCAAiC,GACvC,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EACL,0BAA0B,EAC1B,KAAK,+BAA+B,GACrC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,0BAA0B,EAC1B,KAAK,+BAA+B,GACrC,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,+BAA+B,EAC/B,KAAK,oCAAoC,GAC1C,MAAM,6BAA6B,CAAA;AAEpC,MAAM,WAAW,uCAAuC;IACtD,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2EAA2E;IAC3E,MAAM,CAAC,EAAE;QACP,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,GAAE,uCAA4C,GACpD,cAAc,CA6EhB"}
@@ -0,0 +1,132 @@
1
+ import { adminRoutePageModule, defineAdminExtension, } from "@voyantjs/admin";
2
+ import { AvailabilityPageSkeleton, AvailabilityRuleDetailSkeleton, AvailabilitySlotDetailSkeleton, AvailabilityStartTimeDetailSkeleton, } from "../components/availability-skeletons.js";
3
+ import { defaultFetcher } from "../index.js";
4
+ import { ensureAvailabilityPageData } from "./availability-page-data.js";
5
+ // Packaged admin hosts (packaged-admin RFC Phase 3): the operator-grade
6
+ // availability pages bound to their data wiring + semantic-destination
7
+ // navigation. Host route files only bind route params onto these.
8
+ export { AvailabilityIndexHost } from "./availability-index-host.js";
9
+ export { ensureAvailabilityPageData } from "./availability-page-data.js";
10
+ export { OptionResourceTemplatesPanel, } from "./option-resource-templates-panel.js";
11
+ export { AvailabilityRuleDetailHost, } from "./rule-detail-host.js";
12
+ export { AvailabilitySlotDetailHost, } from "./slot-detail-host.js";
13
+ export { AvailabilityStartTimeDetailHost, } from "./start-time-detail-host.js";
14
+ /**
15
+ * The availability admin contribution (packaged-admin RFC Phase 3,
16
+ * `@voyantjs/<domain>-ui/admin` convention).
17
+ *
18
+ * NAVIGATION: deliberately none. The Availability nav item is part of the
19
+ * BASE operator navigation — see `createOperatorAdminNavigation` in
20
+ * `@voyantjs/admin` — so contributing a nav entry here would duplicate it.
21
+ * If the base nav ever drops the availability item, this extension is where
22
+ * the entry moves.
23
+ *
24
+ * ROUTES: contributions carry the FULL route implementation (packaged-admin
25
+ * RFC §4.2/§4.8) — lazy `page` module loaders, data loaders fed by the
26
+ * host-supplied {@link AdminRouteLoaderContext} (QueryClient + runtime +
27
+ * params), per-route SSR mode, and pending skeletons. Hosts bind them into
28
+ * their code-assembled admin route tree; no per-route host files needed.
29
+ * The pages stay code-split because each contribution's `page` dynamically
30
+ * imports the specific host/page module — never the admin barrel — so the
31
+ * heavy page chunks load on navigation, not with workspace chrome.
32
+ * {@link AvailabilityIndexHost} (the slots list + calendar landing page,
33
+ * with bulk update/delete running through the typed batch mutation hooks in
34
+ * `@voyantjs/availability-react`) mounts as a zero-prop page; the detail
35
+ * hosts {@link AvailabilitySlotDetailHost},
36
+ * {@link AvailabilityRuleDetailHost} and
37
+ * {@link AvailabilityStartTimeDetailHost} read their record id from
38
+ * `AdminRoutePageProps` via the default-exported wrappers in `./pages/`.
39
+ * The index host's SSR loader binding is no longer app-side:
40
+ * {@link ensureAvailabilityPageData} runs in the contribution's own loader
41
+ * against the host runtime's cookie-forwarding fetcher. The pages keep
42
+ * their filter state component-local, so there are no URL search contracts,
43
+ * and every cross-route link resolves through the semantic destinations
44
+ * declared above.
45
+ *
46
+ * WIDGETS: none. {@link OptionResourceTemplatesPanel} (the per-option
47
+ * resource templates editor the product editor embeds) ships from this
48
+ * entry as a directly importable component — the products admin host owns
49
+ * where it mounts.
50
+ */
51
+ export function createAvailabilityAdminExtension(options = {}) {
52
+ const { basePath = "/availability", labels = {} } = options;
53
+ const { availability = "Availability" } = labels;
54
+ return defineAdminExtension({
55
+ id: "availability",
56
+ routes: [
57
+ {
58
+ id: "availability-index",
59
+ path: basePath,
60
+ title: availability,
61
+ ssr: "data-only",
62
+ page: () => import("./availability-index-host.js").then((module) => adminRoutePageModule(module.AvailabilityIndexHost)),
63
+ // Awaits only what the slots tab + the products picker need for
64
+ // first paint; the slot dialog's rules/start-times dimensions
65
+ // prefetch in the background.
66
+ loader: ({ queryClient, runtime }) => ensureAvailabilityPageData(queryClient, loaderClient(runtime)),
67
+ pendingComponent: AvailabilityPageSkeleton,
68
+ },
69
+ {
70
+ id: "availability-slot-detail",
71
+ path: `${basePath}/$id`,
72
+ title: availability,
73
+ page: () => import("./pages/availability-slot-detail-page.js"),
74
+ loader: async ({ queryClient, runtime, params }) => {
75
+ const id = params.id;
76
+ if (!id)
77
+ return;
78
+ // Dynamic import on purpose: the loader helper lives in the slot
79
+ // detail page module, and a static import here would pin that
80
+ // module into the host's workspace-chrome chunk, defeating the
81
+ // route's code-split. The loader and the page resolve the same
82
+ // chunk, fetched once.
83
+ const { loadAvailabilitySlotDetailPage } = await import("../components/availability-slot-detail-page.js");
84
+ return loadAvailabilitySlotDetailPage(queryClient, loaderClient(runtime), id);
85
+ },
86
+ pendingComponent: AvailabilitySlotDetailSkeleton,
87
+ },
88
+ {
89
+ id: "availability-rule-detail",
90
+ path: `${basePath}/rules/$id`,
91
+ title: availability,
92
+ page: () => import("./pages/availability-rule-detail-page.js"),
93
+ loader: async ({ queryClient, runtime, params }) => {
94
+ const id = params.id;
95
+ if (!id)
96
+ return;
97
+ // Dynamic import on purpose — see the slot detail loader above.
98
+ const { loadAvailabilityRuleDetailPage } = await import("../components/availability-rule-detail-page.js");
99
+ return loadAvailabilityRuleDetailPage(queryClient, loaderClient(runtime), id);
100
+ },
101
+ pendingComponent: AvailabilityRuleDetailSkeleton,
102
+ },
103
+ {
104
+ id: "availability-start-time-detail",
105
+ path: `${basePath}/start-times/$id`,
106
+ title: availability,
107
+ page: () => import("./pages/availability-start-time-detail-page.js"),
108
+ loader: async ({ queryClient, runtime, params }) => {
109
+ const id = params.id;
110
+ if (!id)
111
+ return;
112
+ // Dynamic import on purpose — see the slot detail loader above.
113
+ const { loadAvailabilityStartTimeDetailPage } = await import("../components/availability-start-time-detail-page.js");
114
+ return loadAvailabilityStartTimeDetailPage(queryClient, loaderClient(runtime), id);
115
+ },
116
+ pendingComponent: AvailabilityStartTimeDetailSkeleton,
117
+ },
118
+ ],
119
+ });
120
+ }
121
+ /**
122
+ * Bridge the host-supplied {@link AdminRouteRuntime} (optional fetcher) to
123
+ * the required-fetcher client contract the availability loaders take.
124
+ *
125
+ * Note: the operator's detail route files built this client with the
126
+ * package `defaultFetcher` (a plain `credentials: "include"` fetch); the
127
+ * contribution uses the host runtime's cookie-forwarding fetcher instead,
128
+ * so detail SSR prefetches authenticate — an intentional improvement.
129
+ */
130
+ function loaderClient(runtime) {
131
+ return { baseUrl: runtime.baseUrl, fetcher: runtime.fetcher ?? defaultFetcher };
132
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Per-option Resource templates editor. Templates drive the Allocation
3
+ * tab's "Generate resources" automation — without a template configured
4
+ * for an option, no rooms/seats/etc. are materialized when bookings hit
5
+ * a slot.
6
+ *
7
+ * Each template is identified by `(optionId, kind, refId)` — an option can
8
+ * hold several templates of the same kind, one per `option_unit` (e.g. a
9
+ * "room" template per room type), distinguished by `refId`. Common kinds:
10
+ * - `room` — accommodation rooms (default capacity 2)
11
+ * - `vehicle_seat` — coach/van seats (capacity 1)
12
+ * - `cabin` — cruise cabins
13
+ *
14
+ * The kind field is a free-form string on the server; we constrain the
15
+ * picker to the known set for UX, but operators can extend by typing.
16
+ */
17
+ export interface OptionResourceTemplatesPanelProps {
18
+ productId: string;
19
+ optionId: string;
20
+ }
21
+ export declare function OptionResourceTemplatesPanel({ productId, optionId, }: OptionResourceTemplatesPanelProps): import("react/jsx-runtime").JSX.Element;
22
+ //# sourceMappingURL=option-resource-templates-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"option-resource-templates-panel.d.ts","sourceRoot":"","sources":["../../src/admin/option-resource-templates-panel.tsx"],"names":[],"mappings":"AA+CA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAiBD,wBAAgB,4BAA4B,CAAC,EAC3C,SAAS,EACT,QAAQ,GACT,EAAE,iCAAiC,2CAqenC"}