@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.
- package/README.md +161 -1
- package/dist/admin/availability-index-host.d.ts +12 -0
- package/dist/admin/availability-index-host.d.ts.map +1 -0
- package/dist/admin/availability-index-host.js +125 -0
- package/dist/admin/availability-page-data.d.ts +9 -0
- package/dist/admin/availability-page-data.d.ts.map +1 -0
- package/dist/admin/availability-page-data.js +25 -0
- package/dist/admin/index.d.ts +72 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +132 -0
- package/dist/admin/option-resource-templates-panel.d.ts +22 -0
- package/dist/admin/option-resource-templates-panel.d.ts.map +1 -0
- package/dist/admin/option-resource-templates-panel.js +251 -0
- package/dist/admin/pages/availability-rule-detail-page.d.ts +9 -0
- package/dist/admin/pages/availability-rule-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/availability-rule-detail-page.js +11 -0
- package/dist/admin/pages/availability-slot-detail-page.d.ts +9 -0
- package/dist/admin/pages/availability-slot-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/availability-slot-detail-page.js +11 -0
- package/dist/admin/pages/availability-start-time-detail-page.d.ts +9 -0
- package/dist/admin/pages/availability-start-time-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/availability-start-time-detail-page.js +11 -0
- package/dist/admin/rule-detail-host.d.ts +14 -0
- package/dist/admin/rule-detail-host.d.ts.map +1 -0
- package/dist/admin/rule-detail-host.js +27 -0
- package/dist/admin/slot-detail-host.d.ts +29 -0
- package/dist/admin/slot-detail-host.d.ts.map +1 -0
- package/dist/admin/slot-detail-host.js +110 -0
- package/dist/admin/start-time-detail-host.d.ts +15 -0
- package/dist/admin/start-time-detail-host.d.ts.map +1 -0
- package/dist/admin/start-time-detail-host.js +37 -0
- package/dist/components/availability-columns.d.ts +42 -0
- package/dist/components/availability-columns.d.ts.map +1 -0
- package/dist/components/availability-columns.js +182 -0
- package/dist/components/availability-dialogs.d.ts +236 -0
- package/dist/components/availability-dialogs.d.ts.map +1 -0
- package/dist/components/availability-dialogs.js +369 -0
- package/dist/components/availability-overview.d.ts +54 -0
- package/dist/components/availability-overview.d.ts.map +1 -0
- package/dist/components/availability-overview.js +50 -0
- package/dist/components/availability-page.d.ts +32 -0
- package/dist/components/availability-page.d.ts.map +1 -0
- package/dist/components/availability-page.js +128 -0
- package/dist/components/availability-rule-detail-page.d.ts +251 -0
- package/dist/components/availability-rule-detail-page.d.ts.map +1 -0
- package/dist/components/availability-rule-detail-page.js +74 -0
- package/dist/components/availability-section-header.d.ts +8 -0
- package/dist/components/availability-section-header.d.ts.map +1 -0
- package/dist/components/availability-section-header.js +7 -0
- package/dist/components/availability-skeletons.d.ts +6 -0
- package/dist/components/availability-skeletons.d.ts.map +1 -0
- package/dist/components/availability-skeletons.js +34 -0
- package/dist/components/availability-slot-detail-page.d.ts +974 -0
- package/dist/components/availability-slot-detail-page.d.ts.map +1 -0
- package/dist/components/availability-slot-detail-page.js +383 -0
- package/dist/components/availability-start-time-detail-page.d.ts +246 -0
- package/dist/components/availability-start-time-detail-page.d.ts.map +1 -0
- package/dist/components/availability-start-time-detail-page.js +83 -0
- package/dist/components/availability-tabs.d.ts +152 -0
- package/dist/components/availability-tabs.d.ts.map +1 -0
- package/dist/components/availability-tabs.js +192 -0
- package/dist/components/slot-status-tone.d.ts +15 -0
- package/dist/components/slot-status-tone.d.ts.map +1 -0
- package/dist/components/slot-status-tone.js +18 -0
- package/dist/form-resolver.d.ts +4 -0
- package/dist/form-resolver.d.ts.map +1 -0
- package/dist/form-resolver.js +40 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/provider.d.ts +2003 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +102 -0
- package/dist/ui.d.ts +13 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +12 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -0
- package/package.json +92 -9
- package/src/styles.css +11 -0
package/README.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# @voyantjs/availability-react
|
|
2
2
|
|
|
3
|
-
|
|
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"}
|