@voyant-travel/distribution-react 0.109.8
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/LICENSE +201 -0
- package/README.md +82 -0
- package/dist/admin/index.d.ts +30 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +65 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +59 -0
- package/dist/components/booking-link-detail-page.d.ts +10 -0
- package/dist/components/booking-link-detail-page.d.ts.map +1 -0
- package/dist/components/booking-link-detail-page.js +51 -0
- package/dist/components/channel-detail-page.d.ts +12 -0
- package/dist/components/channel-detail-page.d.ts.map +1 -0
- package/dist/components/channel-detail-page.js +41 -0
- package/dist/components/channel-sync-controls.d.ts +15 -0
- package/dist/components/channel-sync-controls.d.ts.map +1 -0
- package/dist/components/channel-sync-controls.js +29 -0
- package/dist/components/channel-sync-deliveries-drawer.d.ts +12 -0
- package/dist/components/channel-sync-deliveries-drawer.d.ts.map +1 -0
- package/dist/components/channel-sync-deliveries-drawer.js +18 -0
- package/dist/components/channel-sync-page-utils.d.ts +99 -0
- package/dist/components/channel-sync-page-utils.d.ts.map +1 -0
- package/dist/components/channel-sync-page-utils.js +85 -0
- package/dist/components/channel-sync-page.d.ts +4 -0
- package/dist/components/channel-sync-page.d.ts.map +1 -0
- package/dist/components/channel-sync-page.js +141 -0
- package/dist/components/channels-page.d.ts +6 -0
- package/dist/components/channels-page.d.ts.map +1 -0
- package/dist/components/channels-page.js +132 -0
- package/dist/components/commission-rule-detail-page.d.ts +10 -0
- package/dist/components/commission-rule-detail-page.d.ts.map +1 -0
- package/dist/components/commission-rule-detail-page.js +57 -0
- package/dist/components/contract-detail-page.d.ts +10 -0
- package/dist/components/contract-detail-page.d.ts.map +1 -0
- package/dist/components/contract-detail-page.js +64 -0
- package/dist/components/distribution-overview.d.ts +19 -0
- package/dist/components/distribution-overview.d.ts.map +1 -0
- package/dist/components/distribution-overview.js +13 -0
- package/dist/components/distribution-page.d.ts +26 -0
- package/dist/components/distribution-page.d.ts.map +1 -0
- package/dist/components/distribution-page.js +190 -0
- package/dist/components/distribution-section-header.d.ts +7 -0
- package/dist/components/distribution-section-header.d.ts.map +1 -0
- package/dist/components/distribution-section-header.js +6 -0
- package/dist/components/distribution-shared.d.ts +32 -0
- package/dist/components/distribution-shared.d.ts.map +1 -0
- package/dist/components/distribution-shared.js +246 -0
- package/dist/components/distribution-tabs-primary.d.ts +57 -0
- package/dist/components/distribution-tabs-primary.d.ts.map +1 -0
- package/dist/components/distribution-tabs-primary.js +89 -0
- package/dist/components/distribution-tabs-secondary.d.ts +58 -0
- package/dist/components/distribution-tabs-secondary.d.ts.map +1 -0
- package/dist/components/distribution-tabs-secondary.js +89 -0
- package/dist/components/mapping-detail-page.d.ts +10 -0
- package/dist/components/mapping-detail-page.d.ts.map +1 -0
- package/dist/components/mapping-detail-page.js +51 -0
- package/dist/components/webhook-event-detail-page.d.ts +9 -0
- package/dist/components/webhook-event-detail-page.d.ts.map +1 -0
- package/dist/components/webhook-event-detail-page.js +46 -0
- package/dist/constants.d.ts +103 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +48 -0
- package/dist/external-refs/client.d.ts +14 -0
- package/dist/external-refs/client.d.ts.map +1 -0
- package/dist/external-refs/client.js +58 -0
- package/dist/external-refs/components/entity-ref-picker.d.ts +21 -0
- package/dist/external-refs/components/entity-ref-picker.d.ts.map +1 -0
- package/dist/external-refs/components/entity-ref-picker.js +38 -0
- package/dist/external-refs/components/external-ref-dialog.d.ts +11 -0
- package/dist/external-refs/components/external-ref-dialog.d.ts.map +1 -0
- package/dist/external-refs/components/external-ref-dialog.js +120 -0
- package/dist/external-refs/components/external-refs-page.d.ts +16 -0
- package/dist/external-refs/components/external-refs-page.d.ts.map +1 -0
- package/dist/external-refs/components/external-refs-page.js +109 -0
- package/dist/external-refs/hooks/index.d.ts +3 -0
- package/dist/external-refs/hooks/index.d.ts.map +1 -0
- package/dist/external-refs/hooks/index.js +2 -0
- package/dist/external-refs/hooks/use-external-ref-mutation.d.ts +57 -0
- package/dist/external-refs/hooks/use-external-ref-mutation.d.ts.map +1 -0
- package/dist/external-refs/hooks/use-external-ref-mutation.js +40 -0
- package/dist/external-refs/hooks/use-external-refs.d.ts +26 -0
- package/dist/external-refs/hooks/use-external-refs.d.ts.map +1 -0
- package/dist/external-refs/hooks/use-external-refs.js +12 -0
- package/dist/external-refs/i18n/en.d.ts +3 -0
- package/dist/external-refs/i18n/en.d.ts.map +1 -0
- package/dist/external-refs/i18n/en.js +92 -0
- package/dist/external-refs/i18n/index.d.ts +5 -0
- package/dist/external-refs/i18n/index.d.ts.map +1 -0
- package/dist/external-refs/i18n/index.js +3 -0
- package/dist/external-refs/i18n/messages.d.ts +85 -0
- package/dist/external-refs/i18n/messages.d.ts.map +1 -0
- package/dist/external-refs/i18n/messages.js +1 -0
- package/dist/external-refs/i18n/provider.d.ts +26 -0
- package/dist/external-refs/i18n/provider.d.ts.map +1 -0
- package/dist/external-refs/i18n/provider.js +44 -0
- package/dist/external-refs/i18n/ro.d.ts +3 -0
- package/dist/external-refs/i18n/ro.d.ts.map +1 -0
- package/dist/external-refs/i18n/ro.js +92 -0
- package/dist/external-refs/index.d.ts +7 -0
- package/dist/external-refs/index.d.ts.map +1 -0
- package/dist/external-refs/index.js +6 -0
- package/dist/external-refs/provider.d.ts +2 -0
- package/dist/external-refs/provider.d.ts.map +1 -0
- package/dist/external-refs/provider.js +1 -0
- package/dist/external-refs/query-keys.d.ts +18 -0
- package/dist/external-refs/query-keys.d.ts.map +1 -0
- package/dist/external-refs/query-keys.js +6 -0
- package/dist/external-refs/query-options.d.ts +159 -0
- package/dist/external-refs/query-options.d.ts.map +1 -0
- package/dist/external-refs/query-options.js +31 -0
- package/dist/external-refs/schemas.d.ts +82 -0
- package/dist/external-refs/schemas.d.ts.map +1 -0
- package/dist/external-refs/schemas.js +20 -0
- package/dist/external-refs/ui.d.ts +5 -0
- package/dist/external-refs/ui.d.ts.map +1 -0
- package/dist/external-refs/ui.js +4 -0
- package/dist/hooks/index.d.ts +12 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +12 -0
- package/dist/hooks/use-booking-links.d.ts +20 -0
- package/dist/hooks/use-booking-links.d.ts.map +1 -0
- package/dist/hooks/use-booking-links.js +9 -0
- package/dist/hooks/use-bookings.d.ts +14 -0
- package/dist/hooks/use-bookings.d.ts.map +1 -0
- package/dist/hooks/use-bookings.js +9 -0
- package/dist/hooks/use-channel-mutation.d.ts +69 -0
- package/dist/hooks/use-channel-mutation.d.ts.map +1 -0
- package/dist/hooks/use-channel-mutation.js +49 -0
- package/dist/hooks/use-channel.d.ts +16 -0
- package/dist/hooks/use-channel.d.ts.map +1 -0
- package/dist/hooks/use-channel.js +13 -0
- package/dist/hooks/use-channels.d.ts +20 -0
- package/dist/hooks/use-channels.d.ts.map +1 -0
- package/dist/hooks/use-channels.js +9 -0
- package/dist/hooks/use-commission-rules.d.ts +23 -0
- package/dist/hooks/use-commission-rules.d.ts.map +1 -0
- package/dist/hooks/use-commission-rules.js +9 -0
- package/dist/hooks/use-contracts.d.ts +22 -0
- package/dist/hooks/use-contracts.d.ts.map +1 -0
- package/dist/hooks/use-contracts.js +9 -0
- package/dist/hooks/use-mappings.d.ts +19 -0
- package/dist/hooks/use-mappings.d.ts.map +1 -0
- package/dist/hooks/use-mappings.js +9 -0
- package/dist/hooks/use-products.d.ts +14 -0
- package/dist/hooks/use-products.d.ts.map +1 -0
- package/dist/hooks/use-products.js +9 -0
- package/dist/hooks/use-suppliers.d.ts +14 -0
- package/dist/hooks/use-suppliers.d.ts.map +1 -0
- package/dist/hooks/use-suppliers.js +9 -0
- package/dist/hooks/use-webhook-events.d.ts +21 -0
- package/dist/hooks/use-webhook-events.d.ts.map +1 -0
- package/dist/hooks/use-webhook-events.js +9 -0
- package/dist/i18n/en.d.ts +592 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +561 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/messages.d.ts +409 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/provider.d.ts +1207 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +44 -0
- package/dist/i18n/ro.d.ts +592 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +561 -0
- package/dist/i18n/utils.d.ts +4 -0
- package/dist/i18n/utils.d.ts.map +1 -0
- package/dist/i18n/utils.js +8 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +1 -0
- package/dist/query-keys.d.ts +61 -0
- package/dist/query-keys.d.ts.map +1 -0
- package/dist/query-keys.js +30 -0
- package/dist/query-options.d.ts +999 -0
- package/dist/query-options.d.ts.map +1 -0
- package/dist/query-options.js +219 -0
- package/dist/schemas.d.ts +615 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +144 -0
- package/dist/suppliers/admin/index.d.ts +69 -0
- package/dist/suppliers/admin/index.d.ts.map +1 -0
- package/dist/suppliers/admin/index.js +111 -0
- package/dist/suppliers/admin/pages/supplier-detail-page.d.ts +9 -0
- package/dist/suppliers/admin/pages/supplier-detail-page.d.ts.map +1 -0
- package/dist/suppliers/admin/pages/supplier-detail-page.js +11 -0
- package/dist/suppliers/admin/slots.d.ts +19 -0
- package/dist/suppliers/admin/slots.d.ts.map +1 -0
- package/dist/suppliers/admin/slots.js +18 -0
- package/dist/suppliers/admin/supplier-detail-host.d.ts +30 -0
- package/dist/suppliers/admin/supplier-detail-host.d.ts.map +1 -0
- package/dist/suppliers/admin/supplier-detail-host.js +34 -0
- package/dist/suppliers/admin/supplier-detail-skeleton.d.ts +9 -0
- package/dist/suppliers/admin/supplier-detail-skeleton.d.ts.map +1 -0
- package/dist/suppliers/admin/supplier-detail-skeleton.js +20 -0
- package/dist/suppliers/admin/suppliers-host.d.ts +11 -0
- package/dist/suppliers/admin/suppliers-host.d.ts.map +1 -0
- package/dist/suppliers/admin/suppliers-host.js +17 -0
- package/dist/suppliers/admin/suppliers-list-skeleton.d.ts +9 -0
- package/dist/suppliers/admin/suppliers-list-skeleton.d.ts.map +1 -0
- package/dist/suppliers/admin/suppliers-list-skeleton.js +24 -0
- package/dist/suppliers/client.d.ts +14 -0
- package/dist/suppliers/client.d.ts.map +1 -0
- package/dist/suppliers/client.js +59 -0
- package/dist/suppliers/components/message-format.d.ts +2 -0
- package/dist/suppliers/components/message-format.d.ts.map +1 -0
- package/dist/suppliers/components/message-format.js +3 -0
- package/dist/suppliers/components/rate-dialog.d.ts +11 -0
- package/dist/suppliers/components/rate-dialog.d.ts.map +1 -0
- package/dist/suppliers/components/rate-dialog.js +93 -0
- package/dist/suppliers/components/service-dialog.d.ts +10 -0
- package/dist/suppliers/components/service-dialog.d.ts.map +1 -0
- package/dist/suppliers/components/service-dialog.js +69 -0
- package/dist/suppliers/components/supplier-combobox.d.ts +13 -0
- package/dist/suppliers/components/supplier-combobox.d.ts.map +1 -0
- package/dist/suppliers/components/supplier-combobox.js +23 -0
- package/dist/suppliers/components/supplier-detail-page.d.ts +17 -0
- package/dist/suppliers/components/supplier-detail-page.d.ts.map +1 -0
- package/dist/suppliers/components/supplier-detail-page.js +105 -0
- package/dist/suppliers/components/supplier-dialog.d.ts +9 -0
- package/dist/suppliers/components/supplier-dialog.d.ts.map +1 -0
- package/dist/suppliers/components/supplier-dialog.js +118 -0
- package/dist/suppliers/components/supplier-service-row.d.ts +22 -0
- package/dist/suppliers/components/supplier-service-row.d.ts.map +1 -0
- package/dist/suppliers/components/supplier-service-row.js +18 -0
- package/dist/suppliers/components/suppliers-page.d.ts +10 -0
- package/dist/suppliers/components/suppliers-page.d.ts.map +1 -0
- package/dist/suppliers/components/suppliers-page.js +94 -0
- package/dist/suppliers/constants.d.ts +69 -0
- package/dist/suppliers/constants.d.ts.map +1 -0
- package/dist/suppliers/constants.js +34 -0
- package/dist/suppliers/hooks/index.d.ts +10 -0
- package/dist/suppliers/hooks/index.d.ts.map +1 -0
- package/dist/suppliers/hooks/index.js +10 -0
- package/dist/suppliers/hooks/use-supplier-mutation.d.ts +95 -0
- package/dist/suppliers/hooks/use-supplier-mutation.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier-mutation.js +41 -0
- package/dist/suppliers/hooks/use-supplier-note-mutation.d.ts +13 -0
- package/dist/suppliers/hooks/use-supplier-note-mutation.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier-note-mutation.js +20 -0
- package/dist/suppliers/hooks/use-supplier-notes.d.ts +13 -0
- package/dist/suppliers/hooks/use-supplier-notes.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier-notes.js +12 -0
- package/dist/suppliers/hooks/use-supplier-rate-mutation.d.ts +56 -0
- package/dist/suppliers/hooks/use-supplier-rate-mutation.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier-rate-mutation.js +41 -0
- package/dist/suppliers/hooks/use-supplier-service-mutation.d.ts +45 -0
- package/dist/suppliers/hooks/use-supplier-service-mutation.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier-service-mutation.js +44 -0
- package/dist/suppliers/hooks/use-supplier-service-rates.d.ts +20 -0
- package/dist/suppliers/hooks/use-supplier-service-rates.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier-service-rates.js +12 -0
- package/dist/suppliers/hooks/use-supplier-services.d.ts +19 -0
- package/dist/suppliers/hooks/use-supplier-services.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier-services.js +12 -0
- package/dist/suppliers/hooks/use-supplier.d.ts +38 -0
- package/dist/suppliers/hooks/use-supplier.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-supplier.js +9 -0
- package/dist/suppliers/hooks/use-suppliers.d.ts +42 -0
- package/dist/suppliers/hooks/use-suppliers.d.ts.map +1 -0
- package/dist/suppliers/hooks/use-suppliers.js +9 -0
- package/dist/suppliers/i18n/en.d.ts +204 -0
- package/dist/suppliers/i18n/en.d.ts.map +1 -0
- package/dist/suppliers/i18n/en.js +203 -0
- package/dist/suppliers/i18n/index.d.ts +5 -0
- package/dist/suppliers/i18n/index.d.ts.map +1 -0
- package/dist/suppliers/i18n/index.js +3 -0
- package/dist/suppliers/i18n/messages.d.ts +187 -0
- package/dist/suppliers/i18n/messages.d.ts.map +1 -0
- package/dist/suppliers/i18n/messages.js +1 -0
- package/dist/suppliers/i18n/provider.d.ts +430 -0
- package/dist/suppliers/i18n/provider.d.ts.map +1 -0
- package/dist/suppliers/i18n/provider.js +44 -0
- package/dist/suppliers/i18n/ro.d.ts +204 -0
- package/dist/suppliers/i18n/ro.d.ts.map +1 -0
- package/dist/suppliers/i18n/ro.js +203 -0
- package/dist/suppliers/index.d.ts +9 -0
- package/dist/suppliers/index.d.ts.map +1 -0
- package/dist/suppliers/index.js +8 -0
- package/dist/suppliers/provider.d.ts +2 -0
- package/dist/suppliers/provider.d.ts.map +1 -0
- package/dist/suppliers/provider.js +1 -0
- package/dist/suppliers/query-keys.d.ts +29 -0
- package/dist/suppliers/query-keys.d.ts.map +1 -0
- package/dist/suppliers/query-keys.js +12 -0
- package/dist/suppliers/query-options.d.ts +467 -0
- package/dist/suppliers/query-options.d.ts.map +1 -0
- package/dist/suppliers/query-options.js +63 -0
- package/dist/suppliers/schemas.d.ts +363 -0
- package/dist/suppliers/schemas.d.ts.map +1 -0
- package/dist/suppliers/schemas.js +109 -0
- package/dist/suppliers/ui.d.ts +9 -0
- package/dist/suppliers/ui.d.ts.map +1 -0
- package/dist/suppliers/ui.js +8 -0
- package/dist/suppliers/utils.d.ts +4 -0
- package/dist/suppliers/utils.d.ts.map +1 -0
- package/dist/suppliers/utils.js +10 -0
- package/dist/ui.d.ts +16 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +14 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +44 -0
- package/package.json +263 -0
- package/src/external-refs/styles.css +11 -0
- package/src/styles.css +11 -0
- package/src/suppliers/styles.css +11 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmActionButton, } from "@voyant-travel/ui/components";
|
|
4
|
+
import { cn } from "@voyant-travel/ui/lib/utils";
|
|
5
|
+
import { ArrowLeft, Link2, Loader2, Package } from "lucide-react";
|
|
6
|
+
import { useDistributionUiI18nOrDefault } from "../i18n/index.js";
|
|
7
|
+
import { distributionQueryKeys, fetchWithValidation, getChannelQueryOptions, getMappingQueryOptions, getProductQueryOptions, successEnvelope, useVoyantDistributionContext, } from "../index.js";
|
|
8
|
+
import { formatDistributionDateTime } from "./distribution-shared.js";
|
|
9
|
+
const noop = () => { };
|
|
10
|
+
export function MappingDetailPage({ id, className, onBack = noop, onDeleted = noop, onChannelOpen = noop, onProductOpen = noop, }) {
|
|
11
|
+
const i18n = useDistributionUiI18nOrDefault();
|
|
12
|
+
const { messages } = i18n;
|
|
13
|
+
const detail = messages.details.mapping;
|
|
14
|
+
const client = useVoyantDistributionContext();
|
|
15
|
+
const queryClient = useQueryClient();
|
|
16
|
+
const mappingQuery = useQuery({
|
|
17
|
+
...getMappingQueryOptions(client, id),
|
|
18
|
+
select: (result) => result.data,
|
|
19
|
+
});
|
|
20
|
+
const mapping = mappingQuery.data;
|
|
21
|
+
const channelQuery = useQuery({
|
|
22
|
+
...getChannelQueryOptions(client, mapping?.channelId),
|
|
23
|
+
select: (result) => result.data,
|
|
24
|
+
enabled: Boolean(mapping?.channelId),
|
|
25
|
+
});
|
|
26
|
+
const productQuery = useQuery({
|
|
27
|
+
...getProductQueryOptions(client, mapping?.productId),
|
|
28
|
+
select: (result) => result.data,
|
|
29
|
+
enabled: Boolean(mapping?.productId),
|
|
30
|
+
});
|
|
31
|
+
const remove = useMutation({
|
|
32
|
+
mutationFn: () => fetchWithValidation(`/v1/distribution/product-mappings/${id}`, successEnvelope, client, {
|
|
33
|
+
method: "DELETE", // i18n-literal-ok HTTP method
|
|
34
|
+
}),
|
|
35
|
+
onSuccess: () => {
|
|
36
|
+
void queryClient.invalidateQueries({ queryKey: distributionQueryKeys.mappings() });
|
|
37
|
+
queryClient.removeQueries({ queryKey: distributionQueryKeys.mapping(id) });
|
|
38
|
+
onDeleted();
|
|
39
|
+
onBack();
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
if (mappingQuery.isPending) {
|
|
43
|
+
return (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) }));
|
|
44
|
+
}
|
|
45
|
+
if (!mapping) {
|
|
46
|
+
return (_jsxs("div", { className: "flex flex-col items-center justify-center gap-4 py-12", children: [_jsx("p", { className: "text-muted-foreground", children: detail.notFound }), _jsx(Button, { variant: "outline", onClick: onBack, children: messages.common.backToDistribution })] }));
|
|
47
|
+
}
|
|
48
|
+
return (_jsxs("div", { "data-slot": "mapping-detail-page", className: cn("flex flex-col gap-6 p-6", className), children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx(Button, { variant: "ghost", size: "icon", onClick: onBack, children: _jsx(ArrowLeft, { className: "h-4 w-4" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: detail.title }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [_jsx(Badge, { variant: mapping.active ? "default" : "secondary", children: mapping.active ? messages.common.active : messages.common.inactive }), _jsx(Badge, { variant: "outline", children: mapping.externalProductId })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => onChannelOpen(mapping.channelId), children: [_jsx(Link2, { className: "mr-2 h-4 w-4" }), detail.openChannel] }), _jsxs(Button, { variant: "outline", onClick: () => onProductOpen(mapping.productId), children: [_jsx(Package, { className: "mr-2 h-4 w-4" }), detail.openProduct] }), _jsx(ConfirmActionButton, { buttonLabel: detail.deleteButton, confirmLabel: detail.deleteButton, title: detail.deleteConfirm, description: detail.deleteDescription, variant: "destructive", confirmVariant: "destructive", disabled: remove.isPending, onConfirm: async () => {
|
|
49
|
+
await remove.mutateAsync();
|
|
50
|
+
} })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: detail.sections.details }) }), _jsxs(CardContent, { className: "grid gap-3 text-sm md:grid-cols-2", children: [_jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [messages.common.channelLabel, ":"] }), " ", _jsx("span", { children: channelQuery.data?.name ?? mapping.channelId })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [messages.common.productLabel, ":"] }), " ", _jsx("span", { children: productQuery.data?.name ?? mapping.productId })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [detail.labels.externalProduct, ":"] }), " ", _jsx("span", { children: mapping.externalProductId })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [detail.labels.externalRate, ":"] }), " ", _jsx("span", { children: mapping.externalRateId ?? messages.common.none })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [detail.labels.externalCategory, ":"] }), " ", _jsx("span", { children: mapping.externalCategoryId ?? messages.common.none })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [messages.common.createdLabel, ":"] }), " ", _jsx("span", { children: formatDistributionDateTime(mapping.createdAt, i18n) })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [messages.common.updatedLabel, ":"] }), " ", _jsx("span", { children: formatDistributionDateTime(mapping.updatedAt, i18n) })] })] })] })] }));
|
|
51
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface WebhookEventDetailPageProps {
|
|
2
|
+
id: string;
|
|
3
|
+
className?: string;
|
|
4
|
+
onBack?: () => void;
|
|
5
|
+
onDeleted?: () => void;
|
|
6
|
+
onChannelOpen?: (channelId: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function WebhookEventDetailPage({ id, className, onBack, onDeleted, onChannelOpen, }: WebhookEventDetailPageProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=webhook-event-detail-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-event-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/webhook-event-detail-page.tsx"],"names":[],"mappings":"AAuBA,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5C;AAID,wBAAgB,sBAAsB,CAAC,EACrC,EAAE,EACF,SAAS,EACT,MAAa,EACb,SAAgB,EAChB,aAAoB,GACrB,EAAE,2BAA2B,2CA0I7B"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmActionButton, } from "@voyant-travel/ui/components";
|
|
4
|
+
import { cn } from "@voyant-travel/ui/lib/utils";
|
|
5
|
+
import { ArrowLeft, Link2, Loader2, Webhook } from "lucide-react";
|
|
6
|
+
import { useDistributionUiI18nOrDefault } from "../i18n/index.js";
|
|
7
|
+
import { distributionQueryKeys, fetchWithValidation, getChannelQueryOptions, getWebhookEventQueryOptions, successEnvelope, useVoyantDistributionContext, } from "../index.js";
|
|
8
|
+
import { formatDistributionDateTime } from "./distribution-shared.js";
|
|
9
|
+
const noop = () => { };
|
|
10
|
+
export function WebhookEventDetailPage({ id, className, onBack = noop, onDeleted = noop, onChannelOpen = noop, }) {
|
|
11
|
+
const i18n = useDistributionUiI18nOrDefault();
|
|
12
|
+
const { messages } = i18n;
|
|
13
|
+
const detail = messages.details.webhookEvent;
|
|
14
|
+
const client = useVoyantDistributionContext();
|
|
15
|
+
const queryClient = useQueryClient();
|
|
16
|
+
const eventQuery = useQuery({
|
|
17
|
+
...getWebhookEventQueryOptions(client, id),
|
|
18
|
+
select: (result) => result.data,
|
|
19
|
+
});
|
|
20
|
+
const event = eventQuery.data;
|
|
21
|
+
const channelQuery = useQuery({
|
|
22
|
+
...getChannelQueryOptions(client, event?.channelId),
|
|
23
|
+
select: (result) => result.data,
|
|
24
|
+
enabled: Boolean(event?.channelId),
|
|
25
|
+
});
|
|
26
|
+
const remove = useMutation({
|
|
27
|
+
mutationFn: () => fetchWithValidation(`/v1/distribution/webhook-events/${id}`, successEnvelope, client, {
|
|
28
|
+
method: "DELETE", // i18n-literal-ok HTTP method
|
|
29
|
+
}),
|
|
30
|
+
onSuccess: () => {
|
|
31
|
+
void queryClient.invalidateQueries({ queryKey: distributionQueryKeys.webhookEvents() });
|
|
32
|
+
queryClient.removeQueries({ queryKey: distributionQueryKeys.webhookEvent(id) });
|
|
33
|
+
onDeleted();
|
|
34
|
+
onBack();
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
if (eventQuery.isPending) {
|
|
38
|
+
return (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) }));
|
|
39
|
+
}
|
|
40
|
+
if (!event) {
|
|
41
|
+
return (_jsxs("div", { className: "flex flex-col items-center justify-center gap-4 py-12", children: [_jsx("p", { className: "text-muted-foreground", children: detail.notFound }), _jsx(Button, { variant: "outline", onClick: onBack, children: messages.common.backToDistribution })] }));
|
|
42
|
+
}
|
|
43
|
+
return (_jsxs("div", { "data-slot": "webhook-event-detail-page", className: cn("flex flex-col gap-6 p-6", className), children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx(Button, { variant: "ghost", size: "icon", onClick: onBack, children: _jsx(ArrowLeft, { className: "h-4 w-4" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: detail.title }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [_jsx(Badge, { variant: "outline", children: messages.common.webhookStatusLabels[event.status] }), _jsx(Badge, { variant: "secondary", children: event.eventType })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => onChannelOpen(event.channelId), children: [_jsx(Link2, { className: "mr-2 h-4 w-4" }), detail.openChannel] }), _jsx(ConfirmActionButton, { buttonLabel: detail.deleteButton, confirmLabel: detail.deleteButton, title: detail.deleteConfirm, description: detail.deleteDescription, variant: "destructive", confirmVariant: "destructive", disabled: remove.isPending, onConfirm: async () => {
|
|
44
|
+
await remove.mutateAsync();
|
|
45
|
+
} })] })] }), _jsxs("div", { className: "grid gap-6 md:grid-cols-2", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center gap-2", children: [_jsx(Webhook, { className: "h-4 w-4" }), _jsx(CardTitle, { children: detail.sections.details })] }), _jsxs(CardContent, { className: "grid gap-3 text-sm", children: [_jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [messages.common.channelLabel, ":"] }), " ", _jsx("span", { children: channelQuery.data?.name ?? event.channelId })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [detail.labels.externalEvent, ":"] }), " ", _jsx("span", { children: event.externalEventId ?? messages.common.none })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [detail.labels.received, ":"] }), " ", _jsx("span", { children: formatDistributionDateTime(event.receivedAt, i18n) })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [detail.labels.processed, ":"] }), " ", _jsx("span", { children: formatDistributionDateTime(event.processedAt, i18n) })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [messages.common.createdLabel, ":"] }), " ", _jsx("span", { children: formatDistributionDateTime(event.createdAt, i18n) })] }), _jsxs("div", { children: [_jsxs("span", { className: "text-muted-foreground", children: [messages.common.updatedLabel, ":"] }), " ", _jsx("span", { children: formatDistributionDateTime(event.updatedAt, i18n) })] }), event.errorMessage ? (_jsxs("div", { children: [_jsx("div", { className: "mb-1 text-muted-foreground", children: detail.labels.error }), _jsx("div", { className: "whitespace-pre-wrap rounded-md border p-3", children: event.errorMessage })] })) : null] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: detail.sections.payload }) }), _jsx(CardContent, { className: "text-sm", children: _jsx("pre", { className: "overflow-x-auto rounded-md bg-muted p-3 text-xs", children: JSON.stringify(event.payload, null, 2) }) })] })] })] }));
|
|
46
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export declare const NONE_VALUE = "__none__";
|
|
2
|
+
export declare const channelKindOptions: readonly [{
|
|
3
|
+
readonly value: "direct";
|
|
4
|
+
readonly label: "Direct";
|
|
5
|
+
}, {
|
|
6
|
+
readonly value: "affiliate";
|
|
7
|
+
readonly label: "Affiliate";
|
|
8
|
+
}, {
|
|
9
|
+
readonly value: "ota";
|
|
10
|
+
readonly label: "OTA";
|
|
11
|
+
}, {
|
|
12
|
+
readonly value: "reseller";
|
|
13
|
+
readonly label: "Reseller";
|
|
14
|
+
}, {
|
|
15
|
+
readonly value: "marketplace";
|
|
16
|
+
readonly label: "Marketplace";
|
|
17
|
+
}, {
|
|
18
|
+
readonly value: "api_partner";
|
|
19
|
+
readonly label: "API Partner";
|
|
20
|
+
}, {
|
|
21
|
+
readonly value: "connect";
|
|
22
|
+
readonly label: "Connect";
|
|
23
|
+
}];
|
|
24
|
+
export declare const channelStatusOptions: readonly [{
|
|
25
|
+
readonly value: "active";
|
|
26
|
+
readonly label: "Active";
|
|
27
|
+
}, {
|
|
28
|
+
readonly value: "inactive";
|
|
29
|
+
readonly label: "Inactive";
|
|
30
|
+
}, {
|
|
31
|
+
readonly value: "pending";
|
|
32
|
+
readonly label: "Pending";
|
|
33
|
+
}, {
|
|
34
|
+
readonly value: "archived";
|
|
35
|
+
readonly label: "Archived";
|
|
36
|
+
}];
|
|
37
|
+
export declare const contractStatusOptions: readonly [{
|
|
38
|
+
readonly value: "draft";
|
|
39
|
+
readonly label: "Draft";
|
|
40
|
+
}, {
|
|
41
|
+
readonly value: "active";
|
|
42
|
+
readonly label: "Active";
|
|
43
|
+
}, {
|
|
44
|
+
readonly value: "expired";
|
|
45
|
+
readonly label: "Expired";
|
|
46
|
+
}, {
|
|
47
|
+
readonly value: "terminated";
|
|
48
|
+
readonly label: "Terminated";
|
|
49
|
+
}];
|
|
50
|
+
export declare const paymentOwnerOptions: readonly [{
|
|
51
|
+
readonly value: "operator";
|
|
52
|
+
readonly label: "Operator";
|
|
53
|
+
}, {
|
|
54
|
+
readonly value: "channel";
|
|
55
|
+
readonly label: "Channel";
|
|
56
|
+
}, {
|
|
57
|
+
readonly value: "split";
|
|
58
|
+
readonly label: "Split";
|
|
59
|
+
}];
|
|
60
|
+
export declare const cancellationOwnerOptions: readonly [{
|
|
61
|
+
readonly value: "operator";
|
|
62
|
+
readonly label: "Operator";
|
|
63
|
+
}, {
|
|
64
|
+
readonly value: "channel";
|
|
65
|
+
readonly label: "Channel";
|
|
66
|
+
}, {
|
|
67
|
+
readonly value: "mixed";
|
|
68
|
+
readonly label: "Mixed";
|
|
69
|
+
}];
|
|
70
|
+
export declare const commissionScopeOptions: readonly [{
|
|
71
|
+
readonly value: "booking";
|
|
72
|
+
readonly label: "Booking";
|
|
73
|
+
}, {
|
|
74
|
+
readonly value: "product";
|
|
75
|
+
readonly label: "Product";
|
|
76
|
+
}, {
|
|
77
|
+
readonly value: "rate";
|
|
78
|
+
readonly label: "Rate";
|
|
79
|
+
}, {
|
|
80
|
+
readonly value: "category";
|
|
81
|
+
readonly label: "Category";
|
|
82
|
+
}];
|
|
83
|
+
export declare const commissionTypeOptions: readonly [{
|
|
84
|
+
readonly value: "fixed";
|
|
85
|
+
readonly label: "Fixed";
|
|
86
|
+
}, {
|
|
87
|
+
readonly value: "percentage";
|
|
88
|
+
readonly label: "Percentage";
|
|
89
|
+
}];
|
|
90
|
+
export declare const webhookStatusOptions: readonly [{
|
|
91
|
+
readonly value: "pending";
|
|
92
|
+
readonly label: "Pending";
|
|
93
|
+
}, {
|
|
94
|
+
readonly value: "processed";
|
|
95
|
+
readonly label: "Processed";
|
|
96
|
+
}, {
|
|
97
|
+
readonly value: "failed";
|
|
98
|
+
readonly label: "Failed";
|
|
99
|
+
}, {
|
|
100
|
+
readonly value: "ignored";
|
|
101
|
+
readonly label: "Ignored";
|
|
102
|
+
}];
|
|
103
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,aAAa,CAAA;AAEpC,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;EAQrB,CAAA;AAEV,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAKvB,CAAA;AAEV,eAAO,MAAM,qBAAqB;;;;;;;;;;;;EAKxB,CAAA;AAEV,eAAO,MAAM,mBAAmB;;;;;;;;;EAItB,CAAA;AAEV,eAAO,MAAM,wBAAwB;;;;;;;;;EAI3B,CAAA;AAEV,eAAO,MAAM,sBAAsB;;;;;;;;;;;;EAKzB,CAAA;AAEV,eAAO,MAAM,qBAAqB;;;;;;EAGxB,CAAA;AAEV,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAKvB,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const NONE_VALUE = "__none__";
|
|
2
|
+
export const channelKindOptions = [
|
|
3
|
+
{ value: "direct", label: "Direct" },
|
|
4
|
+
{ value: "affiliate", label: "Affiliate" },
|
|
5
|
+
{ value: "ota", label: "OTA" },
|
|
6
|
+
{ value: "reseller", label: "Reseller" },
|
|
7
|
+
{ value: "marketplace", label: "Marketplace" },
|
|
8
|
+
{ value: "api_partner", label: "API Partner" },
|
|
9
|
+
{ value: "connect", label: "Connect" },
|
|
10
|
+
];
|
|
11
|
+
export const channelStatusOptions = [
|
|
12
|
+
{ value: "active", label: "Active" },
|
|
13
|
+
{ value: "inactive", label: "Inactive" },
|
|
14
|
+
{ value: "pending", label: "Pending" },
|
|
15
|
+
{ value: "archived", label: "Archived" },
|
|
16
|
+
];
|
|
17
|
+
export const contractStatusOptions = [
|
|
18
|
+
{ value: "draft", label: "Draft" },
|
|
19
|
+
{ value: "active", label: "Active" },
|
|
20
|
+
{ value: "expired", label: "Expired" },
|
|
21
|
+
{ value: "terminated", label: "Terminated" },
|
|
22
|
+
];
|
|
23
|
+
export const paymentOwnerOptions = [
|
|
24
|
+
{ value: "operator", label: "Operator" },
|
|
25
|
+
{ value: "channel", label: "Channel" },
|
|
26
|
+
{ value: "split", label: "Split" },
|
|
27
|
+
];
|
|
28
|
+
export const cancellationOwnerOptions = [
|
|
29
|
+
{ value: "operator", label: "Operator" },
|
|
30
|
+
{ value: "channel", label: "Channel" },
|
|
31
|
+
{ value: "mixed", label: "Mixed" },
|
|
32
|
+
];
|
|
33
|
+
export const commissionScopeOptions = [
|
|
34
|
+
{ value: "booking", label: "Booking" },
|
|
35
|
+
{ value: "product", label: "Product" },
|
|
36
|
+
{ value: "rate", label: "Rate" },
|
|
37
|
+
{ value: "category", label: "Category" },
|
|
38
|
+
];
|
|
39
|
+
export const commissionTypeOptions = [
|
|
40
|
+
{ value: "fixed", label: "Fixed" },
|
|
41
|
+
{ value: "percentage", label: "Percentage" },
|
|
42
|
+
];
|
|
43
|
+
export const webhookStatusOptions = [
|
|
44
|
+
{ value: "pending", label: "Pending" },
|
|
45
|
+
{ value: "processed", label: "Processed" },
|
|
46
|
+
{ value: "failed", label: "Failed" },
|
|
47
|
+
{ value: "ignored", label: "Ignored" },
|
|
48
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
export type VoyantFetcher = (url: string, init?: RequestInit) => Promise<Response>;
|
|
3
|
+
export declare const defaultFetcher: VoyantFetcher;
|
|
4
|
+
export declare class VoyantApiError extends Error {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly body: unknown;
|
|
7
|
+
constructor(message: string, status: number, body: unknown);
|
|
8
|
+
}
|
|
9
|
+
export interface FetchWithValidationOptions {
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
fetcher: VoyantFetcher;
|
|
12
|
+
}
|
|
13
|
+
export declare function fetchWithValidation<TOut>(path: string, schema: z.ZodType<TOut>, options: FetchWithValidationOptions, init?: RequestInit): Promise<TOut>;
|
|
14
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/external-refs/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAElF,eAAO,MAAM,cAAc,EAAE,aACoB,CAAA;AAEjD,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;gBAEV,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAM3D;AAaD,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,aAAa,CAAA;CACvB;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAC5C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EACvB,OAAO,EAAE,0BAA0B,EACnC,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const defaultFetcher = (url, init) => fetch(url, { credentials: "include", ...init });
|
|
2
|
+
export class VoyantApiError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
body;
|
|
5
|
+
constructor(message, status, body) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "VoyantApiError";
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.body = body;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function extractErrorMessage(status, statusText, body) {
|
|
13
|
+
if (typeof body === "object" && body !== null && "error" in body) {
|
|
14
|
+
const err = body.error;
|
|
15
|
+
if (typeof err === "string")
|
|
16
|
+
return err;
|
|
17
|
+
if (typeof err === "object" && err !== null && "message" in err) {
|
|
18
|
+
return String(err.message);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return `Voyant API error: ${status} ${statusText}`;
|
|
22
|
+
}
|
|
23
|
+
export async function fetchWithValidation(path, schema, options, init) {
|
|
24
|
+
const url = joinUrl(options.baseUrl, path);
|
|
25
|
+
const headers = new Headers(init?.headers);
|
|
26
|
+
if (init?.body !== undefined && !headers.has("Content-Type")) {
|
|
27
|
+
headers.set("Content-Type", "application/json");
|
|
28
|
+
}
|
|
29
|
+
const response = await options.fetcher(url, { ...init, headers });
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const body = await safeJson(response);
|
|
32
|
+
throw new VoyantApiError(extractErrorMessage(response.status, response.statusText, body), response.status, body);
|
|
33
|
+
}
|
|
34
|
+
if (response.status === 204)
|
|
35
|
+
return schema.parse(undefined);
|
|
36
|
+
const body = await safeJson(response);
|
|
37
|
+
const parsed = schema.safeParse(body);
|
|
38
|
+
if (!parsed.success) {
|
|
39
|
+
throw new VoyantApiError(`Voyant API response failed validation: ${parsed.error.message}`, response.status, body);
|
|
40
|
+
}
|
|
41
|
+
return parsed.data;
|
|
42
|
+
}
|
|
43
|
+
async function safeJson(response) {
|
|
44
|
+
const text = await response.text();
|
|
45
|
+
if (!text)
|
|
46
|
+
return undefined;
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return text;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function joinUrl(baseUrl, path) {
|
|
55
|
+
const trimmedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
56
|
+
const trimmedPath = path.startsWith("/") ? path : `/${path}`;
|
|
57
|
+
return `${trimmedBase}${trimmedPath}`;
|
|
58
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type KnownEntityType = "person" | "organization" | "supplier" | "booking" | "product";
|
|
2
|
+
export interface EntityRefPickerMessages {
|
|
3
|
+
entityTypeLabel: string;
|
|
4
|
+
entityLabel: string;
|
|
5
|
+
customEntityTypeLabel: string;
|
|
6
|
+
typePlaceholder: string;
|
|
7
|
+
entityPlaceholder: string;
|
|
8
|
+
entityTypeLabels: Record<KnownEntityType, string>;
|
|
9
|
+
}
|
|
10
|
+
export interface EntityRefPickerProps {
|
|
11
|
+
entityType: string;
|
|
12
|
+
entityId: string;
|
|
13
|
+
onChange: (scope: {
|
|
14
|
+
entityType: string;
|
|
15
|
+
entityId: string;
|
|
16
|
+
}) => void;
|
|
17
|
+
messages: EntityRefPickerMessages;
|
|
18
|
+
}
|
|
19
|
+
export declare function EntityRefPicker({ entityType, entityId, onChange, messages, }: EntityRefPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=entity-ref-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-ref-picker.d.ts","sourceRoot":"","sources":["../../../src/external-refs/components/entity-ref-picker.tsx"],"names":[],"mappings":"AAgBA,KAAK,eAAe,GAAG,QAAQ,GAAG,cAAc,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;AAWrF,MAAM,WAAW,uBAAuB;IACtC,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IACnE,QAAQ,EAAE,uBAAuB,CAAA;CAClC;AAMD,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,oBAAoB,2CA4EtB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { BookingCombobox } from "@voyant-travel/bookings-react/ui";
|
|
4
|
+
import { ProductCombobox } from "@voyant-travel/inventory-react/ui";
|
|
5
|
+
import { OrganizationCombobox, PersonCombobox } from "@voyant-travel/relationships-react/ui";
|
|
6
|
+
import { Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyant-travel/ui/components";
|
|
7
|
+
import { SupplierCombobox } from "../../suppliers/ui.js";
|
|
8
|
+
const KNOWN_ENTITY_TYPES = [
|
|
9
|
+
"person",
|
|
10
|
+
"organization",
|
|
11
|
+
"supplier",
|
|
12
|
+
"booking",
|
|
13
|
+
"product",
|
|
14
|
+
];
|
|
15
|
+
function isKnownEntityType(value) {
|
|
16
|
+
return KNOWN_ENTITY_TYPES.includes(value);
|
|
17
|
+
}
|
|
18
|
+
export function EntityRefPicker({ entityType, entityId, onChange, messages, }) {
|
|
19
|
+
const selectedType = isKnownEntityType(entityType) ? entityType : "custom";
|
|
20
|
+
const setEntityType = (nextType) => {
|
|
21
|
+
if (nextType === "custom") {
|
|
22
|
+
onChange({ entityType: "", entityId: "" });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
onChange({ entityType: nextType, entityId: "" });
|
|
26
|
+
};
|
|
27
|
+
const setEntityId = (nextId) => {
|
|
28
|
+
onChange({ entityType, entityId: nextId ?? "" });
|
|
29
|
+
};
|
|
30
|
+
const entityPicker = isKnownEntityType(entityType) ? (entityType === "person" ? (_jsx(PersonCombobox, { value: entityId || null, onChange: setEntityId })) : entityType === "organization" ? (_jsx(OrganizationCombobox, { value: entityId || null, onChange: setEntityId })) : entityType === "supplier" ? (_jsx(SupplierCombobox, { value: entityId || null, onChange: setEntityId })) : entityType === "booking" ? (_jsx(BookingCombobox, { value: entityId || null, onChange: setEntityId })) : (_jsx(ProductCombobox, { value: entityId || null, onChange: setEntityId }))) : (_jsx(Input, { value: entityId, onChange: (event) => onChange({ entityType, entityId: event.target.value }), placeholder: messages.entityPlaceholder }));
|
|
31
|
+
return (_jsxs("div", { className: "grid max-w-2xl grid-cols-1 gap-4 sm:grid-cols-2", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.entityTypeLabel }), _jsxs(Select, { items: [
|
|
32
|
+
...KNOWN_ENTITY_TYPES.map((type) => ({
|
|
33
|
+
label: messages.entityTypeLabels[type],
|
|
34
|
+
value: type,
|
|
35
|
+
})),
|
|
36
|
+
{ label: messages.customEntityTypeLabel, value: "custom" },
|
|
37
|
+
], value: selectedType, onValueChange: (value) => setEntityType(value ?? "custom"), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: messages.typePlaceholder }) }), _jsxs(SelectContent, { children: [KNOWN_ENTITY_TYPES.map((type) => (_jsx(SelectItem, { value: type, children: messages.entityTypeLabels[type] }, type))), _jsx(SelectItem, { value: "custom", children: messages.customEntityTypeLabel })] })] }), selectedType === "custom" ? (_jsx(Input, { value: entityType, onChange: (event) => onChange({ entityType: event.target.value, entityId }), placeholder: messages.typePlaceholder })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.entityLabel }), entityPicker] })] }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ExternalRefRecord } from "../index.js";
|
|
2
|
+
export interface ExternalRefDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
entityType: string;
|
|
6
|
+
entityId: string;
|
|
7
|
+
externalRef?: ExternalRefRecord;
|
|
8
|
+
onSuccess?: (externalRef: ExternalRefRecord) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function ExternalRefDialog({ open, onOpenChange, entityType, entityId, externalRef, onSuccess, }: ExternalRefDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=external-ref-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-ref-dialog.d.ts","sourceRoot":"","sources":["../../../src/external-refs/components/external-ref-dialog.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAEL,KAAK,iBAAiB,EAGvB,MAAM,aAAa,CAAA;AA+CpB,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,iBAAiB,KAAK,IAAI,CAAA;CACrD;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,WAAW,EACX,SAAS,GACV,EAAE,sBAAsB,2CAiMxB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea, } from "@voyant-travel/ui/components";
|
|
4
|
+
import { zodResolver } from "@voyant-travel/ui/lib/zod-resolver";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
import { useForm } from "react-hook-form";
|
|
8
|
+
import { z } from "zod/v4";
|
|
9
|
+
import { useExternalRefsUiI18nOrDefault } from "../i18n/provider.js";
|
|
10
|
+
import { useExternalRefMutation, } from "../index.js";
|
|
11
|
+
const REF_STATUSES = ["active", "inactive", "archived"];
|
|
12
|
+
function createFormSchema(messages) {
|
|
13
|
+
return z.object({
|
|
14
|
+
sourceSystem: z
|
|
15
|
+
.string()
|
|
16
|
+
.min(1, messages.externalRefDialog.validation.sourceSystemRequired)
|
|
17
|
+
.max(100),
|
|
18
|
+
objectType: z
|
|
19
|
+
.string()
|
|
20
|
+
.min(1, messages.externalRefDialog.validation.objectTypeRequired)
|
|
21
|
+
.max(100),
|
|
22
|
+
namespace: z.string().min(1).max(100),
|
|
23
|
+
externalId: z
|
|
24
|
+
.string()
|
|
25
|
+
.min(1, messages.externalRefDialog.validation.externalIdRequired)
|
|
26
|
+
.max(255),
|
|
27
|
+
externalParentId: z.string().optional().nullable(),
|
|
28
|
+
isPrimary: z.boolean(),
|
|
29
|
+
status: z.enum(REF_STATUSES),
|
|
30
|
+
metadataJson: z
|
|
31
|
+
.string()
|
|
32
|
+
.refine((value) => {
|
|
33
|
+
if (!value || value.trim() === "")
|
|
34
|
+
return true;
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(value);
|
|
37
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}, { message: messages.externalRefDialog.validation.metadataMustBeObject })
|
|
43
|
+
.optional()
|
|
44
|
+
.nullable(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function ExternalRefDialog({ open, onOpenChange, entityType, entityId, externalRef, onSuccess, }) {
|
|
48
|
+
const isEditing = Boolean(externalRef);
|
|
49
|
+
const { create, update } = useExternalRefMutation();
|
|
50
|
+
const { messages } = useExternalRefsUiI18nOrDefault();
|
|
51
|
+
const m = messages.externalRefDialog;
|
|
52
|
+
const formSchema = createFormSchema(messages);
|
|
53
|
+
const form = useForm({
|
|
54
|
+
resolver: zodResolver(formSchema),
|
|
55
|
+
defaultValues: {
|
|
56
|
+
sourceSystem: "",
|
|
57
|
+
objectType: "",
|
|
58
|
+
namespace: "default",
|
|
59
|
+
externalId: "",
|
|
60
|
+
externalParentId: "",
|
|
61
|
+
isPrimary: false,
|
|
62
|
+
status: "active",
|
|
63
|
+
metadataJson: "",
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (open && externalRef) {
|
|
68
|
+
form.reset({
|
|
69
|
+
sourceSystem: externalRef.sourceSystem,
|
|
70
|
+
objectType: externalRef.objectType,
|
|
71
|
+
namespace: externalRef.namespace,
|
|
72
|
+
externalId: externalRef.externalId,
|
|
73
|
+
externalParentId: externalRef.externalParentId ?? "",
|
|
74
|
+
isPrimary: externalRef.isPrimary,
|
|
75
|
+
status: externalRef.status,
|
|
76
|
+
metadataJson: externalRef.metadata ? JSON.stringify(externalRef.metadata, null, 2) : "",
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (open) {
|
|
81
|
+
form.reset({
|
|
82
|
+
sourceSystem: "",
|
|
83
|
+
objectType: "",
|
|
84
|
+
namespace: "default",
|
|
85
|
+
externalId: "",
|
|
86
|
+
externalParentId: "",
|
|
87
|
+
isPrimary: false,
|
|
88
|
+
status: "active",
|
|
89
|
+
metadataJson: "",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}, [externalRef, form, open]);
|
|
93
|
+
const onSubmit = async (values) => {
|
|
94
|
+
const metadata = values.metadataJson && values.metadataJson.trim() !== ""
|
|
95
|
+
? JSON.parse(values.metadataJson)
|
|
96
|
+
: null;
|
|
97
|
+
const payload = {
|
|
98
|
+
entityType,
|
|
99
|
+
entityId,
|
|
100
|
+
sourceSystem: values.sourceSystem,
|
|
101
|
+
objectType: values.objectType,
|
|
102
|
+
namespace: values.namespace,
|
|
103
|
+
externalId: values.externalId,
|
|
104
|
+
externalParentId: values.externalParentId || null,
|
|
105
|
+
isPrimary: values.isPrimary,
|
|
106
|
+
status: values.status,
|
|
107
|
+
metadata,
|
|
108
|
+
};
|
|
109
|
+
const saved = isEditing
|
|
110
|
+
? await update.mutateAsync({ id: externalRef.id, input: payload })
|
|
111
|
+
: await create.mutateAsync(payload);
|
|
112
|
+
onOpenChange(false);
|
|
113
|
+
onSuccess?.(saved);
|
|
114
|
+
};
|
|
115
|
+
const isSubmitting = form.formState.isSubmitting || create.isPending || update.isPending;
|
|
116
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? m.titles.edit : m.titles.add }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: m.labels.sourceSystem }), _jsx(Input, { ...form.register("sourceSystem"), placeholder: m.placeholders.sourceSystem }), form.formState.errors.sourceSystem ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.sourceSystem.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: m.labels.objectType }), _jsx(Input, { ...form.register("objectType"), placeholder: m.placeholders.objectType }), form.formState.errors.objectType ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.objectType.message })) : null] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: m.labels.namespace }), _jsx(Input, { ...form.register("namespace"), placeholder: m.placeholders.namespace })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: m.labels.externalId }), _jsx(Input, { ...form.register("externalId"), placeholder: m.placeholders.externalId }), form.formState.errors.externalId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.externalId.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: m.labels.externalParentId }), _jsx(Input, { ...form.register("externalParentId"), placeholder: m.placeholders.externalParentId })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: m.labels.status }), _jsxs(Select, { items: REF_STATUSES.map((status) => ({
|
|
117
|
+
label: messages.common.refStatusLabels[status],
|
|
118
|
+
value: status,
|
|
119
|
+
})), value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: REF_STATUSES.map((status) => (_jsx(SelectItem, { value: status, children: messages.common.refStatusLabels[status] }, status))) })] })] }), _jsxs("div", { className: "flex items-center gap-2 pt-6", children: [_jsx(Switch, { checked: form.watch("isPrimary"), onCheckedChange: (value) => form.setValue("isPrimary", value) }), _jsx(Label, { children: m.labels.primary })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: m.labels.metadataJson }), _jsx(Textarea, { ...form.register("metadataJson"), rows: 4, className: "font-mono text-xs", placeholder: m.placeholders.metadataJson }), form.formState.errors.metadataJson ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.metadataJson.message })) : null] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: m.actions.cancel }), _jsxs(Button, { type: "submit", disabled: isSubmitting, children: [isSubmitting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? m.actions.saveChanges : m.actions.addExternalRef] })] })] })] }) }));
|
|
120
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ExternalRefsPageProps {
|
|
2
|
+
entityType?: string;
|
|
3
|
+
entityId?: string;
|
|
4
|
+
onScopeChange?: (scope: {
|
|
5
|
+
entityType: string;
|
|
6
|
+
entityId: string;
|
|
7
|
+
}) => void;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function ExternalRefsPage({ entityType, entityId, onScopeChange, className, }?: ExternalRefsPageProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export interface ExternalRefsTabProps {
|
|
12
|
+
entityType: string;
|
|
13
|
+
entityId: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function ExternalRefsTab({ entityType, entityId }: ExternalRefsTabProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=external-refs-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-refs-page.d.ts","sourceRoot":"","sources":["../../../src/external-refs/components/external-refs-page.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IACzE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,QAAQ,EACR,aAAa,EACb,SAAS,GACV,GAAE,qBAA0B,2CAiD5B;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,eAAe,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,oBAAoB,2CAkK7E"}
|