@voyantjs/suppliers-ui 0.30.7 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/message-format.d.ts +2 -0
- package/dist/components/message-format.d.ts.map +1 -0
- package/dist/components/message-format.js +3 -0
- package/dist/components/rate-dialog.d.ts +11 -0
- package/dist/components/rate-dialog.d.ts.map +1 -0
- package/dist/components/rate-dialog.js +82 -0
- package/dist/components/service-dialog.d.ts +10 -0
- package/dist/components/service-dialog.d.ts.map +1 -0
- package/dist/components/service-dialog.js +69 -0
- package/dist/components/supplier-detail-page.d.ts +16 -0
- package/dist/components/supplier-detail-page.d.ts.map +1 -0
- package/dist/components/supplier-detail-page.js +104 -0
- package/dist/components/supplier-dialog.d.ts +9 -0
- package/dist/components/supplier-dialog.d.ts.map +1 -0
- package/dist/components/supplier-dialog.js +114 -0
- package/dist/components/suppliers-page.d.ts +8 -10
- package/dist/components/suppliers-page.d.ts.map +1 -1
- package/dist/components/suppliers-page.js +77 -43
- package/dist/i18n/en.d.ts +125 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +125 -0
- package/dist/i18n/messages.d.ts +128 -0
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/i18n/provider.d.ts +250 -0
- package/dist/i18n/provider.d.ts.map +1 -1
- package/dist/i18n/ro.d.ts +125 -0
- package/dist/i18n/ro.d.ts.map +1 -1
- package/dist/i18n/ro.js +125 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/package.json +12 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-format.d.ts","sourceRoot":"","sources":["../../src/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 "@voyantjs/suppliers-react";
|
|
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/components/rate-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,YAAY,EAA2B,MAAM,2BAA2B,CAAA;AAwClG,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,2CAsJjB"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { RATE_UNITS, useSupplierRateMutation } from "@voyantjs/suppliers-react";
|
|
4
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, } from "@voyantjs/ui/components";
|
|
5
|
+
import { zodResolver } from "@voyantjs/ui/lib/zod-resolver";
|
|
6
|
+
import { Loader2 } from "lucide-react";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { useForm } from "react-hook-form";
|
|
9
|
+
import { z } from "zod/v4";
|
|
10
|
+
import { useSuppliersUiMessagesOrDefault } from "../i18n/index.js";
|
|
11
|
+
function getRateSchema(messages) {
|
|
12
|
+
const dialog = messages.dialogs.rate;
|
|
13
|
+
return z.object({
|
|
14
|
+
name: z.string().min(1, dialog.validationNameRequired),
|
|
15
|
+
currency: z.string().min(3, dialog.validationIsoCurrency).max(3, dialog.validationIsoCurrency),
|
|
16
|
+
amount: z.coerce.number().min(0, dialog.validationNonNegative),
|
|
17
|
+
unit: z.enum(["per_person", "per_group", "per_night", "per_vehicle", "flat"]),
|
|
18
|
+
validFrom: z.string().optional().nullable(),
|
|
19
|
+
validTo: z.string().optional().nullable(),
|
|
20
|
+
minPax: z.coerce.number().int().positive().optional().or(z.literal("")).nullable(),
|
|
21
|
+
maxPax: z.coerce.number().int().positive().optional().or(z.literal("")).nullable(),
|
|
22
|
+
notes: z.string().optional().nullable(),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function RateDialog({ open, onOpenChange, supplierId, serviceId, rate, onSuccess, }) {
|
|
26
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
27
|
+
const dialog = messages.dialogs.rate;
|
|
28
|
+
const schema = React.useMemo(() => getRateSchema(messages), [messages]);
|
|
29
|
+
const rateMutation = useSupplierRateMutation(supplierId);
|
|
30
|
+
const isEditing = !!rate;
|
|
31
|
+
const form = useForm({
|
|
32
|
+
resolver: zodResolver(schema),
|
|
33
|
+
defaultValues: {
|
|
34
|
+
name: "",
|
|
35
|
+
currency: "EUR",
|
|
36
|
+
amount: 0,
|
|
37
|
+
unit: "per_person",
|
|
38
|
+
validFrom: "",
|
|
39
|
+
validTo: "",
|
|
40
|
+
minPax: "",
|
|
41
|
+
maxPax: "",
|
|
42
|
+
notes: "",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
if (!open)
|
|
47
|
+
return;
|
|
48
|
+
form.reset({
|
|
49
|
+
name: rate?.name ?? "",
|
|
50
|
+
currency: rate?.currency ?? "EUR",
|
|
51
|
+
amount: rate ? rate.amountCents / 100 : 0,
|
|
52
|
+
unit: rate?.unit ?? "per_person",
|
|
53
|
+
validFrom: rate?.validFrom ?? "",
|
|
54
|
+
validTo: rate?.validTo ?? "",
|
|
55
|
+
minPax: rate?.minPax ?? "",
|
|
56
|
+
maxPax: rate?.maxPax ?? "",
|
|
57
|
+
notes: rate?.notes ?? "",
|
|
58
|
+
});
|
|
59
|
+
}, [form, open, rate]);
|
|
60
|
+
async function onSubmit(values) {
|
|
61
|
+
const input = {
|
|
62
|
+
name: values.name,
|
|
63
|
+
currency: values.currency.toUpperCase(),
|
|
64
|
+
amountCents: Math.round(values.amount * 100),
|
|
65
|
+
unit: values.unit,
|
|
66
|
+
validFrom: values.validFrom || null,
|
|
67
|
+
validTo: values.validTo || null,
|
|
68
|
+
minPax: values.minPax && typeof values.minPax === "number" ? values.minPax : null,
|
|
69
|
+
maxPax: values.maxPax && typeof values.maxPax === "number" ? values.maxPax : null,
|
|
70
|
+
notes: values.notes || null,
|
|
71
|
+
};
|
|
72
|
+
const saved = isEditing
|
|
73
|
+
? await rateMutation.update.mutateAsync({ serviceId, rateId: rate.id, input })
|
|
74
|
+
: await rateMutation.create.mutateAsync({ serviceId, input });
|
|
75
|
+
onSuccess?.(saved);
|
|
76
|
+
onOpenChange(false);
|
|
77
|
+
}
|
|
78
|
+
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(Input, { ...form.register("currency"), maxLength: 3, placeholder: dialog.currencyPlaceholder, className: "uppercase" }) }), _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(Input, { ...form.register("validFrom"), type: "date" }) }), _jsx(Field, { label: dialog.validToLabel, children: _jsx(Input, { ...form.register("validTo"), type: "date" }) })] }), _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] })] })] })] }) }));
|
|
79
|
+
}
|
|
80
|
+
function Field({ label, error, children, }) {
|
|
81
|
+
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 })] }));
|
|
82
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type SupplierService } from "@voyantjs/suppliers-react";
|
|
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/components/service-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,2BAA2B,CAAA;AAqClC,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 { SERVICE_TYPES, useSupplierServiceMutation, } from "@voyantjs/suppliers-react";
|
|
4
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea, } from "@voyantjs/ui/components";
|
|
5
|
+
import { zodResolver } from "@voyantjs/ui/lib/zod-resolver";
|
|
6
|
+
import { Loader2 } from "lucide-react";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { useForm } from "react-hook-form";
|
|
9
|
+
import { z } from "zod/v4";
|
|
10
|
+
import { useSuppliersUiMessagesOrDefault } from "../i18n/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,16 @@
|
|
|
1
|
+
import { type Supplier, type UpdateSupplierInput } from "@voyantjs/suppliers-react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
export type SupplierDetailPageProps = {
|
|
4
|
+
id: string;
|
|
5
|
+
locale?: string;
|
|
6
|
+
onBack?: () => void;
|
|
7
|
+
onDeleted?: () => void;
|
|
8
|
+
confirmAction?: (message: string) => boolean;
|
|
9
|
+
renderCustomerPaymentPolicy?: (args: {
|
|
10
|
+
supplier: Supplier;
|
|
11
|
+
updateSupplier: (input: UpdateSupplierInput) => Promise<Supplier>;
|
|
12
|
+
isUpdating: boolean;
|
|
13
|
+
}) => React.ReactNode;
|
|
14
|
+
};
|
|
15
|
+
export declare function SupplierDetailPage({ id, locale, onBack, onDeleted, confirmAction, renderCustomerPaymentPolicy, }: SupplierDetailPageProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=supplier-detail-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supplier-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/supplier-detail-page.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,QAAQ,EAIb,KAAK,mBAAmB,EAQzB,MAAM,2BAA2B,CAAA;AAWlC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,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,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,2BAA2B,GAC5B,EAAE,uBAAuB,2CAmSzB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { statusVariant, useSupplier, useSupplierMutation, useSupplierNoteMutation, useSupplierNotes, useSupplierRateMutation, useSupplierServiceMutation, useSupplierServices, } from "@voyantjs/suppliers-react";
|
|
4
|
+
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Textarea, } from "@voyantjs/ui/components";
|
|
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 { RateDialog } from "./rate-dialog.js";
|
|
9
|
+
import { ServiceDialog } from "./service-dialog.js";
|
|
10
|
+
import { SupplierDialog } from "./supplier-dialog.js";
|
|
11
|
+
import { SupplierServiceRow } from "./supplier-service-row.js";
|
|
12
|
+
export function SupplierDetailPage({ id, locale = "en-US", onBack, onDeleted, confirmAction = (message) => globalThis.confirm?.(message) ?? true, renderCustomerPaymentPolicy, }) {
|
|
13
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
14
|
+
const detail = messages.supplierDetailPage;
|
|
15
|
+
const supplierQuery = useSupplier(id);
|
|
16
|
+
const servicesQuery = useSupplierServices(id);
|
|
17
|
+
const notesQuery = useSupplierNotes(id);
|
|
18
|
+
const supplierMutation = useSupplierMutation();
|
|
19
|
+
const serviceMutation = useSupplierServiceMutation(id);
|
|
20
|
+
const rateMutation = useSupplierRateMutation(id);
|
|
21
|
+
const noteMutation = useSupplierNoteMutation(id);
|
|
22
|
+
const [editOpen, setEditOpen] = React.useState(false);
|
|
23
|
+
const [serviceDialogOpen, setServiceDialogOpen] = React.useState(false);
|
|
24
|
+
const [editingService, setEditingService] = React.useState();
|
|
25
|
+
const [rateDialog, setRateDialog] = React.useState({ open: false, serviceId: "" });
|
|
26
|
+
const [expandedServiceId, setExpandedServiceId] = React.useState(null);
|
|
27
|
+
const [noteContent, setNoteContent] = React.useState("");
|
|
28
|
+
const supplier = supplierQuery.data?.data;
|
|
29
|
+
async function deleteSupplier() {
|
|
30
|
+
if (!supplier || !confirmAction(detail.deleteSupplierConfirm))
|
|
31
|
+
return;
|
|
32
|
+
await supplierMutation.remove.mutateAsync(supplier.id);
|
|
33
|
+
onDeleted?.();
|
|
34
|
+
}
|
|
35
|
+
async function deleteService(serviceId) {
|
|
36
|
+
if (!confirmAction(detail.deleteServiceConfirm))
|
|
37
|
+
return;
|
|
38
|
+
await serviceMutation.remove.mutateAsync(serviceId);
|
|
39
|
+
if (expandedServiceId === serviceId)
|
|
40
|
+
setExpandedServiceId(null);
|
|
41
|
+
}
|
|
42
|
+
async function deleteRate(serviceId, rateId) {
|
|
43
|
+
if (!confirmAction(detail.deleteRateConfirm))
|
|
44
|
+
return;
|
|
45
|
+
await rateMutation.remove.mutateAsync({ serviceId, rateId });
|
|
46
|
+
}
|
|
47
|
+
async function addNote() {
|
|
48
|
+
const content = noteContent.trim();
|
|
49
|
+
if (!content)
|
|
50
|
+
return;
|
|
51
|
+
await noteMutation.create.mutateAsync({ content });
|
|
52
|
+
setNoteContent("");
|
|
53
|
+
}
|
|
54
|
+
if (supplierQuery.isPending)
|
|
55
|
+
return _jsx(SupplierDetailSkeleton, {});
|
|
56
|
+
if (supplierQuery.isError) {
|
|
57
|
+
return (_jsx(EmptyState, { message: detail.loadFailed, onBack: onBack, backLabel: detail.backToSuppliers }));
|
|
58
|
+
}
|
|
59
|
+
if (!supplier) {
|
|
60
|
+
return (_jsx(EmptyState, { message: detail.notFound, onBack: onBack, backLabel: detail.backToSuppliers }));
|
|
61
|
+
}
|
|
62
|
+
const services = servicesQuery.data?.data ?? [];
|
|
63
|
+
const notes = notesQuery.data?.data ?? [];
|
|
64
|
+
return (_jsxs("div", { className: "flex flex-col gap-6", 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
|
|
65
|
+
? messages.common.none
|
|
66
|
+
: 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?.({
|
|
67
|
+
supplier,
|
|
68
|
+
updateSupplier: (input) => supplierMutation.update.mutateAsync({ id: supplier.id, input }),
|
|
69
|
+
isUpdating: supplierMutation.update.isPending,
|
|
70
|
+
}), _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: () => {
|
|
71
|
+
setEditingService(undefined);
|
|
72
|
+
setServiceDialogOpen(true);
|
|
73
|
+
}, 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: () => {
|
|
74
|
+
setEditingService(service);
|
|
75
|
+
setServiceDialogOpen(true);
|
|
76
|
+
}, 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: () => {
|
|
77
|
+
setServiceDialogOpen(false);
|
|
78
|
+
setEditingService(undefined);
|
|
79
|
+
} }), _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: "" }) })] }));
|
|
80
|
+
}
|
|
81
|
+
function Detail({ label, children }) {
|
|
82
|
+
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 })] }));
|
|
83
|
+
}
|
|
84
|
+
function EmptyState({ message, onBack, backLabel, }) {
|
|
85
|
+
return (_jsxs("div", { className: "flex min-h-80 flex-col items-center justify-center gap-4 text-center", children: [_jsx("p", { className: "text-muted-foreground", children: message }), onBack && (_jsxs(Button, { type: "button", variant: "outline", onClick: onBack, children: [_jsx(ArrowLeft, {}), backLabel] }))] }));
|
|
86
|
+
}
|
|
87
|
+
function SupplierDetailSkeleton() {
|
|
88
|
+
return (_jsxs("div", { className: "flex flex-col gap-6", 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" })] }));
|
|
89
|
+
}
|
|
90
|
+
function LoadingLine() {
|
|
91
|
+
return _jsx("div", { className: "h-4 w-40 animate-pulse rounded bg-muted" });
|
|
92
|
+
}
|
|
93
|
+
function hasContactDetails(supplier) {
|
|
94
|
+
return Boolean(supplier.email ||
|
|
95
|
+
supplier.phone ||
|
|
96
|
+
supplier.website ||
|
|
97
|
+
supplier.address ||
|
|
98
|
+
supplier.contactName ||
|
|
99
|
+
supplier.contactEmail ||
|
|
100
|
+
supplier.contactPhone);
|
|
101
|
+
}
|
|
102
|
+
function formatDate(value, locale) {
|
|
103
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }).format(new Date(value));
|
|
104
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Supplier } from "@voyantjs/suppliers-react";
|
|
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/components/supplier-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,QAAQ,EAEd,MAAM,2BAA2B,CAAA;AAkDlC,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,2CAyO9F"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { SUPPLIER_STATUSES, SUPPLIER_TYPES, useSupplierMutation, } from "@voyantjs/suppliers-react";
|
|
4
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, } from "@voyantjs/ui/components";
|
|
5
|
+
import { CountryCombobox } from "@voyantjs/ui/components/country-combobox";
|
|
6
|
+
import { zodResolver } from "@voyantjs/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
|
+
function getSupplierSchema(messages) {
|
|
13
|
+
const dialog = messages.dialogs.supplier;
|
|
14
|
+
return z.object({
|
|
15
|
+
name: z.string().min(1, dialog.validationNameRequired),
|
|
16
|
+
type: z.enum(["hotel", "transfer", "guide", "experience", "airline", "restaurant", "other"]),
|
|
17
|
+
status: z.enum(["active", "inactive", "pending"]),
|
|
18
|
+
description: z.string().optional().nullable(),
|
|
19
|
+
email: z.string().email().optional().or(z.literal("")).nullable(),
|
|
20
|
+
phone: z.string().optional().nullable(),
|
|
21
|
+
website: z.string().url().optional().or(z.literal("")).nullable(),
|
|
22
|
+
address: z.string().optional().nullable(),
|
|
23
|
+
city: z.string().optional().nullable(),
|
|
24
|
+
country: z.string().optional().nullable(),
|
|
25
|
+
defaultCurrency: z.string().max(3, dialog.validationIsoCurrency).optional().nullable(),
|
|
26
|
+
reservationTimeoutMinutes: z
|
|
27
|
+
.union([z.literal(""), z.coerce.number().int().min(0, dialog.validationReservationTimeout)])
|
|
28
|
+
.optional()
|
|
29
|
+
.nullable(),
|
|
30
|
+
contactName: z.string().optional().nullable(),
|
|
31
|
+
contactEmail: z.string().email().optional().or(z.literal("")).nullable(),
|
|
32
|
+
contactPhone: z.string().optional().nullable(),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function SupplierDialog({ open, onOpenChange, supplier, onSuccess }) {
|
|
36
|
+
const messages = useSuppliersUiMessagesOrDefault();
|
|
37
|
+
const dialog = messages.dialogs.supplier;
|
|
38
|
+
const schema = React.useMemo(() => getSupplierSchema(messages), [messages]);
|
|
39
|
+
const supplierMutation = useSupplierMutation();
|
|
40
|
+
const isEditing = !!supplier;
|
|
41
|
+
const form = useForm({
|
|
42
|
+
resolver: zodResolver(schema),
|
|
43
|
+
defaultValues: {
|
|
44
|
+
name: "",
|
|
45
|
+
type: "hotel",
|
|
46
|
+
status: "active",
|
|
47
|
+
description: "",
|
|
48
|
+
email: "",
|
|
49
|
+
phone: "",
|
|
50
|
+
website: "",
|
|
51
|
+
address: "",
|
|
52
|
+
city: "",
|
|
53
|
+
country: "",
|
|
54
|
+
defaultCurrency: "",
|
|
55
|
+
reservationTimeoutMinutes: "",
|
|
56
|
+
contactName: "",
|
|
57
|
+
contactEmail: "",
|
|
58
|
+
contactPhone: "",
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
if (!open)
|
|
63
|
+
return;
|
|
64
|
+
form.reset({
|
|
65
|
+
name: supplier?.name ?? "",
|
|
66
|
+
type: supplier?.type ?? "hotel",
|
|
67
|
+
status: supplier?.status ?? "active",
|
|
68
|
+
description: supplier?.description ?? "",
|
|
69
|
+
email: supplier?.email ?? "",
|
|
70
|
+
phone: supplier?.phone ?? "",
|
|
71
|
+
website: supplier?.website ?? "",
|
|
72
|
+
address: supplier?.address ?? "",
|
|
73
|
+
city: supplier?.city ?? "",
|
|
74
|
+
country: supplier?.country ?? "",
|
|
75
|
+
defaultCurrency: supplier?.defaultCurrency ?? "",
|
|
76
|
+
reservationTimeoutMinutes: supplier?.reservationTimeoutMinutes == null
|
|
77
|
+
? ""
|
|
78
|
+
: String(supplier.reservationTimeoutMinutes),
|
|
79
|
+
contactName: supplier?.contactName ?? "",
|
|
80
|
+
contactEmail: supplier?.contactEmail ?? "",
|
|
81
|
+
contactPhone: supplier?.contactPhone ?? "",
|
|
82
|
+
});
|
|
83
|
+
}, [form, open, supplier]);
|
|
84
|
+
async function onSubmit(values) {
|
|
85
|
+
const input = {
|
|
86
|
+
...values,
|
|
87
|
+
description: values.description || null,
|
|
88
|
+
email: values.email || null,
|
|
89
|
+
phone: values.phone || null,
|
|
90
|
+
website: values.website || null,
|
|
91
|
+
address: values.address || null,
|
|
92
|
+
city: values.city || null,
|
|
93
|
+
country: values.country || null,
|
|
94
|
+
defaultCurrency: values.defaultCurrency || null,
|
|
95
|
+
reservationTimeoutMinutes: values.reservationTimeoutMinutes === "" ||
|
|
96
|
+
values.reservationTimeoutMinutes === null ||
|
|
97
|
+
values.reservationTimeoutMinutes === undefined
|
|
98
|
+
? null
|
|
99
|
+
: values.reservationTimeoutMinutes,
|
|
100
|
+
contactName: values.contactName || null,
|
|
101
|
+
contactEmail: values.contactEmail || null,
|
|
102
|
+
contactPhone: values.contactPhone || null,
|
|
103
|
+
};
|
|
104
|
+
const saved = isEditing
|
|
105
|
+
? await supplierMutation.update.mutateAsync({ id: supplier.id, input })
|
|
106
|
+
: await supplierMutation.create.mutateAsync(input);
|
|
107
|
+
onSuccess?.(saved);
|
|
108
|
+
onOpenChange(false);
|
|
109
|
+
}
|
|
110
|
+
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(Input, { ...form.register("defaultCurrency"), maxLength: 3, placeholder: dialog.defaultCurrencyPlaceholder, className: "uppercase" }) }), _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] })] })] })] }) }));
|
|
111
|
+
}
|
|
112
|
+
function Field({ label, error, children, }) {
|
|
113
|
+
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 })] }));
|
|
114
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import type
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
isPending?: boolean;
|
|
10
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import { type Supplier } from "@voyantjs/suppliers-react";
|
|
2
|
+
export type SuppliersPageProps = {
|
|
3
|
+
pageSize?: number;
|
|
4
|
+
onSupplierOpen?: (supplier: Supplier) => void;
|
|
5
|
+
onSupplierCreated?: (supplier: Supplier) => void;
|
|
6
|
+
initialSearch?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function SuppliersPage({ pageSize, onSupplierOpen, onSupplierCreated, initialSearch, }?: SuppliersPageProps): import("react/jsx-runtime").JSX.Element;
|
|
11
9
|
//# sourceMappingURL=suppliers-page.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suppliers-page.d.ts","sourceRoot":"","sources":["../../src/components/suppliers-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"suppliers-page.d.ts","sourceRoot":"","sources":["../../src/components/suppliers-page.tsx"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,QAAQ,EAKd,MAAM,2BAA2B,CAAA;AAmBlC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC7C,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAA;IAChD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAa,EACb,cAAc,EACd,iBAAiB,EACjB,aAAkB,GACnB,GAAE,kBAAuB,2CAoPzB"}
|