@voyant-travel/quotes-react 0.119.2
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 +39 -0
- package/dist/client.d.ts +31 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +71 -0
- package/dist/components/create-quote-dialog.d.ts +10 -0
- package/dist/components/create-quote-dialog.d.ts.map +1 -0
- package/dist/components/create-quote-dialog.js +45 -0
- package/dist/components/create-quote-version-dialog.d.ts +9 -0
- package/dist/components/create-quote-version-dialog.d.ts.map +1 -0
- package/dist/components/create-quote-version-dialog.js +108 -0
- package/dist/components/crm-format.d.ts +7 -0
- package/dist/components/crm-format.d.ts.map +1 -0
- package/dist/components/crm-format.js +40 -0
- package/dist/components/inline-currency-field.d.ts +12 -0
- package/dist/components/inline-currency-field.d.ts.map +1 -0
- package/dist/components/inline-currency-field.js +46 -0
- package/dist/components/inline-field.d.ts +16 -0
- package/dist/components/inline-field.d.ts.map +1 -0
- package/dist/components/inline-field.js +45 -0
- package/dist/components/inline-language-field.d.ts +12 -0
- package/dist/components/inline-language-field.d.ts.map +1 -0
- package/dist/components/inline-language-field.js +46 -0
- package/dist/components/inline-number-field.d.ts +15 -0
- package/dist/components/inline-number-field.d.ts.map +1 -0
- package/dist/components/inline-number-field.js +58 -0
- package/dist/components/inline-select-field.d.ts +19 -0
- package/dist/components/inline-select-field.d.ts.map +1 -0
- package/dist/components/inline-select-field.js +34 -0
- package/dist/components/quote-summary-card.d.ts +11 -0
- package/dist/components/quote-summary-card.d.ts.map +1 -0
- package/dist/components/quote-summary-card.js +11 -0
- package/dist/components/quote-version-detail-sections.d.ts +22 -0
- package/dist/components/quote-version-detail-sections.d.ts.map +1 -0
- package/dist/components/quote-version-detail-sections.js +87 -0
- package/dist/components/quote-versions-page.d.ts +8 -0
- package/dist/components/quote-versions-page.d.ts.map +1 -0
- package/dist/components/quote-versions-page.js +30 -0
- package/dist/components/quotes-board.d.ts +8 -0
- package/dist/components/quotes-board.d.ts.map +1 -0
- package/dist/components/quotes-board.js +24 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +9 -0
- package/dist/hooks/use-pipeline-mutation.d.ts +76 -0
- package/dist/hooks/use-pipeline-mutation.d.ts.map +1 -0
- package/dist/hooks/use-pipeline-mutation.js +76 -0
- package/dist/hooks/use-pipelines.d.ts +31 -0
- package/dist/hooks/use-pipelines.d.ts.map +1 -0
- package/dist/hooks/use-pipelines.js +42 -0
- package/dist/hooks/use-quote-mutation.d.ts +72 -0
- package/dist/hooks/use-quote-mutation.d.ts.map +1 -0
- package/dist/hooks/use-quote-mutation.js +50 -0
- package/dist/hooks/use-quote-version-mutation.d.ts +272 -0
- package/dist/hooks/use-quote-version-mutation.d.ts.map +1 -0
- package/dist/hooks/use-quote-version-mutation.js +164 -0
- package/dist/hooks/use-quote-version.d.ts +37 -0
- package/dist/hooks/use-quote-version.d.ts.map +1 -0
- package/dist/hooks/use-quote-version.js +34 -0
- package/dist/hooks/use-quote-versions.d.ts +30 -0
- package/dist/hooks/use-quote-versions.d.ts.map +1 -0
- package/dist/hooks/use-quote-versions.js +27 -0
- package/dist/hooks/use-quote.d.ts +26 -0
- package/dist/hooks/use-quote.d.ts.map +1 -0
- package/dist/hooks/use-quote.js +23 -0
- package/dist/hooks/use-quotes.d.ts +36 -0
- package/dist/hooks/use-quotes.d.ts.map +1 -0
- package/dist/hooks/use-quotes.js +44 -0
- package/dist/hooks/use-stages.d.ts +37 -0
- package/dist/hooks/use-stages.d.ts.map +1 -0
- package/dist/hooks/use-stages.js +45 -0
- package/dist/i18n/en/base.d.ts +189 -0
- package/dist/i18n/en/base.d.ts.map +1 -0
- package/dist/i18n/en/base.js +188 -0
- package/dist/i18n/en/commerce.d.ts +133 -0
- package/dist/i18n/en/commerce.d.ts.map +1 -0
- package/dist/i18n/en/commerce.js +132 -0
- package/dist/i18n/en/detail.d.ts +211 -0
- package/dist/i18n/en/detail.d.ts.map +1 -0
- package/dist/i18n/en/detail.js +210 -0
- package/dist/i18n/en/lists.d.ts +75 -0
- package/dist/i18n/en/lists.d.ts.map +1 -0
- package/dist/i18n/en/lists.js +74 -0
- package/dist/i18n/en.d.ts +599 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +10 -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 +577 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +15 -0
- package/dist/i18n/provider.d.ts +1220 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +44 -0
- package/dist/i18n/ro/base.d.ts +189 -0
- package/dist/i18n/ro/base.d.ts.map +1 -0
- package/dist/i18n/ro/base.js +188 -0
- package/dist/i18n/ro/commerce.d.ts +133 -0
- package/dist/i18n/ro/commerce.d.ts.map +1 -0
- package/dist/i18n/ro/commerce.js +132 -0
- package/dist/i18n/ro/detail.d.ts +211 -0
- package/dist/i18n/ro/detail.d.ts.map +1 -0
- package/dist/i18n/ro/detail.js +210 -0
- package/dist/i18n/ro/lists.d.ts +75 -0
- package/dist/i18n/ro/lists.d.ts.map +1 -0
- package/dist/i18n/ro/lists.js +74 -0
- package/dist/i18n/ro.d.ts +599 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +10 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -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 +44 -0
- package/dist/query-keys.d.ts.map +1 -0
- package/dist/query-keys.js +16 -0
- package/dist/query-options.d.ts +678 -0
- package/dist/query-options.d.ts.map +1 -0
- package/dist/query-options.js +160 -0
- package/dist/schemas.d.ts +348 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +101 -0
- package/dist/ui.d.ts +8 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +7 -0
- package/package.json +117 -0
- package/src/styles.css +11 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatMessage } from "@voyant-travel/i18n";
|
|
4
|
+
import { cn } from "@voyant-travel/ui/components";
|
|
5
|
+
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyant-travel/ui/components/combobox";
|
|
6
|
+
import { currencies } from "@voyant-travel/utils/currencies";
|
|
7
|
+
import { Pencil } from "lucide-react";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { useCrmUiMessagesOrDefault } from "../i18n/index.js";
|
|
10
|
+
const CURRENCY_CODES = Object.keys(currencies).sort();
|
|
11
|
+
export function InlineCurrencyField({ icon: Icon, label, value, disabled, onSave, }) {
|
|
12
|
+
const messages = useCrmUiMessagesOrDefault().inlineEditor;
|
|
13
|
+
const [editing, setEditing] = useState(false);
|
|
14
|
+
const [saving, setSaving] = useState(false);
|
|
15
|
+
const [error, setError] = useState(null);
|
|
16
|
+
async function commitSelection(next) {
|
|
17
|
+
setSaving(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
try {
|
|
20
|
+
await onSave(next);
|
|
21
|
+
setEditing(false);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
setError(err instanceof Error ? err.message : messages.failedToSave);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
setSaving(false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const currencyInfo = value ? currencies[value] : null;
|
|
31
|
+
return (_jsxs("div", { className: "group flex items-start gap-3 py-1.5", children: [Icon ? _jsx(Icon, { className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }), editing ? (_jsxs(Combobox, { items: CURRENCY_CODES, defaultOpen: true, autoHighlight: true, filter: (code, query) => {
|
|
32
|
+
const currency = currencies[code];
|
|
33
|
+
if (!currency)
|
|
34
|
+
return false;
|
|
35
|
+
const normalized = query.toLowerCase();
|
|
36
|
+
return (currency.code.toLowerCase().includes(normalized) ||
|
|
37
|
+
currency.name.toLowerCase().includes(normalized) ||
|
|
38
|
+
currency.symbol.toLowerCase().includes(normalized));
|
|
39
|
+
}, onValueChange: (next) => void commitSelection(next ?? null), onOpenChange: (open) => {
|
|
40
|
+
if (!open)
|
|
41
|
+
setEditing(false);
|
|
42
|
+
}, children: [_jsx(ComboboxInput, { autoFocus: true, placeholder: messages.searchCurrencyPlaceholder, className: "mt-0.5 h-8 text-sm", disabled: saving }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: messages.noCurrenciesFound }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (code) => {
|
|
43
|
+
const currency = currencies[code];
|
|
44
|
+
return (_jsxs(ComboboxItem, { value: code, children: [_jsx("span", { className: "min-w-10 font-mono text-xs text-muted-foreground", children: code }), _jsx("span", { className: "truncate", children: currency?.name ?? code })] }, code));
|
|
45
|
+
} }) })] })] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => !disabled && setEditing(true), disabled: disabled, className: cn("-mx-1 flex-1 truncate rounded px-1 py-0.5 text-left text-sm transition-colors", !disabled && "cursor-text hover:bg-muted/60", !value && "text-muted-foreground italic"), children: value ? (_jsxs("span", { children: [_jsx("span", { className: "font-mono", children: value }), currencyInfo ? (_jsx("span", { className: "ml-2 text-muted-foreground", children: currencyInfo.name })) : null] })) : (formatMessage(messages.addTemplate, { label: label.toLowerCase() })) }), !disabled ? (_jsx(Pencil, { className: "h-3 w-3 text-muted-foreground opacity-0 group-hover:opacity-100" })) : null] })), error ? _jsx("p", { className: "mt-1 text-xs text-destructive", children: error }) : null] })] }));
|
|
46
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ComponentType } from "react";
|
|
2
|
+
export type InlineFieldKind = "text" | "email" | "url" | "textarea";
|
|
3
|
+
export interface InlineFieldProps {
|
|
4
|
+
icon?: ComponentType<{
|
|
5
|
+
className?: string;
|
|
6
|
+
}>;
|
|
7
|
+
label: string;
|
|
8
|
+
value: string | null;
|
|
9
|
+
kind?: InlineFieldKind;
|
|
10
|
+
href?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
onSave: (next: string | null) => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function InlineField({ icon: Icon, label, value, kind, href, placeholder, disabled, onSave, }: InlineFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=inline-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-field.d.ts","sourceRoot":"","sources":["../../src/components/inline-field.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAgC,MAAM,OAAO,CAAA;AAIxE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,CAAA;AAEnE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,eAAe,CAAA;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EAAE,IAAI,EACV,KAAK,EACL,KAAK,EACL,IAAa,EACb,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,MAAM,GACP,EAAE,gBAAgB,2CA+HlB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, cn, Input, Textarea } from "@voyant-travel/ui/components";
|
|
4
|
+
import { Check, Loader2, Pencil, X } from "lucide-react";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { useCrmUiMessagesOrDefault } from "../i18n/index.js";
|
|
7
|
+
export function InlineField({ icon: Icon, label, value, kind = "text", href, placeholder, disabled, onSave, }) {
|
|
8
|
+
const messages = useCrmUiMessagesOrDefault().inlineEditor;
|
|
9
|
+
const [editing, setEditing] = useState(false);
|
|
10
|
+
const [draft, setDraft] = useState(value ?? "");
|
|
11
|
+
const [saving, setSaving] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
function handleCancel() {
|
|
14
|
+
setDraft(value ?? "");
|
|
15
|
+
setEditing(false);
|
|
16
|
+
setError(null);
|
|
17
|
+
}
|
|
18
|
+
async function handleSave() {
|
|
19
|
+
setSaving(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
try {
|
|
22
|
+
const trimmed = draft.trim();
|
|
23
|
+
await onSave(trimmed === "" ? null : trimmed);
|
|
24
|
+
setEditing(false);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
setError(err instanceof Error ? err.message : messages.failedToSave);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
setSaving(false);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function handleKeyDown(event) {
|
|
34
|
+
if (kind !== "textarea" && event.key === "Enter") {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
void handleSave();
|
|
37
|
+
}
|
|
38
|
+
else if (event.key === "Escape") {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
handleCancel();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const display = value || (_jsx("span", { className: "text-muted-foreground italic", children: placeholder || messages.notSet }));
|
|
44
|
+
return (_jsxs("div", { className: "group flex items-start gap-3 py-1.5", children: [Icon ? _jsx(Icon, { className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }), editing ? (_jsxs("div", { className: "mt-1 flex items-start gap-2", children: [kind === "textarea" ? (_jsx(Textarea, { autoFocus: true, value: draft, onChange: (event) => setDraft(event.target.value), onKeyDown: handleKeyDown, disabled: saving, className: "min-h-[80px] text-sm", placeholder: placeholder })) : (_jsx(Input, { autoFocus: true, type: kind === "email" ? "email" : kind === "url" ? "url" : "text", value: draft, onChange: (event) => setDraft(event.target.value), onKeyDown: handleKeyDown, disabled: saving, className: "h-8 text-sm", placeholder: placeholder })), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: () => void handleSave(), disabled: saving, className: "h-8 w-8 p-0", children: saving ? (_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" })) : (_jsx(Check, { className: "h-3.5 w-3.5", "aria-hidden": "true" })) }), _jsx(Button, { size: "sm", variant: "ghost", onClick: handleCancel, disabled: saving, className: "h-8 w-8 p-0", children: _jsx(X, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })] })] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: cn("flex-1 text-sm", kind !== "textarea" && "truncate"), children: href && value ? (_jsx("a", { href: href, className: "text-primary hover:underline", target: href.startsWith("http") ? "_blank" : undefined, rel: href.startsWith("http") ? "noreferrer" : undefined, children: value })) : (display) }), !disabled ? (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditing(true), className: "h-6 w-6 p-0 opacity-0 group-hover:opacity-100", children: _jsx(Pencil, { className: "h-3 w-3", "aria-hidden": "true" }) })) : null] })), error ? _jsx("p", { className: "mt-1 text-xs text-destructive", children: error }) : null] })] }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ComponentType } from "react";
|
|
2
|
+
export interface InlineLanguageFieldProps {
|
|
3
|
+
icon?: ComponentType<{
|
|
4
|
+
className?: string;
|
|
5
|
+
}>;
|
|
6
|
+
label: string;
|
|
7
|
+
value: string | null;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onSave: (next: string | null) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function InlineLanguageField({ icon: Icon, label, value, disabled, onSave, }: InlineLanguageFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=inline-language-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-language-field.d.ts","sourceRoot":"","sources":["../../src/components/inline-language-field.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,aAAa,EAAY,MAAM,OAAO,CAAA;AAMpD,MAAM,WAAW,wBAAwB;IACvC,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EAAE,IAAI,EACV,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,GACP,EAAE,wBAAwB,2CAuG1B"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatMessage } from "@voyant-travel/i18n";
|
|
4
|
+
import { cn } from "@voyant-travel/ui/components";
|
|
5
|
+
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyant-travel/ui/components/combobox";
|
|
6
|
+
import { languages } from "@voyant-travel/utils/languages";
|
|
7
|
+
import { Pencil } from "lucide-react";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { useCrmUiMessagesOrDefault } from "../i18n/index.js";
|
|
10
|
+
const LANGUAGE_CODES = Object.keys(languages).sort();
|
|
11
|
+
export function InlineLanguageField({ icon: Icon, label, value, disabled, onSave, }) {
|
|
12
|
+
const messages = useCrmUiMessagesOrDefault().inlineEditor;
|
|
13
|
+
const [editing, setEditing] = useState(false);
|
|
14
|
+
const [saving, setSaving] = useState(false);
|
|
15
|
+
const [error, setError] = useState(null);
|
|
16
|
+
async function commitSelection(next) {
|
|
17
|
+
setSaving(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
try {
|
|
20
|
+
await onSave(next);
|
|
21
|
+
setEditing(false);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
setError(err instanceof Error ? err.message : messages.failedToSave);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
setSaving(false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const languageName = value ? languages[value] : null;
|
|
31
|
+
return (_jsxs("div", { className: "group flex items-start gap-3 py-1.5", children: [Icon ? _jsx(Icon, { className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }), editing ? (_jsxs(Combobox, { items: LANGUAGE_CODES, defaultOpen: true, autoHighlight: true, filter: (code, query) => {
|
|
32
|
+
const languageCode = String(code);
|
|
33
|
+
const languageName = languages[languageCode];
|
|
34
|
+
if (!languageName)
|
|
35
|
+
return false;
|
|
36
|
+
const normalized = query.toLowerCase();
|
|
37
|
+
return (languageCode.toLowerCase().includes(normalized) ||
|
|
38
|
+
languageName.toLowerCase().includes(normalized));
|
|
39
|
+
}, onValueChange: (next) => void commitSelection(next ?? null), onOpenChange: (open) => {
|
|
40
|
+
if (!open)
|
|
41
|
+
setEditing(false);
|
|
42
|
+
}, children: [_jsx(ComboboxInput, { autoFocus: true, placeholder: messages.searchLanguagePlaceholder, className: "mt-0.5 h-8 text-sm", disabled: saving }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: messages.noLanguagesFound }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (code) => {
|
|
43
|
+
const languageName = languages[code];
|
|
44
|
+
return (_jsxs(ComboboxItem, { value: code, children: [_jsx("span", { className: "min-w-10 font-mono text-xs text-muted-foreground", children: code }), _jsx("span", { className: "truncate", children: languageName })] }, code));
|
|
45
|
+
} }) })] })] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => !disabled && setEditing(true), disabled: disabled, className: cn("-mx-1 flex-1 truncate rounded px-1 py-0.5 text-left text-sm transition-colors", !disabled && "cursor-text hover:bg-muted/60", !value && "text-muted-foreground italic"), children: value ? (_jsxs("span", { children: [_jsx("span", { className: "font-mono", children: value }), languageName ? (_jsx("span", { className: "ml-2 text-muted-foreground", children: languageName })) : null] })) : (formatMessage(messages.addTemplate, { label: label.toLowerCase() })) }), !disabled ? (_jsx(Pencil, { className: "h-3 w-3 text-muted-foreground opacity-0 group-hover:opacity-100" })) : null] })), error ? _jsx("p", { className: "mt-1 text-xs text-destructive", children: error }) : null] })] }));
|
|
46
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ComponentType } from "react";
|
|
2
|
+
export interface InlineNumberFieldProps {
|
|
3
|
+
icon?: ComponentType<{
|
|
4
|
+
className?: string;
|
|
5
|
+
}>;
|
|
6
|
+
label: string;
|
|
7
|
+
value: number | null;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
min?: number;
|
|
11
|
+
max?: number;
|
|
12
|
+
onSave: (next: number | null) => Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function InlineNumberField({ icon: Icon, label, value, placeholder, disabled, min, max, onSave, }: InlineNumberFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=inline-number-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-number-field.d.ts","sourceRoot":"","sources":["../../src/components/inline-number-field.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,aAAa,EAAgC,MAAM,OAAO,CAAA;AAIxE,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EAAE,IAAI,EACV,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,GAAG,EACH,GAAG,EACH,MAAM,GACP,EAAE,sBAAsB,2CAqHxB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatMessage } from "@voyant-travel/i18n";
|
|
4
|
+
import { Button, Input } from "@voyant-travel/ui/components";
|
|
5
|
+
import { Check, Loader2, Pencil, X } from "lucide-react";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { useCrmUiMessagesOrDefault } from "../i18n/index.js";
|
|
8
|
+
export function InlineNumberField({ icon: Icon, label, value, placeholder, disabled, min, max, onSave, }) {
|
|
9
|
+
const messages = useCrmUiMessagesOrDefault().inlineEditor;
|
|
10
|
+
const [editing, setEditing] = useState(false);
|
|
11
|
+
const [draft, setDraft] = useState(value != null ? String(value) : "");
|
|
12
|
+
const [saving, setSaving] = useState(false);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
function handleCancel() {
|
|
15
|
+
setDraft(value != null ? String(value) : "");
|
|
16
|
+
setEditing(false);
|
|
17
|
+
setError(null);
|
|
18
|
+
}
|
|
19
|
+
async function handleSave() {
|
|
20
|
+
setSaving(true);
|
|
21
|
+
setError(null);
|
|
22
|
+
try {
|
|
23
|
+
if (draft.trim() === "") {
|
|
24
|
+
await onSave(null);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const parsed = Number.parseInt(draft, 10);
|
|
28
|
+
if (!Number.isFinite(parsed))
|
|
29
|
+
throw new Error(messages.invalidNumber);
|
|
30
|
+
if (min != null && parsed < min) {
|
|
31
|
+
throw new Error(formatMessage(messages.minNumber, { min: String(min) }));
|
|
32
|
+
}
|
|
33
|
+
if (max != null && parsed > max) {
|
|
34
|
+
throw new Error(formatMessage(messages.maxNumber, { max: String(max) }));
|
|
35
|
+
}
|
|
36
|
+
await onSave(parsed);
|
|
37
|
+
}
|
|
38
|
+
setEditing(false);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
setError(err instanceof Error ? err.message : messages.failedToSave);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
setSaving(false);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function handleKeyDown(event) {
|
|
48
|
+
if (event.key === "Enter") {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
void handleSave();
|
|
51
|
+
}
|
|
52
|
+
else if (event.key === "Escape") {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
handleCancel();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return (_jsxs("div", { className: "group flex items-start gap-3 py-1.5", children: [Icon ? _jsx(Icon, { className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }), editing ? (_jsxs("div", { className: "mt-1 flex items-center gap-2", children: [_jsx(Input, { autoFocus: true, type: "number", value: draft, onChange: (event) => setDraft(event.target.value), onKeyDown: handleKeyDown, disabled: saving, className: "h-8 text-sm", placeholder: placeholder, min: min, max: max }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => void handleSave(), disabled: saving, className: "h-8 w-8 p-0", children: saving ? (_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" })) : (_jsx(Check, { className: "h-3.5 w-3.5", "aria-hidden": "true" })) }), _jsx(Button, { size: "sm", variant: "ghost", onClick: handleCancel, disabled: saving, className: "h-8 w-8 p-0", children: _jsx(X, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex-1 truncate text-sm", children: value != null ? (value.toLocaleString()) : (_jsx("span", { className: "text-muted-foreground italic", children: placeholder || messages.notSet })) }), !disabled ? (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditing(true), className: "h-6 w-6 p-0 opacity-0 group-hover:opacity-100", children: _jsx(Pencil, { className: "h-3 w-3", "aria-hidden": "true" }) })) : null] })), error ? _jsx("p", { className: "mt-1 text-xs text-destructive", children: error }) : null] })] }));
|
|
58
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ComponentType } from "react";
|
|
2
|
+
export interface InlineSelectFieldOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface InlineSelectFieldProps {
|
|
7
|
+
icon?: ComponentType<{
|
|
8
|
+
className?: string;
|
|
9
|
+
}>;
|
|
10
|
+
label: string;
|
|
11
|
+
value: string | null;
|
|
12
|
+
options: readonly InlineSelectFieldOption[];
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
allowClear?: boolean;
|
|
16
|
+
onSave: (next: string | null) => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export declare function InlineSelectField({ icon: Icon, label, value, options, placeholder, disabled, allowClear, onSave, }: InlineSelectFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=inline-select-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-select-field.d.ts","sourceRoot":"","sources":["../../src/components/inline-select-field.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,aAAa,EAAY,MAAM,OAAO,CAAA;AAIpD,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/C;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EAAE,IAAI,EACV,KAAK,EACL,KAAK,EACL,OAAO,EACP,WAAW,EACX,QAAQ,EACR,UAAiB,EACjB,MAAM,GACP,EAAE,sBAAsB,2CAsGxB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyant-travel/ui/components";
|
|
4
|
+
import { Check, Loader2, Pencil, X } from "lucide-react";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { useCrmUiMessagesOrDefault } from "../i18n/index.js";
|
|
7
|
+
export function InlineSelectField({ icon: Icon, label, value, options, placeholder, disabled, allowClear = true, onSave, }) {
|
|
8
|
+
const messages = useCrmUiMessagesOrDefault().inlineEditor;
|
|
9
|
+
const [editing, setEditing] = useState(false);
|
|
10
|
+
const [draft, setDraft] = useState(value ?? "");
|
|
11
|
+
const [saving, setSaving] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
function handleCancel() {
|
|
14
|
+
setDraft(value ?? "");
|
|
15
|
+
setEditing(false);
|
|
16
|
+
setError(null);
|
|
17
|
+
}
|
|
18
|
+
async function handleSave() {
|
|
19
|
+
setSaving(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
try {
|
|
22
|
+
await onSave(draft === "__none__" || draft === "" ? null : draft);
|
|
23
|
+
setEditing(false);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
setError(err instanceof Error ? err.message : messages.failedToSave);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
setSaving(false);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const matched = options.find((option) => option.value === value);
|
|
33
|
+
return (_jsxs("div", { className: "group flex items-start gap-3 py-1.5", children: [Icon ? _jsx(Icon, { className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" }) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }), editing ? (_jsxs("div", { className: "mt-1 flex items-center gap-2", children: [_jsxs(Select, { value: draft, onValueChange: (next) => setDraft(next ?? ""), disabled: saving, children: [_jsx(SelectTrigger, { className: "h-8 flex-1 text-sm", children: _jsx(SelectValue, { placeholder: placeholder || messages.selectPlaceholder }) }), _jsxs(SelectContent, { children: [allowClear ? (_jsx(SelectItem, { value: "__none__", children: _jsx("span", { className: "text-muted-foreground italic", children: messages.noneOption }) })) : null, options.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value)))] })] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => void handleSave(), disabled: saving, className: "h-8 w-8 p-0", children: saving ? (_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" })) : (_jsx(Check, { className: "h-3.5 w-3.5", "aria-hidden": "true" })) }), _jsx(Button, { size: "sm", variant: "ghost", onClick: handleCancel, disabled: saving, className: "h-8 w-8 p-0", children: _jsx(X, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex-1 truncate text-sm", children: matched ? (matched.label) : (_jsx("span", { className: "text-muted-foreground italic", children: placeholder || messages.notSet })) }), !disabled ? (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditing(true), className: "h-6 w-6 p-0 opacity-0 group-hover:opacity-100", children: _jsx(Pencil, { className: "h-3 w-3", "aria-hidden": "true" }) })) : null] })), error ? _jsx("p", { className: "mt-1 text-xs text-destructive", children: error }) : null] })] }));
|
|
34
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface QuoteSummaryCardProps {
|
|
2
|
+
title: string;
|
|
3
|
+
pipelineName?: string | null;
|
|
4
|
+
stageName?: string | null;
|
|
5
|
+
status: string;
|
|
6
|
+
valueAmountCents?: number | null;
|
|
7
|
+
valueCurrency?: string | null;
|
|
8
|
+
expectedCloseDate?: string | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function QuoteSummaryCard({ title, pipelineName, stageName, status, valueAmountCents, valueCurrency, expectedCloseDate, }: QuoteSummaryCardProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=quote-summary-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-summary-card.d.ts","sourceRoot":"","sources":["../../src/components/quote-summary-card.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,YAAY,EACZ,SAAS,EACT,MAAM,EACN,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,EAAE,qBAAqB,2CAgCvB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, Card, CardContent } from "@voyant-travel/ui/components";
|
|
3
|
+
import { TrendingUp } from "lucide-react";
|
|
4
|
+
import { useCrmUiI18nOrDefault } from "../i18n/index.js";
|
|
5
|
+
import { formatCrmDate, formatCrmMoney } from "./crm-format.js";
|
|
6
|
+
export function QuoteSummaryCard({ title, pipelineName, stageName, status, valueAmountCents, valueCurrency, expectedCloseDate, }) {
|
|
7
|
+
const i18n = useCrmUiI18nOrDefault();
|
|
8
|
+
const { messages } = i18n;
|
|
9
|
+
const statusLabel = messages.common.quoteStatusLabels[status] ?? status;
|
|
10
|
+
return (_jsx(Card, { children: _jsxs(CardContent, { className: "pt-6", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("h2", { className: "text-lg font-semibold leading-tight", children: title }), _jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [pipelineName ?? messages.quoteSummaryCard.unknown, " -", " ", stageName ?? messages.quoteSummaryCard.unknown] })] }), _jsx(Badge, { variant: "outline", children: statusLabel })] }), _jsxs("div", { className: "mt-3 flex items-center gap-2", children: [_jsx(TrendingUp, { className: "size-4 text-muted-foreground", "aria-hidden": "true" }), _jsx("span", { className: "text-lg font-semibold", children: formatCrmMoney(i18n, valueAmountCents, valueCurrency) })] }), expectedCloseDate ? (_jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [messages.quoteSummaryCard.expectedClose, ": ", formatCrmDate(i18n, expectedCloseDate)] })) : null] }) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { QuoteVersionLineRecord } from "../index.js";
|
|
2
|
+
export interface QuoteVersionLinesCardProps {
|
|
3
|
+
currency: string;
|
|
4
|
+
lines: QuoteVersionLineRecord[];
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
onCreate: (input: {
|
|
7
|
+
description: string;
|
|
8
|
+
currency: string;
|
|
9
|
+
quantity: number;
|
|
10
|
+
unitPriceAmountCents: number;
|
|
11
|
+
totalAmountCents: number;
|
|
12
|
+
}) => Promise<void>;
|
|
13
|
+
onUpdate: (lineId: string, input: Partial<{
|
|
14
|
+
description: string;
|
|
15
|
+
quantity: number;
|
|
16
|
+
unitPriceAmountCents: number;
|
|
17
|
+
totalAmountCents: number;
|
|
18
|
+
}>) => Promise<void>;
|
|
19
|
+
onRemove: (lineId: string) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare function QuoteVersionLinesCard({ currency, lines, isLoading, onCreate, onUpdate, onRemove, }: QuoteVersionLinesCardProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
//# sourceMappingURL=quote-version-detail-sections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-version-detail-sections.d.ts","sourceRoot":"","sources":["../../src/components/quote-version-detail-sections.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAGzD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,sBAAsB,EAAE,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE;QAChB,WAAW,EAAE,MAAM,CAAA;QACnB,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,EAAE,MAAM,CAAA;QAChB,oBAAoB,EAAE,MAAM,CAAA;QAC5B,gBAAgB,EAAE,MAAM,CAAA;KACzB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnB,QAAQ,EAAE,CACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,CAAC;QACb,WAAW,EAAE,MAAM,CAAA;QACnB,QAAQ,EAAE,MAAM,CAAA;QAChB,oBAAoB,EAAE,MAAM,CAAA;QAC5B,gBAAgB,EAAE,MAAM,CAAA;KACzB,CAAC,KACC,OAAO,CAAC,IAAI,CAAC,CAAA;IAClB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5C;AAED,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,KAAK,EACL,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAQ,GACT,EAAE,0BAA0B,2CAuH5B"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Card, CardContent, CardHeader, CardTitle, Input, } from "@voyant-travel/ui/components";
|
|
3
|
+
import { CurrencyInput } from "@voyant-travel/ui/components/currency-input";
|
|
4
|
+
import { Loader2, Plus, Trash2 } from "lucide-react";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { useCrmUiI18nOrDefault } from "../i18n/index.js";
|
|
7
|
+
import { formatCrmMoney } from "./crm-format.js";
|
|
8
|
+
export function QuoteVersionLinesCard({ currency, lines, isLoading, onCreate, onUpdate, onRemove, }) {
|
|
9
|
+
const i18n = useCrmUiI18nOrDefault();
|
|
10
|
+
const { messages } = i18n;
|
|
11
|
+
const [newDescription, setNewDescription] = useState("");
|
|
12
|
+
const [newQuantity, setNewQuantity] = useState("1");
|
|
13
|
+
const [newPriceCents, setNewPriceCents] = useState(0);
|
|
14
|
+
const [adding, setAdding] = useState(false);
|
|
15
|
+
const [error, setError] = useState(null);
|
|
16
|
+
async function handleAdd() {
|
|
17
|
+
const description = newDescription.trim();
|
|
18
|
+
if (!description) {
|
|
19
|
+
setError(messages.quoteVersionLinesCard.validation.descriptionRequired);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const quantity = Number.parseInt(newQuantity, 10) || 1;
|
|
23
|
+
const price = newPriceCents ?? 0;
|
|
24
|
+
setAdding(true);
|
|
25
|
+
setError(null);
|
|
26
|
+
try {
|
|
27
|
+
await onCreate({
|
|
28
|
+
description,
|
|
29
|
+
currency,
|
|
30
|
+
quantity,
|
|
31
|
+
unitPriceAmountCents: price,
|
|
32
|
+
totalAmountCents: quantity * price,
|
|
33
|
+
});
|
|
34
|
+
setNewDescription("");
|
|
35
|
+
setNewQuantity("1");
|
|
36
|
+
setNewPriceCents(0);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
setError(err instanceof Error ? err.message : messages.quoteVersionLinesCard.validation.addFailed);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
setAdding(false);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const subtotal = lines.reduce((sum, line) => sum + line.totalAmountCents, 0);
|
|
46
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsx(CardTitle, { className: "text-sm font-semibold", children: messages.quoteVersionLinesCard.title }) }), _jsxs(CardContent, { children: [isLoading ? (_jsx("div", { className: "flex justify-center py-6", children: _jsx(Loader2, { className: "size-5 animate-spin text-muted-foreground" }) })) : lines.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.quoteVersionLinesCard.empty })) : (_jsx("ul", { className: "divide-y", children: lines.map((line) => (_jsx(QuoteVersionLineRow, { currency: currency, line: line, onUpdate: (input) => onUpdate(line.id, input), onRemove: () => onRemove(line.id) }, line.id))) })), _jsxs("div", { className: "mt-3 flex flex-col gap-2 border-t pt-3", children: [_jsxs("div", { className: "grid grid-cols-12 gap-2", children: [_jsx(Input, { className: "col-span-6 h-8 text-sm", value: newDescription, onChange: (event) => setNewDescription(event.target.value), placeholder: messages.quoteVersionLinesCard.fields.description }), _jsx(Input, { className: "col-span-2 h-8 text-sm", type: "number", min: 1, value: newQuantity, onChange: (event) => setNewQuantity(event.target.value), placeholder: messages.quoteVersionLinesCard.fields.quantity }), _jsx(CurrencyInput, { className: "col-span-3 h-8 text-sm", inputClassName: "h-8 text-sm", value: newPriceCents, onChange: setNewPriceCents, currency: currency, placeholder: messages.quoteVersionLinesCard.fields.priceCents }), _jsx(Button, { size: "sm", className: "col-span-1 h-8", onClick: () => void handleAdd(), disabled: adding, "aria-label": messages.common.create, children: adding ? (_jsx(Loader2, { className: "size-3.5 animate-spin", "aria-hidden": "true" })) : (_jsx(Plus, { className: "size-3.5", "aria-hidden": "true" })) })] }), error ? _jsx("p", { className: "text-xs text-destructive", children: error }) : null] }), _jsxs("div", { className: "mt-3 flex items-center justify-between border-t pt-3 text-sm", children: [_jsx("span", { className: "text-muted-foreground", children: messages.quoteVersionLinesCard.subtotal }), _jsx("span", { className: "font-semibold", children: formatCrmMoney(i18n, subtotal, currency) })] })] })] }));
|
|
47
|
+
}
|
|
48
|
+
function QuoteVersionLineRow({ currency, line, onUpdate, onRemove, }) {
|
|
49
|
+
const i18n = useCrmUiI18nOrDefault();
|
|
50
|
+
const [removing, setRemoving] = useState(false);
|
|
51
|
+
const [draftPriceCents, setDraftPriceCents] = useState(line.unitPriceAmountCents);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
setDraftPriceCents(line.unitPriceAmountCents);
|
|
54
|
+
}, [line.unitPriceAmountCents]);
|
|
55
|
+
async function handleRemove() {
|
|
56
|
+
setRemoving(true);
|
|
57
|
+
try {
|
|
58
|
+
await onRemove();
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
setRemoving(false);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function handleQuantity(value) {
|
|
65
|
+
const quantity = Number.parseInt(value, 10);
|
|
66
|
+
if (!Number.isFinite(quantity) || quantity < 1)
|
|
67
|
+
return;
|
|
68
|
+
await onUpdate({
|
|
69
|
+
quantity,
|
|
70
|
+
totalAmountCents: quantity * line.unitPriceAmountCents,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function handlePrice(value) {
|
|
74
|
+
if (value == null || value < 0 || value === line.unitPriceAmountCents)
|
|
75
|
+
return;
|
|
76
|
+
await onUpdate({
|
|
77
|
+
unitPriceAmountCents: value,
|
|
78
|
+
totalAmountCents: line.quantity * value,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return (_jsx("li", { className: "py-2", children: _jsxs("div", { className: "grid grid-cols-12 items-center gap-2", children: [_jsx(Input, { className: "col-span-6 h-8 text-sm", defaultValue: line.description, onBlur: (event) => {
|
|
82
|
+
const value = event.target.value.trim();
|
|
83
|
+
if (value && value !== line.description) {
|
|
84
|
+
void onUpdate({ description: value });
|
|
85
|
+
}
|
|
86
|
+
} }), _jsx(Input, { className: "col-span-2 h-8 text-sm", type: "number", min: 1, defaultValue: line.quantity, onBlur: (event) => void handleQuantity(event.target.value) }), _jsx(CurrencyInput, { className: "col-span-2 h-8 text-sm", inputClassName: "h-8 text-sm", value: draftPriceCents, onChange: setDraftPriceCents, onBlur: () => void handlePrice(draftPriceCents), currency: currency }), _jsx("span", { className: "col-span-1 text-right text-sm font-medium", children: formatCrmMoney(i18n, line.totalAmountCents, currency) }), _jsx(Button, { size: "sm", variant: "ghost", className: "col-span-1 size-8 p-0", onClick: () => void handleRemove(), disabled: removing, children: removing ? (_jsx(Loader2, { className: "size-3.5 animate-spin", "aria-hidden": "true" })) : (_jsx(Trash2, { className: "size-3.5", "aria-hidden": "true" })) })] }) }));
|
|
87
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type QuoteVersionRecord } from "../index.js";
|
|
2
|
+
export interface QuoteVersionsPageProps {
|
|
3
|
+
onQuoteVersionOpen?: (quoteVersion: QuoteVersionRecord) => void;
|
|
4
|
+
onQuoteVersionCreated?: (quoteVersion: QuoteVersionRecord) => void;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function QuoteVersionsPage({ onQuoteVersionOpen, onQuoteVersionCreated, className, }?: QuoteVersionsPageProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=quote-versions-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-versions-page.d.ts","sourceRoot":"","sources":["../../src/components/quote-versions-page.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAE,KAAK,kBAAkB,EAAoB,MAAM,aAAa,CAAA;AAIvE,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC/D,qBAAqB,CAAC,EAAE,CAAC,YAAY,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAClE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,kBAAkB,EAClB,qBAAqB,EACrB,SAAS,GACV,GAAE,sBAA2B,2CAwH7B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyant-travel/ui/components";
|
|
3
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@voyant-travel/ui/components/table";
|
|
4
|
+
import { cn } from "@voyant-travel/ui/lib/utils";
|
|
5
|
+
import { Loader2, Plus } from "lucide-react";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { useCrmUiI18nOrDefault } from "../i18n/index.js";
|
|
8
|
+
import { useQuoteVersions } from "../index.js";
|
|
9
|
+
import { CreateQuoteVersionDialog } from "./create-quote-version-dialog.js";
|
|
10
|
+
import { formatCrmDate, formatCrmMoney, formatCrmRelative } from "./crm-format.js";
|
|
11
|
+
export function QuoteVersionsPage({ onQuoteVersionOpen, onQuoteVersionCreated, className, } = {}) {
|
|
12
|
+
const i18n = useCrmUiI18nOrDefault();
|
|
13
|
+
const { messages } = i18n;
|
|
14
|
+
const [statusFilter, setStatusFilter] = useState("all");
|
|
15
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
16
|
+
const { data, isPending, isError } = useQuoteVersions({
|
|
17
|
+
status: statusFilter === "all" ? undefined : statusFilter,
|
|
18
|
+
limit: 100,
|
|
19
|
+
});
|
|
20
|
+
const quoteVersions = data?.data ?? [];
|
|
21
|
+
const quoteVersionStatusOptions = Object.entries(messages.common.quoteVersionStatusLabels).map(([value, label]) => ({ value, label }));
|
|
22
|
+
const handleCreated = (quoteVersion) => {
|
|
23
|
+
onQuoteVersionCreated?.(quoteVersion);
|
|
24
|
+
onQuoteVersionOpen?.(quoteVersion);
|
|
25
|
+
};
|
|
26
|
+
return (_jsxs("div", { "data-slot": "quote-versions-page", className: cn("flex flex-col gap-6 p-6", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: messages.quoteVersionsPage.title }), _jsx("p", { className: "text-sm text-muted-foreground", children: messages.quoteVersionsPage.description })] }), _jsxs(Button, { onClick: () => setDialogOpen(true), children: [_jsx(Plus, { className: "mr-2 size-4", "aria-hidden": "true" }), messages.quoteVersionsPage.create] })] }), _jsx("div", { className: "flex flex-wrap items-center gap-3", children: _jsxs(Select, { value: statusFilter, onValueChange: (value) => setStatusFilter(value ?? "all"), children: [_jsx(SelectTrigger, { className: "w-[180px] text-sm", children: _jsx(SelectValue, { placeholder: messages.quoteVersionsPage.filters.status }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: messages.quoteVersionsPage.filters.allStatuses }), quoteVersionStatusOptions.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value)))] })] }) }), _jsx("div", { className: "overflow-hidden rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: messages.quoteVersionsPage.columns.quoteVersion }), _jsx(TableHead, { children: messages.quoteVersionsPage.columns.status }), _jsx(TableHead, { children: messages.quoteVersionsPage.columns.total }), _jsx(TableHead, { children: messages.quoteVersionsPage.columns.validUntil }), _jsx(TableHead, { children: messages.quoteVersionsPage.columns.updated })] }) }), _jsx(TableBody, { children: isPending ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, className: "h-24 text-center", children: _jsx(Loader2, { className: "mx-auto size-4 animate-spin text-muted-foreground" }) }) })) : isError ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, className: "h-24 text-center text-sm text-destructive", children: messages.quoteVersionsPage.loadFailed }) })) : quoteVersions.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, className: "h-24 text-center text-sm text-muted-foreground", children: messages.quoteVersionsPage.empty }) })) : (quoteVersions.map((quoteVersion) => {
|
|
27
|
+
const statusLabel = messages.common.quoteVersionStatusLabels[quoteVersion.status] ?? quoteVersion.status;
|
|
28
|
+
return (_jsxs(TableRow, { onClick: () => onQuoteVersionOpen?.(quoteVersion), className: cn(onQuoteVersionOpen && "cursor-pointer"), children: [_jsx(TableCell, { className: "font-mono text-xs", children: quoteVersion.label ?? quoteVersion.id.slice(-8) }), _jsx(TableCell, { children: _jsx(Badge, { variant: "secondary", children: statusLabel }) }), _jsx(TableCell, { className: "font-medium", children: formatCrmMoney(i18n, quoteVersion.totalAmountCents, quoteVersion.currency) }), _jsx(TableCell, { children: formatCrmDate(i18n, quoteVersion.validUntil) }), _jsx(TableCell, { className: "text-muted-foreground", children: formatCrmRelative(i18n, quoteVersion.updatedAt) })] }, quoteVersion.id));
|
|
29
|
+
})) })] }) }), _jsx(CreateQuoteVersionDialog, { open: dialogOpen, onOpenChange: setDialogOpen, onCreated: handleCreated })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { QuoteRecord as QuoteData, StageRecord as StageData } from "../index.js";
|
|
2
|
+
export interface QuotesBoardProps {
|
|
3
|
+
stages: StageData[];
|
|
4
|
+
quotesByStage: Map<string, QuoteData[]>;
|
|
5
|
+
onQuoteOpen?: (quote: QuoteData) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function QuotesBoard({ stages, quotesByStage, onQuoteOpen }: QuotesBoardProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=quotes-board.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quotes-board.d.ts","sourceRoot":"","sources":["../../src/components/quotes-board.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,IAAI,SAAS,EAAE,WAAW,IAAI,SAAS,EAAE,MAAM,aAAa,CAAA;AAGrF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;IACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CACzC;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,gBAAgB,2CA6CnF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Card } from "@voyant-travel/ui/components";
|
|
3
|
+
import { ScrollArea, ScrollBar } from "@voyant-travel/ui/components/scroll-area";
|
|
4
|
+
import { TrendingUp } from "lucide-react";
|
|
5
|
+
import { useCrmUiI18nOrDefault } from "../i18n/index.js";
|
|
6
|
+
import { formatCrmDate, formatCrmMoney } from "./crm-format.js";
|
|
7
|
+
export function QuotesBoard({ stages, quotesByStage, onQuoteOpen }) {
|
|
8
|
+
const i18n = useCrmUiI18nOrDefault();
|
|
9
|
+
const { messages } = i18n;
|
|
10
|
+
return (_jsxs(ScrollArea, { className: "flex-1", children: [_jsx("div", { className: "flex gap-3 pb-2", children: stages.map((stage) => {
|
|
11
|
+
const quotes = quotesByStage.get(stage.id) ?? [];
|
|
12
|
+
const total = quotes.reduce((sum, quote) => sum + (quote.valueAmountCents ?? 0), 0);
|
|
13
|
+
const primaryCurrency = quotes[0]?.valueCurrency ?? null;
|
|
14
|
+
return (_jsxs("div", { className: "flex w-[280px] min-w-[280px] flex-col gap-2 rounded-md border bg-muted/30 p-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 px-2 py-1", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: stage.name || messages.quotesBoard.fallbackName }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [i18n.formatNumber(quotes.length), " -", " ", formatCrmMoney(i18n, total, primaryCurrency)] })] }), stage.probability != null ? (_jsxs("span", { className: "rounded border px-1.5 py-0.5 text-[10px]", children: [i18n.formatNumber(stage.probability), "%"] })) : null] }), _jsx("div", { className: "flex flex-col gap-2", children: quotes.map((quote) => (_jsx(QuoteBoardCard, { quote: quote, onOpen: onQuoteOpen }, quote.id))) })] }, stage.id));
|
|
15
|
+
}) }), _jsx(ScrollBar, { orientation: "horizontal" })] }));
|
|
16
|
+
}
|
|
17
|
+
function QuoteBoardCard({ quote, onOpen, }) {
|
|
18
|
+
const i18n = useCrmUiI18nOrDefault();
|
|
19
|
+
const content = (_jsxs(_Fragment, { children: [_jsx("p", { className: "line-clamp-2 font-medium", children: quote.title }), _jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(TrendingUp, { className: "size-3", "aria-hidden": "true" }), formatCrmMoney(i18n, quote.valueAmountCents, quote.valueCurrency)] }), quote.expectedCloseDate ? (_jsx("span", { className: "text-xs text-muted-foreground", children: formatCrmDate(i18n, quote.expectedCloseDate) })) : null] })] }));
|
|
20
|
+
if (onOpen) {
|
|
21
|
+
return (_jsx(Card, { className: "p-3 text-sm", children: _jsx("button", { type: "button", className: "block w-full text-left", onClick: () => onOpen(quote), children: content }) }));
|
|
22
|
+
}
|
|
23
|
+
return _jsx(Card, { className: "p-3 text-sm", children: content });
|
|
24
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { type CreatePipelineInput, type CreateStageInput, type UpdatePipelineInput, type UpdateStageInput, usePipelineMutation, } from "./use-pipeline-mutation.js";
|
|
2
|
+
export { type UsePipelineOptions, type UsePipelinesOptions, usePipeline, usePipelines, } from "./use-pipelines.js";
|
|
3
|
+
export { type UseQuoteOptions, useQuote } from "./use-quote.js";
|
|
4
|
+
export { type CreateQuoteInput, type UpdateQuoteInput, useQuoteMutation, } from "./use-quote-mutation.js";
|
|
5
|
+
export { type UseQuoteVersionOptions, useQuoteVersion, useQuoteVersionLines, } from "./use-quote-version.js";
|
|
6
|
+
export { type CreateQuoteVersionInput, type CreateQuoteVersionLineInput, type ExpireQuoteVersionsInput, type SendQuoteVersionInput, type UpdateQuoteVersionInput, type UpdateQuoteVersionLineInput, useQuoteVersionMutation, } from "./use-quote-version-mutation.js";
|
|
7
|
+
export { type UseQuoteVersionsOptions, useQuoteVersions, } from "./use-quote-versions.js";
|
|
8
|
+
export { type UseQuotesOptions, useQuotes } from "./use-quotes.js";
|
|
9
|
+
export { type UseStageOptions, type UseStagesOptions, useStage, useStages, } from "./use-stages.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,mBAAmB,GACpB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,WAAW,EACX,YAAY,GACb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC/D,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,gBAAgB,GACjB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,KAAK,sBAAsB,EAC3B,eAAe,EACf,oBAAoB,GACrB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,KAAK,uBAAuB,EAC5B,gBAAgB,GACjB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAClE,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,QAAQ,EACR,SAAS,GACV,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { usePipelineMutation, } from "./use-pipeline-mutation.js";
|
|
2
|
+
export { usePipeline, usePipelines, } from "./use-pipelines.js";
|
|
3
|
+
export { useQuote } from "./use-quote.js";
|
|
4
|
+
export { useQuoteMutation, } from "./use-quote-mutation.js";
|
|
5
|
+
export { useQuoteVersion, useQuoteVersionLines, } from "./use-quote-version.js";
|
|
6
|
+
export { useQuoteVersionMutation, } from "./use-quote-version-mutation.js";
|
|
7
|
+
export { useQuoteVersions, } from "./use-quote-versions.js";
|
|
8
|
+
export { useQuotes } from "./use-quotes.js";
|
|
9
|
+
export { useStage, useStages, } from "./use-stages.js";
|