@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,59 @@
|
|
|
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
|
+
}
|
|
37
|
+
const body = await safeJson(response);
|
|
38
|
+
const parsed = schema.safeParse(body);
|
|
39
|
+
if (!parsed.success) {
|
|
40
|
+
throw new VoyantApiError(`Voyant API response failed validation: ${parsed.error.message}`, response.status, body);
|
|
41
|
+
}
|
|
42
|
+
return parsed.data;
|
|
43
|
+
}
|
|
44
|
+
async function safeJson(response) {
|
|
45
|
+
const text = await response.text();
|
|
46
|
+
if (!text)
|
|
47
|
+
return undefined;
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(text);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return text;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function joinUrl(baseUrl, path) {
|
|
56
|
+
const trimmedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
57
|
+
const trimmedPath = path.startsWith("/") ? path : `/${path}`;
|
|
58
|
+
return `${trimmedBase}${trimmedPath}`;
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-format.d.ts","sourceRoot":"","sources":["../../../src/suppliers/components/message-format.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,UAEtF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SupplierRate } from "../index.js";
|
|
2
|
+
export type RateDialogProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
supplierId: string;
|
|
6
|
+
serviceId: string;
|
|
7
|
+
rate?: SupplierRate;
|
|
8
|
+
onSuccess?: (rate: SupplierRate) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function RateDialog({ open, onOpenChange, supplierId, serviceId, rate, onSuccess, }: RateDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=rate-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-dialog.d.ts","sourceRoot":"","sources":["../../../src/suppliers/components/rate-dialog.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAAc,KAAK,YAAY,EAA2B,MAAM,aAAa,CAAA;AAiBpF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;CACzC,CAAA;AAED,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,SAAS,EACT,IAAI,EACJ,SAAS,GACV,EAAE,eAAe,2CA0KjB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
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, Textarea, } from "@voyant-travel/ui/components";
|
|
4
|
+
import { CurrencyCombobox } from "@voyant-travel/ui/components/currency-combobox";
|
|
5
|
+
import { DatePicker } from "@voyant-travel/ui/components/date-picker";
|
|
6
|
+
import { zodResolver } from "@voyant-travel/ui/lib/zod-resolver";
|
|
7
|
+
import { Loader2 } from "lucide-react";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { useForm } from "react-hook-form";
|
|
10
|
+
import { z } from "zod/v4";
|
|
11
|
+
import { useSuppliersUiMessagesOrDefault } from "../i18n/index.js";
|
|
12
|
+
import { RATE_UNITS, useSupplierRateMutation } from "../index.js";
|
|
13
|
+
function getRateSchema(messages) {
|
|
14
|
+
const dialog = messages.dialogs.rate;
|
|
15
|
+
return z.object({
|
|
16
|
+
name: z.string().min(1, dialog.validationNameRequired),
|
|
17
|
+
currency: z.string().min(3, dialog.validationIsoCurrency).max(3, dialog.validationIsoCurrency),
|
|
18
|
+
amount: z.coerce.number().min(0, dialog.validationNonNegative),
|
|
19
|
+
unit: z.enum(["per_person", "per_group", "per_night", "per_vehicle", "flat"]),
|
|
20
|
+
validFrom: z.string().optional().nullable(),
|
|
21
|
+
validTo: z.string().optional().nullable(),
|
|
22
|
+
minPax: z.coerce.number().int().positive().optional().or(z.literal("")).nullable(),
|
|
23
|
+
maxPax: z.coerce.number().int().positive().optional().or(z.literal("")).nullable(),
|
|
24
|
+
notes: z.string().optional().nullable(),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function RateDialog({ open, onOpenChange, supplierId, serviceId, rate, onSuccess, }) {
|
|
28
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
29
|
+
const dialog = messages.dialogs.rate;
|
|
30
|
+
const schema = React.useMemo(() => getRateSchema(messages), [messages]);
|
|
31
|
+
const rateMutation = useSupplierRateMutation(supplierId);
|
|
32
|
+
const isEditing = !!rate;
|
|
33
|
+
const form = useForm({
|
|
34
|
+
resolver: zodResolver(schema),
|
|
35
|
+
defaultValues: {
|
|
36
|
+
name: "",
|
|
37
|
+
currency: "EUR",
|
|
38
|
+
amount: 0,
|
|
39
|
+
unit: "per_person",
|
|
40
|
+
validFrom: "",
|
|
41
|
+
validTo: "",
|
|
42
|
+
minPax: "",
|
|
43
|
+
maxPax: "",
|
|
44
|
+
notes: "",
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
if (!open)
|
|
49
|
+
return;
|
|
50
|
+
form.reset({
|
|
51
|
+
name: rate?.name ?? "",
|
|
52
|
+
currency: rate?.currency ?? "EUR",
|
|
53
|
+
amount: rate ? rate.amountCents / 100 : 0,
|
|
54
|
+
unit: rate?.unit ?? "per_person",
|
|
55
|
+
validFrom: rate?.validFrom ?? "",
|
|
56
|
+
validTo: rate?.validTo ?? "",
|
|
57
|
+
minPax: rate?.minPax ?? "",
|
|
58
|
+
maxPax: rate?.maxPax ?? "",
|
|
59
|
+
notes: rate?.notes ?? "",
|
|
60
|
+
});
|
|
61
|
+
}, [form, open, rate]);
|
|
62
|
+
async function onSubmit(values) {
|
|
63
|
+
const input = {
|
|
64
|
+
name: values.name,
|
|
65
|
+
currency: values.currency.toUpperCase(),
|
|
66
|
+
amountCents: Math.round(values.amount * 100),
|
|
67
|
+
unit: values.unit,
|
|
68
|
+
validFrom: values.validFrom || null,
|
|
69
|
+
validTo: values.validTo || null,
|
|
70
|
+
minPax: values.minPax && typeof values.minPax === "number" ? values.minPax : null,
|
|
71
|
+
maxPax: values.maxPax && typeof values.maxPax === "number" ? values.maxPax : null,
|
|
72
|
+
notes: values.notes || null,
|
|
73
|
+
};
|
|
74
|
+
const saved = isEditing
|
|
75
|
+
? await rateMutation.update.mutateAsync({ serviceId, rateId: rate.id, input })
|
|
76
|
+
: await rateMutation.create.mutateAsync({ serviceId, input });
|
|
77
|
+
onSuccess?.(saved);
|
|
78
|
+
onOpenChange(false);
|
|
79
|
+
}
|
|
80
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? dialog.editTitle : dialog.newTitle }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsx(Field, { label: dialog.seasonNameLabel, error: form.formState.errors.name?.message, children: _jsx(Input, { ...form.register("name"), placeholder: dialog.seasonNamePlaceholder }) }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsx(Field, { label: dialog.currencyLabel, error: form.formState.errors.currency?.message, children: _jsx(CurrencyCombobox, { value: form.watch("currency") || null, onChange: (next) => form.setValue("currency", next ?? "", {
|
|
81
|
+
shouldValidate: true,
|
|
82
|
+
shouldDirty: true,
|
|
83
|
+
}), placeholder: dialog.currencyPlaceholder }) }), _jsx(Field, { label: dialog.amountLabel, error: form.formState.errors.amount?.message, children: _jsx(Input, { ...form.register("amount"), type: "number", min: "0", step: "0.01", placeholder: dialog.amountPlaceholder }) }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: dialog.unitLabel }), _jsxs(Select, { value: form.watch("unit"), onValueChange: (value) => form.setValue("unit", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: RATE_UNITS.map((unit) => (_jsx(SelectItem, { value: unit.value, children: messages.common.rateUnitLabels[unit.value] }, unit.value))) })] })] })] }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsx(Field, { label: dialog.validFromLabel, children: _jsx(DatePicker, { value: form.watch("validFrom") || null, onChange: (nextValue) => form.setValue("validFrom", nextValue ?? "", {
|
|
84
|
+
shouldDirty: true,
|
|
85
|
+
shouldValidate: true,
|
|
86
|
+
}) }) }), _jsx(Field, { label: dialog.validToLabel, children: _jsx(DatePicker, { value: form.watch("validTo") || null, onChange: (nextValue) => form.setValue("validTo", nextValue ?? "", {
|
|
87
|
+
shouldDirty: true,
|
|
88
|
+
shouldValidate: true,
|
|
89
|
+
}) }) })] }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsx(Field, { label: dialog.minPaxLabel, children: _jsx(Input, { ...form.register("minPax"), type: "number", min: "1", placeholder: dialog.minPaxPlaceholder }) }), _jsx(Field, { label: dialog.maxPaxLabel, children: _jsx(Input, { ...form.register("maxPax"), type: "number", min: "1", placeholder: dialog.maxPaxPlaceholder }) })] }), _jsx(Field, { label: dialog.notesLabel, children: _jsx(Textarea, { ...form.register("notes"), placeholder: dialog.notesPlaceholder }) })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", disabled: form.formState.isSubmitting, children: [form.formState.isSubmitting && _jsx(Loader2, { className: "animate-spin" }), isEditing ? messages.common.save : messages.common.create] })] })] })] }) }));
|
|
90
|
+
}
|
|
91
|
+
function Field({ label, error, children, }) {
|
|
92
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: label }), children, error && _jsx("p", { className: "text-xs text-destructive", children: error })] }));
|
|
93
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type SupplierService } from "../index.js";
|
|
2
|
+
export type ServiceDialogProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
supplierId: string;
|
|
6
|
+
service?: SupplierService;
|
|
7
|
+
onSuccess?: (service: SupplierService) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function ServiceDialog({ open, onOpenChange, supplierId, service, onSuccess, }: ServiceDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=service-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-dialog.d.ts","sourceRoot":"","sources":["../../../src/suppliers/components/service-dialog.tsx"],"names":[],"mappings":"AA0BA,OAAO,EAAiB,KAAK,eAAe,EAA8B,MAAM,aAAa,CAAA;AAa7F,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;CAC/C,CAAA;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,OAAO,EACP,SAAS,GACV,EAAE,kBAAkB,2CAuHpB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
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 * as React from "react";
|
|
7
|
+
import { useForm } from "react-hook-form";
|
|
8
|
+
import { z } from "zod/v4";
|
|
9
|
+
import { useSuppliersUiMessagesOrDefault } from "../i18n/index.js";
|
|
10
|
+
import { SERVICE_TYPES, useSupplierServiceMutation } from "../index.js";
|
|
11
|
+
function getServiceSchema(messages) {
|
|
12
|
+
return z.object({
|
|
13
|
+
serviceType: z.enum(["accommodation", "transfer", "experience", "guide", "meal", "other"]),
|
|
14
|
+
name: z.string().min(1, messages.dialogs.service.validationNameRequired),
|
|
15
|
+
description: z.string().optional().nullable(),
|
|
16
|
+
duration: z.string().optional().nullable(),
|
|
17
|
+
capacity: z.coerce.number().int().positive().optional().or(z.literal("")).nullable(),
|
|
18
|
+
active: z.boolean().default(true),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function ServiceDialog({ open, onOpenChange, supplierId, service, onSuccess, }) {
|
|
22
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
23
|
+
const dialog = messages.dialogs.service;
|
|
24
|
+
const schema = React.useMemo(() => getServiceSchema(messages), [messages]);
|
|
25
|
+
const serviceMutation = useSupplierServiceMutation(supplierId);
|
|
26
|
+
const isEditing = !!service;
|
|
27
|
+
const form = useForm({
|
|
28
|
+
resolver: zodResolver(schema),
|
|
29
|
+
defaultValues: {
|
|
30
|
+
serviceType: "accommodation",
|
|
31
|
+
name: "",
|
|
32
|
+
description: "",
|
|
33
|
+
duration: "",
|
|
34
|
+
capacity: "",
|
|
35
|
+
active: true,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
if (!open)
|
|
40
|
+
return;
|
|
41
|
+
form.reset({
|
|
42
|
+
serviceType: service?.serviceType ?? "accommodation",
|
|
43
|
+
name: service?.name ?? "",
|
|
44
|
+
description: service?.description ?? "",
|
|
45
|
+
duration: service?.duration ?? "",
|
|
46
|
+
capacity: service?.capacity ?? "",
|
|
47
|
+
active: service?.active ?? true,
|
|
48
|
+
});
|
|
49
|
+
}, [form, open, service]);
|
|
50
|
+
async function onSubmit(values) {
|
|
51
|
+
const input = {
|
|
52
|
+
serviceType: values.serviceType,
|
|
53
|
+
name: values.name,
|
|
54
|
+
description: values.description || null,
|
|
55
|
+
duration: values.duration || null,
|
|
56
|
+
capacity: values.capacity && typeof values.capacity === "number" ? values.capacity : null,
|
|
57
|
+
active: values.active,
|
|
58
|
+
};
|
|
59
|
+
const saved = isEditing
|
|
60
|
+
? await serviceMutation.update.mutateAsync({ serviceId: service.id, input })
|
|
61
|
+
: await serviceMutation.create.mutateAsync(input);
|
|
62
|
+
onSuccess?.(saved);
|
|
63
|
+
onOpenChange(false);
|
|
64
|
+
}
|
|
65
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? dialog.editTitle : dialog.newTitle }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: dialog.serviceTypeLabel }), _jsxs(Select, { value: form.watch("serviceType"), onValueChange: (value) => form.setValue("serviceType", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: SERVICE_TYPES.map((type) => (_jsx(SelectItem, { value: type.value, children: messages.common.serviceTypeLabels[type.value] }, type.value))) })] })] }), _jsx(Field, { label: dialog.nameLabel, error: form.formState.errors.name?.message, children: _jsx(Input, { ...form.register("name"), placeholder: dialog.namePlaceholder }) }), _jsx(Field, { label: dialog.descriptionLabel, children: _jsx(Textarea, { ...form.register("description"), placeholder: dialog.descriptionPlaceholder }) }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsx(Field, { label: dialog.durationLabel, children: _jsx(Input, { ...form.register("duration"), placeholder: dialog.durationPlaceholder }) }), _jsx(Field, { label: dialog.capacityLabel, children: _jsx(Input, { ...form.register("capacity"), type: "number", min: "1", placeholder: dialog.capacityPlaceholder }) })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { checked: form.watch("active"), onCheckedChange: (value) => form.setValue("active", value) }), _jsx(Label, { children: dialog.activeLabel })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", disabled: form.formState.isSubmitting, children: [form.formState.isSubmitting && _jsx(Loader2, { className: "animate-spin" }), isEditing ? messages.common.save : messages.common.create] })] })] })] }) }));
|
|
66
|
+
}
|
|
67
|
+
function Field({ label, error, children, }) {
|
|
68
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: label }), children, error && _jsx("p", { className: "text-xs text-destructive", children: error })] }));
|
|
69
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SupplierComboboxProps {
|
|
2
|
+
value: string | null | undefined;
|
|
3
|
+
onChange: (value: string | null) => void;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
emptyText?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
triggerClassName?: string;
|
|
9
|
+
clearable?: boolean;
|
|
10
|
+
limit?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function SupplierCombobox({ value, onChange, placeholder, emptyText, disabled, className, triggerClassName, clearable, limit, }: SupplierComboboxProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=supplier-combobox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supplier-combobox.d.ts","sourceRoot":"","sources":["../../../src/suppliers/components/supplier-combobox.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAYD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,SAAgB,EAChB,KAAqB,GACtB,EAAE,qBAAqB,2CA6BvB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { AsyncCombobox } from "@voyant-travel/ui/components/async-combobox";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { useSuppliersUiMessagesOrDefault } from "../i18n/index.js";
|
|
6
|
+
import { useSupplier, useSuppliers } from "../index.js";
|
|
7
|
+
const DEFAULT_LIMIT = 20;
|
|
8
|
+
function compact(parts) {
|
|
9
|
+
return parts.map((part) => part?.trim()).filter(Boolean);
|
|
10
|
+
}
|
|
11
|
+
function formatSupplierSecondary(supplier) {
|
|
12
|
+
return compact([supplier.city, supplier.country, supplier.defaultCurrency]).join(" - ");
|
|
13
|
+
}
|
|
14
|
+
export function SupplierCombobox({ value, onChange, placeholder, emptyText, disabled, className, triggerClassName, clearable = true, limit = DEFAULT_LIMIT, }) {
|
|
15
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
16
|
+
const comboboxMessages = messages.supplierCombobox;
|
|
17
|
+
const [search, setSearch] = React.useState("");
|
|
18
|
+
const listQuery = useSuppliers({ search: search || undefined, limit });
|
|
19
|
+
const selectedQuery = useSupplier(value ?? "", { enabled: Boolean(value) });
|
|
20
|
+
return (_jsx(AsyncCombobox, { value: value ?? null, onChange: onChange, items: listQuery.data?.data ?? [], selectedItem: selectedQuery.data?.data ?? null, getKey: (supplier) => supplier.id, getLabel: (supplier) => supplier.name, getSecondary: formatSupplierSecondary, onSearchChange: setSearch, placeholder: placeholder ?? comboboxMessages.placeholder, emptyText: listQuery.isPending || selectedQuery.isPending
|
|
21
|
+
? comboboxMessages.loading
|
|
22
|
+
: (emptyText ?? comboboxMessages.empty), disabled: disabled, className: className, triggerClassName: triggerClassName, clearable: clearable }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type Supplier, type UpdateSupplierInput } from "../index.js";
|
|
3
|
+
export type SupplierDetailPageProps = {
|
|
4
|
+
id: string;
|
|
5
|
+
locale?: string;
|
|
6
|
+
onBack?: () => void;
|
|
7
|
+
onDeleted?: () => void;
|
|
8
|
+
confirmAction?: (message: string) => boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
renderCustomerPaymentPolicy?: (args: {
|
|
11
|
+
supplier: Supplier;
|
|
12
|
+
updateSupplier: (input: UpdateSupplierInput) => Promise<Supplier>;
|
|
13
|
+
isUpdating: boolean;
|
|
14
|
+
}) => React.ReactNode;
|
|
15
|
+
};
|
|
16
|
+
export declare function SupplierDetailPage({ id, locale, onBack, onDeleted, confirmAction, className, renderCustomerPaymentPolicy, }: SupplierDetailPageProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=supplier-detail-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supplier-detail-page.d.ts","sourceRoot":"","sources":["../../../src/suppliers/components/supplier-detail-page.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EACL,KAAK,QAAQ,EAIb,KAAK,mBAAmB,EAQzB,MAAM,aAAa,CAAA;AAMpB,MAAM,MAAM,uBAAuB,GAAG;IACpC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAA;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2BAA2B,CAAC,EAAE,CAAC,IAAI,EAAE;QACnC,QAAQ,EAAE,QAAQ,CAAA;QAClB,cAAc,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;QACjE,UAAU,EAAE,OAAO,CAAA;KACpB,KAAK,KAAK,CAAC,SAAS,CAAA;CACtB,CAAA;AAED,wBAAgB,kBAAkB,CAAC,EACjC,EAAE,EACF,MAAgB,EAChB,MAAM,EACN,SAAS,EACT,aAAkE,EAClE,SAAS,EACT,2BAA2B,GAC5B,EAAE,uBAAuB,2CA6SzB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Textarea, } from "@voyant-travel/ui/components";
|
|
4
|
+
import { cn } from "@voyant-travel/ui/lib/utils";
|
|
5
|
+
import { ArrowLeft, Loader2, Pencil, Plus, Trash2 } from "lucide-react";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { useSuppliersUiMessagesOrDefault } from "../i18n/index.js";
|
|
8
|
+
import { statusVariant, useSupplier, useSupplierMutation, useSupplierNoteMutation, useSupplierNotes, useSupplierRateMutation, useSupplierServiceMutation, useSupplierServices, } from "../index.js";
|
|
9
|
+
import { RateDialog } from "./rate-dialog.js";
|
|
10
|
+
import { ServiceDialog } from "./service-dialog.js";
|
|
11
|
+
import { SupplierDialog } from "./supplier-dialog.js";
|
|
12
|
+
import { SupplierServiceRow } from "./supplier-service-row.js";
|
|
13
|
+
export function SupplierDetailPage({ id, locale = "en-US", onBack, onDeleted, confirmAction = (message) => globalThis.confirm?.(message) ?? true, className, renderCustomerPaymentPolicy, }) {
|
|
14
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
15
|
+
const detail = messages.supplierDetailPage;
|
|
16
|
+
const supplierQuery = useSupplier(id);
|
|
17
|
+
const servicesQuery = useSupplierServices(id);
|
|
18
|
+
const notesQuery = useSupplierNotes(id);
|
|
19
|
+
const supplierMutation = useSupplierMutation();
|
|
20
|
+
const serviceMutation = useSupplierServiceMutation(id);
|
|
21
|
+
const rateMutation = useSupplierRateMutation(id);
|
|
22
|
+
const noteMutation = useSupplierNoteMutation(id);
|
|
23
|
+
const [editOpen, setEditOpen] = React.useState(false);
|
|
24
|
+
const [serviceDialogOpen, setServiceDialogOpen] = React.useState(false);
|
|
25
|
+
const [editingService, setEditingService] = React.useState();
|
|
26
|
+
const [rateDialog, setRateDialog] = React.useState({ open: false, serviceId: "" });
|
|
27
|
+
const [expandedServiceId, setExpandedServiceId] = React.useState(null);
|
|
28
|
+
const [noteContent, setNoteContent] = React.useState("");
|
|
29
|
+
const supplier = supplierQuery.data?.data;
|
|
30
|
+
async function deleteSupplier() {
|
|
31
|
+
if (!supplier || !confirmAction(detail.deleteSupplierConfirm))
|
|
32
|
+
return;
|
|
33
|
+
await supplierMutation.remove.mutateAsync(supplier.id);
|
|
34
|
+
onDeleted?.();
|
|
35
|
+
}
|
|
36
|
+
async function deleteService(serviceId) {
|
|
37
|
+
if (!confirmAction(detail.deleteServiceConfirm))
|
|
38
|
+
return;
|
|
39
|
+
await serviceMutation.remove.mutateAsync(serviceId);
|
|
40
|
+
if (expandedServiceId === serviceId)
|
|
41
|
+
setExpandedServiceId(null);
|
|
42
|
+
}
|
|
43
|
+
async function deleteRate(serviceId, rateId) {
|
|
44
|
+
if (!confirmAction(detail.deleteRateConfirm))
|
|
45
|
+
return;
|
|
46
|
+
await rateMutation.remove.mutateAsync({ serviceId, rateId });
|
|
47
|
+
}
|
|
48
|
+
async function addNote() {
|
|
49
|
+
const content = noteContent.trim();
|
|
50
|
+
if (!content)
|
|
51
|
+
return;
|
|
52
|
+
await noteMutation.create.mutateAsync({ content });
|
|
53
|
+
setNoteContent("");
|
|
54
|
+
}
|
|
55
|
+
if (supplierQuery.isPending)
|
|
56
|
+
return _jsx(SupplierDetailSkeleton, { className: className });
|
|
57
|
+
if (supplierQuery.isError) {
|
|
58
|
+
return (_jsx(EmptyState, { message: detail.loadFailed, onBack: onBack, backLabel: detail.backToSuppliers, className: className }));
|
|
59
|
+
}
|
|
60
|
+
if (!supplier) {
|
|
61
|
+
return (_jsx(EmptyState, { message: detail.notFound, onBack: onBack, backLabel: detail.backToSuppliers, className: className }));
|
|
62
|
+
}
|
|
63
|
+
const services = servicesQuery.data?.data ?? [];
|
|
64
|
+
const notes = notesQuery.data?.data ?? [];
|
|
65
|
+
return (_jsxs("div", { "data-slot": "supplier-detail-page", className: cn("flex flex-col gap-6 p-6", className), children: [_jsxs("div", { className: "flex flex-col gap-4 md:flex-row md:items-start md:justify-between", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [onBack && (_jsxs(Button, { type: "button", variant: "ghost", className: "w-fit px-0", onClick: onBack, children: [_jsx(ArrowLeft, {}), detail.backToSuppliers] })), _jsxs("div", { children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("h1", { className: "text-3xl font-semibold tracking-tight", children: supplier.name }), _jsx(Badge, { variant: statusVariant[supplier.status], children: messages.common.supplierStatusLabels[supplier.status] })] }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: supplier.description })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { type: "button", variant: "outline", onClick: () => setEditOpen(true), children: [_jsx(Pencil, {}), messages.common.edit] }), _jsxs(Button, { type: "button", variant: "destructive", onClick: deleteSupplier, disabled: supplierMutation.remove.isPending, children: [_jsx(Trash2, {}), messages.common.delete] })] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: detail.details }) }), _jsxs(CardContent, { className: "grid gap-3 text-sm", children: [_jsx(Detail, { label: detail.labels.type, children: messages.common.supplierTypeLabels[supplier.type] }), _jsx(Detail, { label: detail.labels.status, children: messages.common.supplierStatusLabels[supplier.status] }), _jsx(Detail, { label: detail.labels.city, children: supplier.city ?? messages.common.none }), _jsx(Detail, { label: detail.labels.country, children: supplier.country ?? messages.common.none }), _jsx(Detail, { label: detail.labels.currency, children: supplier.defaultCurrency ?? messages.common.none }), _jsx(Detail, { label: detail.labels.reservationTimeout, children: supplier.reservationTimeoutMinutes == null
|
|
66
|
+
? messages.common.none
|
|
67
|
+
: String(supplier.reservationTimeoutMinutes) }), _jsx(Detail, { label: detail.labels.created, children: formatDate(supplier.createdAt, locale) }), _jsx(Detail, { label: detail.labels.updated, children: formatDate(supplier.updatedAt, locale) })] })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: detail.contact }) }), _jsx(CardContent, { className: "grid gap-3 text-sm", children: !hasContactDetails(supplier) ? (_jsx("p", { className: "text-muted-foreground", children: detail.noContact })) : (_jsxs(_Fragment, { children: [_jsx(Detail, { label: detail.labels.email, children: supplier.email ?? messages.common.none }), _jsx(Detail, { label: detail.labels.phone, children: supplier.phone ?? messages.common.none }), _jsx(Detail, { label: detail.labels.website, children: supplier.website ? (_jsx("a", { href: supplier.website, className: "text-primary underline-offset-4 hover:underline", children: supplier.website })) : (messages.common.none) }), _jsx(Detail, { label: detail.labels.address, children: supplier.address ?? messages.common.none }), _jsx(Detail, { label: detail.labels.contactName, children: supplier.contactName ?? messages.common.none }), _jsx(Detail, { label: detail.labels.contactEmail, children: supplier.contactEmail ?? messages.common.none }), _jsx(Detail, { label: detail.labels.contactPhone, children: supplier.contactPhone ?? messages.common.none })] })) })] })] }), renderCustomerPaymentPolicy?.({
|
|
68
|
+
supplier,
|
|
69
|
+
updateSupplier: (input) => supplierMutation.update.mutateAsync({ id: supplier.id, input }),
|
|
70
|
+
isUpdating: supplierMutation.update.isPending,
|
|
71
|
+
}), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between gap-4", children: [_jsx(CardTitle, { children: detail.services }), _jsxs(Button, { type: "button", onClick: () => {
|
|
72
|
+
setEditingService(undefined);
|
|
73
|
+
setServiceDialogOpen(true);
|
|
74
|
+
}, children: [_jsx(Plus, {}), detail.addService] })] }), _jsx(CardContent, { className: "flex flex-col gap-3", children: servicesQuery.isPending ? (_jsx(LoadingLine, {})) : services.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: detail.noServices })) : (services.map((service) => (_jsx(SupplierServiceRow, { service: service, supplierId: supplier.id, expanded: expandedServiceId === service.id, onToggle: () => setExpandedServiceId((current) => (current === service.id ? null : service.id)), onEdit: () => {
|
|
75
|
+
setEditingService(service);
|
|
76
|
+
setServiceDialogOpen(true);
|
|
77
|
+
}, onDelete: () => void deleteService(service.id), onAddRate: () => setRateDialog({ open: true, serviceId: service.id }), onEditRate: (rate) => setRateDialog({ open: true, serviceId: service.id, rate }), onDeleteRate: (rateId) => void deleteRate(service.id, rateId) }, service.id)))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: detail.notes }) }), _jsxs(CardContent, { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Textarea, { value: noteContent, onChange: (event) => setNoteContent(event.target.value), placeholder: detail.notePlaceholder }), _jsxs(Button, { type: "button", className: "w-fit", onClick: () => void addNote(), disabled: !noteContent.trim() || noteMutation.create.isPending, children: [noteMutation.create.isPending && _jsx(Loader2, { className: "animate-spin" }), detail.addNote] })] }), notesQuery.isPending ? (_jsx(LoadingLine, {})) : notes.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: detail.noNotes })) : (_jsx("div", { className: "flex flex-col gap-3", children: notes.map((note) => (_jsxs("div", { className: "rounded-md border p-3", children: [_jsx("p", { className: "whitespace-pre-wrap text-sm", children: note.content }), _jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: formatDate(note.createdAt, locale) })] }, note.id))) }))] })] }), _jsx(SupplierDialog, { open: editOpen, onOpenChange: setEditOpen, supplier: supplier, onSuccess: () => setEditOpen(false) }), _jsx(ServiceDialog, { open: serviceDialogOpen, onOpenChange: setServiceDialogOpen, supplierId: supplier.id, service: editingService, onSuccess: () => {
|
|
78
|
+
setServiceDialogOpen(false);
|
|
79
|
+
setEditingService(undefined);
|
|
80
|
+
} }), _jsx(RateDialog, { open: rateDialog.open, onOpenChange: (open) => setRateDialog((current) => ({ ...current, open })), supplierId: supplier.id, serviceId: rateDialog.serviceId, rate: rateDialog.rate, onSuccess: () => setRateDialog({ open: false, serviceId: "" }) })] }));
|
|
81
|
+
}
|
|
82
|
+
function Detail({ label, children }) {
|
|
83
|
+
return (_jsxs("div", { className: "grid grid-cols-[10rem_minmax(0,1fr)] gap-3", children: [_jsx("span", { className: "text-muted-foreground", children: label }), _jsx("span", { className: "min-w-0 break-words", children: children })] }));
|
|
84
|
+
}
|
|
85
|
+
function EmptyState({ message, onBack, backLabel, className, }) {
|
|
86
|
+
return (_jsxs("div", { className: cn("flex min-h-80 flex-col items-center justify-center gap-4 p-6 text-center", className), children: [_jsx("p", { className: "text-muted-foreground", children: message }), onBack && (_jsxs(Button, { type: "button", variant: "outline", onClick: onBack, children: [_jsx(ArrowLeft, {}), backLabel] }))] }));
|
|
87
|
+
}
|
|
88
|
+
function SupplierDetailSkeleton({ className }) {
|
|
89
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-6 p-6", className), children: [_jsx("div", { className: "h-9 w-72 animate-pulse rounded bg-muted" }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsx("div", { className: "h-64 animate-pulse rounded-md bg-muted" }), _jsx("div", { className: "h-64 animate-pulse rounded-md bg-muted" })] }), _jsx("div", { className: "h-96 animate-pulse rounded-md bg-muted" })] }));
|
|
90
|
+
}
|
|
91
|
+
function LoadingLine() {
|
|
92
|
+
return _jsx("div", { className: "h-4 w-40 animate-pulse rounded bg-muted" });
|
|
93
|
+
}
|
|
94
|
+
function hasContactDetails(supplier) {
|
|
95
|
+
return Boolean(supplier.email ||
|
|
96
|
+
supplier.phone ||
|
|
97
|
+
supplier.website ||
|
|
98
|
+
supplier.address ||
|
|
99
|
+
supplier.contactName ||
|
|
100
|
+
supplier.contactEmail ||
|
|
101
|
+
supplier.contactPhone);
|
|
102
|
+
}
|
|
103
|
+
function formatDate(value, locale) {
|
|
104
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }).format(new Date(value));
|
|
105
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Supplier } from "../index.js";
|
|
2
|
+
export type SupplierDialogProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
supplier?: Supplier;
|
|
6
|
+
onSuccess?: (supplier: Supplier) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function SupplierDialog({ open, onOpenChange, supplier, onSuccess }: SupplierDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=supplier-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supplier-dialog.d.ts","sourceRoot":"","sources":["../../../src/suppliers/components/supplier-dialog.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAAqC,KAAK,QAAQ,EAAuB,MAAM,aAAa,CAAA;AA0BnG,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAA;CACzC,CAAA;AAED,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CA6O9F"}
|
|
@@ -0,0 +1,118 @@
|
|
|
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, Textarea, } from "@voyant-travel/ui/components";
|
|
4
|
+
import { CountryCombobox } from "@voyant-travel/ui/components/country-combobox";
|
|
5
|
+
import { CurrencyCombobox } from "@voyant-travel/ui/components/currency-combobox";
|
|
6
|
+
import { zodResolver } from "@voyant-travel/ui/lib/zod-resolver";
|
|
7
|
+
import { Loader2 } from "lucide-react";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { useForm } from "react-hook-form";
|
|
10
|
+
import { z } from "zod/v4";
|
|
11
|
+
import { useSuppliersUiMessagesOrDefault } from "../i18n/index.js";
|
|
12
|
+
import { SUPPLIER_STATUSES, SUPPLIER_TYPES, useSupplierMutation } from "../index.js";
|
|
13
|
+
function getSupplierSchema(messages) {
|
|
14
|
+
const dialog = messages.dialogs.supplier;
|
|
15
|
+
return z.object({
|
|
16
|
+
name: z.string().min(1, dialog.validationNameRequired),
|
|
17
|
+
type: z.enum(["hotel", "transfer", "guide", "experience", "airline", "restaurant", "other"]),
|
|
18
|
+
status: z.enum(["active", "inactive", "pending"]),
|
|
19
|
+
description: z.string().optional().nullable(),
|
|
20
|
+
email: z.string().email().optional().or(z.literal("")).nullable(),
|
|
21
|
+
phone: z.string().optional().nullable(),
|
|
22
|
+
website: z.string().url().optional().or(z.literal("")).nullable(),
|
|
23
|
+
address: z.string().optional().nullable(),
|
|
24
|
+
city: z.string().optional().nullable(),
|
|
25
|
+
country: z.string().optional().nullable(),
|
|
26
|
+
defaultCurrency: z.string().max(3, dialog.validationIsoCurrency).optional().nullable(),
|
|
27
|
+
reservationTimeoutMinutes: z
|
|
28
|
+
.union([z.literal(""), z.coerce.number().int().min(0, dialog.validationReservationTimeout)])
|
|
29
|
+
.optional()
|
|
30
|
+
.nullable(),
|
|
31
|
+
contactName: z.string().optional().nullable(),
|
|
32
|
+
contactEmail: z.string().email().optional().or(z.literal("")).nullable(),
|
|
33
|
+
contactPhone: z.string().optional().nullable(),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function SupplierDialog({ open, onOpenChange, supplier, onSuccess }) {
|
|
37
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
38
|
+
const dialog = messages.dialogs.supplier;
|
|
39
|
+
const schema = React.useMemo(() => getSupplierSchema(messages), [messages]);
|
|
40
|
+
const supplierMutation = useSupplierMutation();
|
|
41
|
+
const isEditing = !!supplier;
|
|
42
|
+
const form = useForm({
|
|
43
|
+
resolver: zodResolver(schema),
|
|
44
|
+
defaultValues: {
|
|
45
|
+
name: "",
|
|
46
|
+
type: "hotel",
|
|
47
|
+
status: "active",
|
|
48
|
+
description: "",
|
|
49
|
+
email: "",
|
|
50
|
+
phone: "",
|
|
51
|
+
website: "",
|
|
52
|
+
address: "",
|
|
53
|
+
city: "",
|
|
54
|
+
country: "",
|
|
55
|
+
defaultCurrency: "",
|
|
56
|
+
reservationTimeoutMinutes: "",
|
|
57
|
+
contactName: "",
|
|
58
|
+
contactEmail: "",
|
|
59
|
+
contactPhone: "",
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
if (!open)
|
|
64
|
+
return;
|
|
65
|
+
form.reset({
|
|
66
|
+
name: supplier?.name ?? "",
|
|
67
|
+
type: supplier?.type ?? "hotel",
|
|
68
|
+
status: supplier?.status ?? "active",
|
|
69
|
+
description: supplier?.description ?? "",
|
|
70
|
+
email: supplier?.email ?? "",
|
|
71
|
+
phone: supplier?.phone ?? "",
|
|
72
|
+
website: supplier?.website ?? "",
|
|
73
|
+
address: supplier?.address ?? "",
|
|
74
|
+
city: supplier?.city ?? "",
|
|
75
|
+
country: supplier?.country ?? "",
|
|
76
|
+
defaultCurrency: supplier?.defaultCurrency ?? "",
|
|
77
|
+
reservationTimeoutMinutes: supplier?.reservationTimeoutMinutes == null
|
|
78
|
+
? ""
|
|
79
|
+
: String(supplier.reservationTimeoutMinutes),
|
|
80
|
+
contactName: supplier?.contactName ?? "",
|
|
81
|
+
contactEmail: supplier?.contactEmail ?? "",
|
|
82
|
+
contactPhone: supplier?.contactPhone ?? "",
|
|
83
|
+
});
|
|
84
|
+
}, [form, open, supplier]);
|
|
85
|
+
async function onSubmit(values) {
|
|
86
|
+
const input = {
|
|
87
|
+
...values,
|
|
88
|
+
description: values.description || null,
|
|
89
|
+
email: values.email || null,
|
|
90
|
+
phone: values.phone || null,
|
|
91
|
+
website: values.website || null,
|
|
92
|
+
address: values.address || null,
|
|
93
|
+
city: values.city || null,
|
|
94
|
+
country: values.country || null,
|
|
95
|
+
defaultCurrency: values.defaultCurrency || null,
|
|
96
|
+
reservationTimeoutMinutes: values.reservationTimeoutMinutes === "" ||
|
|
97
|
+
values.reservationTimeoutMinutes === null ||
|
|
98
|
+
values.reservationTimeoutMinutes === undefined
|
|
99
|
+
? null
|
|
100
|
+
: values.reservationTimeoutMinutes,
|
|
101
|
+
contactName: values.contactName || null,
|
|
102
|
+
contactEmail: values.contactEmail || null,
|
|
103
|
+
contactPhone: values.contactPhone || null,
|
|
104
|
+
};
|
|
105
|
+
const saved = isEditing
|
|
106
|
+
? await supplierMutation.update.mutateAsync({ id: supplier.id, input })
|
|
107
|
+
: await supplierMutation.create.mutateAsync(input);
|
|
108
|
+
onSuccess?.(saved);
|
|
109
|
+
onOpenChange(false);
|
|
110
|
+
}
|
|
111
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? dialog.editTitle : dialog.newTitle }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: dialog.typeLabel }), _jsxs(Select, { value: form.watch("type"), onValueChange: (value) => form.setValue("type", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: SUPPLIER_TYPES.map((type) => (_jsx(SelectItem, { value: type.value, children: messages.common.supplierTypeLabels[type.value] }, type.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: dialog.statusLabel }), _jsxs(Select, { value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: SUPPLIER_STATUSES.map((status) => (_jsx(SelectItem, { value: status.value, children: messages.common.supplierStatusLabels[status.value] }, status.value))) })] })] })] }), _jsx(Field, { label: dialog.nameLabel, error: form.formState.errors.name?.message, children: _jsx(Input, { ...form.register("name"), placeholder: dialog.namePlaceholder }) }), _jsx(Field, { label: dialog.descriptionLabel, children: _jsx(Textarea, { ...form.register("description"), placeholder: dialog.descriptionPlaceholder }) }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsx(Field, { label: dialog.emailLabel, error: form.formState.errors.email?.message, children: _jsx(Input, { ...form.register("email"), type: "email", placeholder: dialog.emailPlaceholder }) }), _jsx(Field, { label: dialog.phoneLabel, children: _jsx(Input, { ...form.register("phone"), placeholder: dialog.phonePlaceholder }) })] }), _jsx(Field, { label: dialog.websiteLabel, error: form.formState.errors.website?.message, children: _jsx(Input, { ...form.register("website"), placeholder: dialog.websitePlaceholder }) }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsx(Field, { label: dialog.cityLabel, children: _jsx(Input, { ...form.register("city"), placeholder: dialog.cityPlaceholder }) }), _jsx(Field, { label: dialog.countryLabel, children: _jsx(CountryCombobox, { value: form.watch("country"), onChange: (value) => form.setValue("country", value ?? ""), placeholder: dialog.countryPlaceholder }) })] }), _jsx(Field, { label: dialog.addressLabel, children: _jsx(Textarea, { ...form.register("address"), placeholder: dialog.addressPlaceholder }) }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsx(Field, { label: dialog.defaultCurrencyLabel, error: form.formState.errors.defaultCurrency?.message, children: _jsx(CurrencyCombobox, { value: form.watch("defaultCurrency") || null, onChange: (next) => form.setValue("defaultCurrency", next ?? "", {
|
|
112
|
+
shouldValidate: true,
|
|
113
|
+
shouldDirty: true,
|
|
114
|
+
}), placeholder: dialog.defaultCurrencyPlaceholder }) }), _jsx(Field, { label: dialog.reservationTimeoutLabel, error: form.formState.errors.reservationTimeoutMinutes?.message, children: _jsx(Input, { ...form.register("reservationTimeoutMinutes"), type: "number", min: "0", placeholder: dialog.reservationTimeoutPlaceholder }) })] }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsx(Field, { label: dialog.contactNameLabel, children: _jsx(Input, { ...form.register("contactName"), placeholder: dialog.contactNamePlaceholder }) }), _jsx(Field, { label: dialog.contactEmailLabel, error: form.formState.errors.contactEmail?.message, children: _jsx(Input, { ...form.register("contactEmail"), type: "email", placeholder: dialog.contactEmailPlaceholder }) }), _jsx(Field, { label: dialog.contactPhoneLabel, children: _jsx(Input, { ...form.register("contactPhone"), placeholder: dialog.contactPhonePlaceholder }) })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", disabled: form.formState.isSubmitting, children: [form.formState.isSubmitting && _jsx(Loader2, { className: "animate-spin" }), isEditing ? messages.common.save : messages.common.create] })] })] })] }) }));
|
|
115
|
+
}
|
|
116
|
+
function Field({ label, error, children, }) {
|
|
117
|
+
return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: label }), children, error && _jsx("p", { className: "text-xs text-destructive", children: error })] }));
|
|
118
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SupplierRate, type SupplierService } from "../index.js";
|
|
2
|
+
export declare function SupplierServiceRow({ service, supplierId, rates, expanded, onToggle, onEdit, onDelete, onAddRate, onEditRate, onDeleteRate, }: {
|
|
3
|
+
service: SupplierService;
|
|
4
|
+
/**
|
|
5
|
+
* Optional. When set (and `rates` is not), the row fetches rates lazily on
|
|
6
|
+
* expand via `useSupplierServiceRates`.
|
|
7
|
+
*/
|
|
8
|
+
supplierId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional. When provided, the caller owns rate fetching. When omitted and
|
|
11
|
+
* `supplierId` is set, rates are fetched internally on expand.
|
|
12
|
+
*/
|
|
13
|
+
rates?: SupplierRate[];
|
|
14
|
+
expanded: boolean;
|
|
15
|
+
onToggle: () => void;
|
|
16
|
+
onEdit: () => void;
|
|
17
|
+
onDelete: () => void;
|
|
18
|
+
onAddRate: () => void;
|
|
19
|
+
onEditRate: (rate: SupplierRate) => void;
|
|
20
|
+
onDeleteRate: (rateId: string) => void;
|
|
21
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=supplier-service-row.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supplier-service-row.d.ts","sourceRoot":"","sources":["../../../src/suppliers/components/supplier-service-row.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAA2B,MAAM,aAAa,CAAA;AAE9F,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,UAAU,EACV,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,SAAS,EACT,UAAU,EACV,YAAY,GACb,EAAE;IACD,OAAO,EAAE,eAAe,CAAA;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,YAAY,EAAE,CAAA;IACtB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;IACxC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC,2CA0JA"}
|