@voyantjs/legal-ui 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,20 @@ pnpm add @voyantjs/legal-ui @voyantjs/legal-react @voyantjs/ui @tanstack/react-q
12
12
 
13
13
  All components accept a `className` prop and merge it with `cn()`. Wrap or compose to extend; use the registry copy-paste path (`npx shadcn add @voyant/...`) for components you want to fork outright.
14
14
 
15
+ ## I18n
16
+
17
+ Components render English by default. To localize them, wrap your UI in
18
+ `LegalUiMessagesProvider` and import only the locales your app supports.
19
+
20
+ ```tsx
21
+ import { LegalUiMessagesProvider } from "@voyantjs/legal-ui"
22
+ import { legalUiEn } from "@voyantjs/legal-ui/i18n/en"
23
+ import { legalUiRo } from "@voyantjs/legal-ui/i18n/ro"
24
+ ```
25
+
26
+ English-only apps should import only `./i18n/en`. Bilingual apps can import
27
+ `./i18n/en` and `./i18n/ro`.
28
+
15
29
  ## Not included (registry-only)
16
30
 
17
31
  Some components couple to TanStack Router or template-local helpers and remain available only via the shadcn registry: `contract-detail-page`, `contracts-page`, `policies-page`, `policy-detail-page`, `template-detail-page`, `templates-page`. Import via `npx shadcn add @voyant/<component>` and customize per-project.
@@ -1 +1 @@
1
- {"version":3,"file":"attachment-dialog.d.ts","sourceRoot":"","sources":["../../src/components/attachment-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,6BAA6B,EAEnC,MAAM,uBAAuB,CAAA;AA8B9B,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,6BAA6B,CAAA;IAC1C,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,SAAS,GACV,EAAE,qBAAqB,2CAyGvB"}
1
+ {"version":3,"file":"attachment-dialog.d.ts","sourceRoot":"","sources":["../../src/components/attachment-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,6BAA6B,EAEnC,MAAM,uBAAuB,CAAA;AAkC9B,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,6BAA6B,CAAA;IAC1C,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,SAAS,GACV,EAAE,qBAAqB,2CAkIvB"}
@@ -6,22 +6,27 @@ import { Loader2 } from "lucide-react";
6
6
  import { useEffect } from "react";
7
7
  import { useForm } from "react-hook-form";
8
8
  import { z } from "zod/v4";
9
- const attachmentFormSchema = z.object({
10
- name: z.string().min(1, "Name is required"),
11
- kind: z.string().min(1).optional(),
12
- mimeType: z.string().optional(),
13
- fileSize: z.coerce.number().int().optional(),
14
- storageKey: z.string().optional(),
15
- checksum: z.string().optional(),
16
- });
9
+ import { useLegalUiMessagesOrDefault } from "../i18n";
10
+ function createAttachmentFormSchema(messages) {
11
+ return z.object({
12
+ name: z.string().min(1, messages.attachmentDialog.validation.nameRequired),
13
+ kind: z.string().min(1).optional(),
14
+ mimeType: z.string().optional(),
15
+ fileSize: z.coerce.number().int().optional(),
16
+ storageKey: z.string().optional(),
17
+ checksum: z.string().optional(),
18
+ });
19
+ }
17
20
  export function AttachmentDialog({ open, onOpenChange, contractId, attachment, onSuccess, }) {
18
21
  const isEditing = !!attachment;
19
22
  const { create, update } = useLegalContractAttachmentMutation();
23
+ const messages = useLegalUiMessagesOrDefault();
24
+ const attachmentFormSchema = createAttachmentFormSchema(messages);
20
25
  const form = useForm({
21
26
  resolver: zodResolver(attachmentFormSchema),
22
27
  defaultValues: {
23
28
  name: "",
24
- kind: "appendix",
29
+ kind: "appendix", // i18n-literal-ok domain default attachment kind
25
30
  mimeType: "",
26
31
  fileSize: undefined,
27
32
  storageKey: "",
@@ -46,7 +51,7 @@ export function AttachmentDialog({ open, onOpenChange, contractId, attachment, o
46
51
  const onSubmit = async (values) => {
47
52
  const payload = {
48
53
  name: values.name,
49
- kind: values.kind || "appendix",
54
+ kind: values.kind || "appendix", // i18n-literal-ok domain default attachment kind
50
55
  mimeType: values.mimeType || undefined,
51
56
  fileSize: values.fileSize || undefined,
52
57
  storageKey: values.storageKey || undefined,
@@ -60,5 +65,7 @@ export function AttachmentDialog({ open, onOpenChange, contractId, attachment, o
60
65
  }
61
66
  onSuccess();
62
67
  };
63
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Attachment" : "Add Attachment" }) }), _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: "Name" }), _jsx(Input, { ...form.register("name"), placeholder: "Attachment name" }), form.formState.errors.name && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.name.message }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Kind" }), _jsx(Input, { ...form.register("kind"), placeholder: "appendix" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "MIME Type" }), _jsx(Input, { ...form.register("mimeType"), placeholder: "application/pdf" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "File Size" }), _jsx(Input, { ...form.register("fileSize"), type: "number", placeholder: "Bytes" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Checksum" }), _jsx(Input, { ...form.register("checksum"), placeholder: "Optional" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Storage Key" }), _jsx(Input, { ...form.register("storageKey"), placeholder: "Optional storage reference" })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: form.formState.isSubmitting, children: [form.formState.isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing ? "Save Changes" : "Add Attachment"] })] })] })] }) }));
68
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
69
+ ? messages.attachmentDialog.titles.edit
70
+ : messages.attachmentDialog.titles.create }) }), _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: messages.attachmentDialog.fields.name }), _jsx(Input, { ...form.register("name"), placeholder: messages.attachmentDialog.placeholders.name }), form.formState.errors.name && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.name.message }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.attachmentDialog.fields.kind }), _jsx(Input, { ...form.register("kind"), placeholder: messages.attachmentDialog.placeholders.kind })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.attachmentDialog.fields.mimeType }), _jsx(Input, { ...form.register("mimeType"), placeholder: messages.attachmentDialog.placeholders.mimeType })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.attachmentDialog.fields.fileSize }), _jsx(Input, { ...form.register("fileSize"), type: "number", placeholder: messages.attachmentDialog.placeholders.fileSize })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.attachmentDialog.fields.checksum }), _jsx(Input, { ...form.register("checksum"), placeholder: messages.attachmentDialog.placeholders.checksum })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.attachmentDialog.fields.storageKey }), _jsx(Input, { ...form.register("storageKey"), placeholder: messages.attachmentDialog.placeholders.storageKey })] })] }), _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: "mr-2 h-4 w-4 animate-spin" }), isEditing ? messages.common.saveChanges : messages.attachmentDialog.actions.create] })] })] })] }) }));
64
71
  }
@@ -1,16 +1,5 @@
1
- export interface BookingContractCardLabels {
2
- heading?: string;
3
- empty?: string;
4
- /** Button text when the contract has no document yet. */
5
- generate?: string;
6
- /** Button text when the contract already has a document. */
7
- regenerate?: string;
8
- download?: string;
9
- noAttachments?: string;
10
- issuedAt?: string;
11
- contractNumber?: string;
12
- unsaved?: string;
13
- }
1
+ import type { LegalUiMessages } from "../i18n/messages";
2
+ export type BookingContractCardLabels = Partial<Omit<LegalUiMessages["bookingContractCard"], "contractStatusLabels">>;
14
3
  export interface BookingContractCardProps {
15
4
  /** Booking whose contracts we list. Required — the card filters server-side. */
16
5
  bookingId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"booking-contract-card.d.ts","sourceRoot":"","sources":["../../src/components/booking-contract-card.tsx"],"names":[],"mappings":"AA0BA,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAcD,MAAM,WAAW,wBAAwB;IACvC,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,yBAAyB,CAAA;CACnC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAiC9F"}
1
+ {"version":3,"file":"booking-contract-card.d.ts","sourceRoot":"","sources":["../../src/components/booking-contract-card.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAgBvD,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAC7C,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,EAAE,sBAAsB,CAAC,CACrE,CAAA;AAED,MAAM,WAAW,wBAAwB;IACvC,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,yBAAyB,CAAA;CACnC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAmC9F"}
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useLegalContractAttachments, useLegalContractMutation, useLegalContracts, } from "@voyantjs/legal-react";
4
4
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
5
  import { Download, FileText, Loader2, RotateCw } from "lucide-react";
6
+ import { useLegalUiI18nOrDefault } from "../i18n";
6
7
  /**
7
8
  * Status → badge style map. Keeps the card visually in sync with the
8
9
  * contract detail page (same variant names, same ordering of severity).
@@ -16,17 +17,6 @@ const STATUS_VARIANT = {
16
17
  expired: "destructive",
17
18
  void: "destructive",
18
19
  };
19
- const DEFAULT_LABELS = {
20
- heading: "Contract",
21
- empty: "No contract has been generated for this booking yet.",
22
- generate: "Generate",
23
- regenerate: "Regenerate",
24
- download: "Download",
25
- noAttachments: "No documents attached yet.",
26
- issuedAt: "Issued",
27
- contractNumber: "#",
28
- unsaved: "Pending",
29
- };
30
20
  /**
31
21
  * Operator booking-detail "Contract" card. Mount next to the payments / docs
32
22
  * card on the booking detail page. Responsibilities are deliberately narrow:
@@ -43,12 +33,14 @@ const DEFAULT_LABELS = {
43
33
  * would require a template picker, out of scope here).
44
34
  */
45
35
  export function BookingContractCard({ bookingId, apiBaseUrl, labels }) {
46
- const merged = { ...DEFAULT_LABELS, ...labels };
36
+ const i18n = useLegalUiI18nOrDefault();
37
+ const merged = { ...i18n.messages.bookingContractCard, ...labels };
47
38
  const contractsQuery = useLegalContracts({ bookingId, limit: 25 });
48
39
  const contracts = contractsQuery.data?.data ?? [];
49
- return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(FileText, { className: "h-4 w-4" }), merged.heading] }) }), _jsx(CardContent, { className: "flex flex-col gap-3", children: contractsQuery.isLoading ? (_jsx("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: _jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) })) : contracts.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: merged.empty })) : (contracts.map((contract) => (_jsx(BookingContractRow, { contract: contract, apiBaseUrl: apiBaseUrl, labels: merged }, contract.id)))) })] }));
40
+ return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(FileText, { className: "h-4 w-4" }), merged.heading] }) }), _jsx(CardContent, { className: "flex flex-col gap-3", children: contractsQuery.isLoading ? (_jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }), i18n.messages.common.loading] })) : contracts.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: merged.empty })) : (contracts.map((contract) => (_jsx(BookingContractRow, { contract: contract, apiBaseUrl: apiBaseUrl, labels: merged }, contract.id)))) })] }));
50
41
  }
51
42
  function BookingContractRow({ contract, apiBaseUrl, labels, }) {
43
+ const i18n = useLegalUiI18nOrDefault();
52
44
  const attachmentsQuery = useLegalContractAttachments({ contractId: contract.id });
53
45
  const attachments = attachmentsQuery.data ?? [];
54
46
  const documentAttachments = attachments.filter((a) => a.kind === "document");
@@ -59,14 +51,17 @@ function BookingContractRow({ contract, apiBaseUrl, labels, }) {
59
51
  const mutation = hasDocument ? regenerateDocument : generateDocument;
60
52
  mutation.mutate({ id: contract.id, input: { replaceExisting: true, kind: "document" } });
61
53
  };
62
- return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsxs("span", { className: "font-medium", children: [labels.contractNumber, contract.contractNumber ?? labels.unsaved] }), _jsx(Badge, { variant: STATUS_VARIANT[contract.status] ?? "outline", className: "text-[10px]", children: contract.status })] }), _jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleGenerate, disabled: isPending, children: [isPending ? (_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(RotateCw, { className: "h-3.5 w-3.5" })), _jsx("span", { className: "ml-1 text-xs", children: hasDocument ? labels.regenerate : labels.generate })] })] }), contract.issuedAt ? (_jsxs("p", { className: "text-[11px] text-muted-foreground", children: [labels.issuedAt, ": ", new Date(contract.issuedAt).toLocaleDateString()] })) : null, documentAttachments.length > 0 ? (_jsx("div", { className: "flex flex-col gap-1", children: documentAttachments.map((attachment) => (_jsx(AttachmentDownloadRow, { attachment: attachment, apiBaseUrl: apiBaseUrl, downloadLabel: labels.download }, attachment.id))) })) : (_jsx("p", { className: "text-[11px] text-muted-foreground", children: labels.noAttachments }))] }));
54
+ return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsxs("span", { className: "font-medium", children: [labels.contractNumber, contract.contractNumber ?? labels.unsaved] }), _jsx(Badge, { variant: STATUS_VARIANT[contract.status] ?? "outline", className: "text-[10px]", children: i18n.messages.bookingContractCard.contractStatusLabels[contract.status] })] }), _jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleGenerate, disabled: isPending, children: [isPending ? (_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" })) : (_jsx(RotateCw, { className: "h-3.5 w-3.5" })), _jsx("span", { className: "ml-1 text-xs", children: hasDocument ? labels.regenerate : labels.generate })] })] }), contract.issuedAt ? (_jsxs("p", { className: "text-[11px] text-muted-foreground", children: [labels.issuedAt, ": ", i18n.formatDate(contract.issuedAt)] })) : null, documentAttachments.length > 0 ? (_jsx("div", { className: "flex flex-col gap-1", children: documentAttachments.map((attachment) => (_jsx(AttachmentDownloadRow, { attachment: attachment, apiBaseUrl: apiBaseUrl, downloadLabel: labels.download }, attachment.id))) })) : (_jsx("p", { className: "text-[11px] text-muted-foreground", children: labels.noAttachments }))] }));
63
55
  }
64
56
  function AttachmentDownloadRow({ attachment, apiBaseUrl, downloadLabel, }) {
57
+ const i18n = useLegalUiI18nOrDefault();
65
58
  // The download endpoint returns a 302 to the signed URL. A plain <a> link
66
59
  // with target="_blank" lets the browser follow it and open the file in a
67
60
  // new tab. When apiBaseUrl is omitted we fall back to a relative URL,
68
61
  // which is correct for same-origin admin apps.
69
62
  const href = `${apiBaseUrl ?? ""}/v1/admin/legal/contracts/attachments/${attachment.id}/download`;
70
- const sizeKb = typeof attachment.fileSize === "number" ? `${Math.round(attachment.fileSize / 1024)} KB` : null;
63
+ const sizeKb = typeof attachment.fileSize === "number"
64
+ ? `${i18n.formatNumber(Math.round(attachment.fileSize / 1024))} ${i18n.messages.common.kilobytes}`
65
+ : null;
71
66
  return (_jsxs("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "flex items-center justify-between gap-2 rounded-md border px-2 py-1.5 text-xs hover:bg-muted", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx(FileText, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: attachment.name }), sizeKb ? _jsxs("span", { className: "text-muted-foreground", children: ["\u00B7 ", sizeKb] }) : null] }), _jsxs("span", { className: "flex items-center gap-1 text-muted-foreground", children: [_jsx(Download, { className: "h-3 w-3" }), downloadLabel] })] }));
72
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"policy-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/policy-rule-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAA8B,MAAM,uBAAuB,CAAA;AAsC9F,MAAM,MAAM,QAAQ,GAAG,qBAAqB,CAAA;AAE5C,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAiBD,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,EAAE,qBAAqB,2CAuKvB"}
1
+ {"version":3,"file":"policy-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/policy-rule-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAA8B,MAAM,uBAAuB,CAAA;AAqD9F,MAAM,MAAM,QAAQ,GAAG,qBAAqB,CAAA;AAE5C,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,IAAI,EACJ,SAAS,GACV,EAAE,qBAAqB,2CAkMvB"}
@@ -7,32 +7,38 @@ import { Loader2 } from "lucide-react";
7
7
  import { useEffect } from "react";
8
8
  import { useForm } from "react-hook-form";
9
9
  import { z } from "zod/v4";
10
- const ruleFormSchema = z.object({
11
- ruleType: z.enum(["window", "percentage", "flat_amount", "date_range", "custom"]),
12
- label: z.string().optional(),
13
- daysBeforeDeparture: z.coerce.number().int().optional(),
14
- refundPercent: z.coerce.number().int().min(0).max(10000).optional(),
15
- refundType: z.enum(["cash", "credit", "cash_or_credit", "none"]).optional(),
16
- flatAmountCents: z.coerce.number().int().optional(),
17
- currency: z.string().optional(),
18
- sortOrder: z.coerce.number().int().optional(),
19
- });
20
- const RULE_TYPES = [
21
- { value: "window", label: "Window" },
22
- { value: "percentage", label: "Percentage" },
23
- { value: "flat_amount", label: "Flat Amount" },
24
- { value: "date_range", label: "Date Range" },
25
- { value: "custom", label: "Custom" },
26
- ];
27
- const REFUND_TYPES = [
28
- { value: "cash", label: "Cash" },
29
- { value: "credit", label: "Credit" },
30
- { value: "cash_or_credit", label: "Cash or Credit" },
31
- { value: "none", label: "None" },
32
- ];
10
+ import { useLegalUiMessagesOrDefault } from "../i18n";
11
+ import { legalRefundTypes, legalRuleTypes, } from "../i18n/messages";
12
+ function createRuleFormSchema(messages) {
13
+ return z.object({
14
+ ruleType: z.enum(legalRuleTypes),
15
+ label: z.string().optional(),
16
+ daysBeforeDeparture: z.coerce.number().int().optional(),
17
+ refundPercent: z.coerce
18
+ .number()
19
+ .int()
20
+ .min(0, messages.policyRuleDialog.validation.refundPercentMin)
21
+ .max(10000, messages.policyRuleDialog.validation.refundPercentMax)
22
+ .optional(),
23
+ refundType: z.enum(legalRefundTypes).optional(),
24
+ flatAmountCents: z.coerce.number().int().optional(),
25
+ currency: z.string().optional(),
26
+ sortOrder: z.coerce.number().int().optional(),
27
+ });
28
+ }
33
29
  export function PolicyRuleDialog({ open, onOpenChange, versionId, rule, onSuccess, }) {
34
30
  const isEditing = !!rule;
35
31
  const { create, update } = useLegalPolicyRuleMutation();
32
+ const messages = useLegalUiMessagesOrDefault();
33
+ const ruleFormSchema = createRuleFormSchema(messages);
34
+ const ruleTypeItems = legalRuleTypes.map((value) => ({
35
+ value,
36
+ label: messages.policyRuleDialog.ruleTypeLabels[value],
37
+ }));
38
+ const refundTypeItems = legalRefundTypes.map((value) => ({
39
+ value,
40
+ label: messages.policyRuleDialog.refundTypeLabels[value],
41
+ }));
36
42
  const form = useForm({
37
43
  resolver: zodResolver(ruleFormSchema),
38
44
  defaultValues: {
@@ -82,8 +88,10 @@ export function PolicyRuleDialog({ open, onOpenChange, versionId, rule, onSucces
82
88
  }
83
89
  onSuccess();
84
90
  };
85
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Rule" : "New Rule" }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Rule Type" }), _jsxs(Select, { items: RULE_TYPES, value: form.watch("ruleType"), onValueChange: (v) => form.setValue("ruleType", v), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: RULE_TYPES.map((t) => (_jsx(SelectItem, { value: t.value, children: t.label }, t.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sort Order" }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Label" }), _jsx(Input, { ...form.register("label"), placeholder: "e.g. 30+ days before departure" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Days Before Departure" }), _jsx(Input, { ...form.register("daysBeforeDeparture"), type: "number", placeholder: "e.g. 30" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Refund Percent (basis points)" }), _jsx(Input, { ...form.register("refundPercent"), type: "number", placeholder: "e.g. 10000 = 100%" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Refund Type" }), _jsxs(Select, { items: REFUND_TYPES, value: form.watch("refundType") ?? "", onValueChange: (v) => form.setValue("refundType", (v || undefined)), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: "Select..." }) }), _jsx(SelectContent, { children: REFUND_TYPES.map((t) => (_jsx(SelectItem, { value: t.value, children: t.label }, t.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Currency" }), _jsx(CurrencyCombobox, { value: form.watch("currency") || null, onChange: (next) => form.setValue("currency", next ?? "EUR", {
91
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
92
+ ? messages.policyRuleDialog.titles.edit
93
+ : messages.policyRuleDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.ruleType }), _jsxs(Select, { items: ruleTypeItems, value: form.watch("ruleType"), onValueChange: (v) => form.setValue("ruleType", v, { shouldValidate: true }), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: ruleTypeItems.map((t) => (_jsx(SelectItem, { value: t.value, children: t.label }, t.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.sortOrder }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.label }), _jsx(Input, { ...form.register("label"), placeholder: messages.policyRuleDialog.placeholders.label })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.daysBeforeDeparture }), _jsx(Input, { ...form.register("daysBeforeDeparture"), type: "number", placeholder: messages.policyRuleDialog.placeholders.daysBeforeDeparture })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.refundPercent }), _jsx(Input, { ...form.register("refundPercent"), type: "number", placeholder: messages.policyRuleDialog.placeholders.refundPercent })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.refundType }), _jsxs(Select, { items: refundTypeItems, value: form.watch("refundType") ?? "", onValueChange: (v) => form.setValue("refundType", (v || undefined)), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: messages.common.selectPlaceholder }) }), _jsx(SelectContent, { children: refundTypeItems.map((t) => (_jsx(SelectItem, { value: t.value, children: t.label }, t.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.currency }), _jsx(CurrencyCombobox, { value: form.watch("currency") || null, onChange: (next) => form.setValue("currency", next ?? "EUR" /* i18n-literal-ok domain default currency */, {
86
94
  shouldValidate: true,
87
95
  shouldDirty: true,
88
- }) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Flat Amount (cents)" }), _jsx(Input, { ...form.register("flatAmountCents"), type: "number", placeholder: "e.g. 5000" })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: form.formState.isSubmitting, children: [form.formState.isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing ? "Save Changes" : "Create Rule"] })] })] })] }) }));
96
+ }) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyRuleDialog.fields.flatAmountCents }), _jsx(Input, { ...form.register("flatAmountCents"), type: "number", placeholder: messages.policyRuleDialog.placeholders.flatAmountCents })] })] }), _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: "mr-2 h-4 w-4 animate-spin" }), isEditing ? messages.common.saveChanges : messages.policyRuleDialog.actions.create] })] })] })] }) }));
89
97
  }
@@ -1 +1 @@
1
- {"version":3,"file":"policy-version-dialog.d.ts","sourceRoot":"","sources":["../../src/components/policy-version-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,wBAAwB,EAAiC,MAAM,uBAAuB,CAAA;AA2BpG,KAAK,wBAAwB,GAAG;IAC9B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,wBAAwB,CAAA;IAClC,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE,wBAAwB,2CAgF1B"}
1
+ {"version":3,"file":"policy-version-dialog.d.ts","sourceRoot":"","sources":["../../src/components/policy-version-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,wBAAwB,EAAiC,MAAM,uBAAuB,CAAA;AAgCpG,KAAK,wBAAwB,GAAG;IAC9B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,wBAAwB,CAAA;IAClC,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE,wBAAwB,2CA2F1B"}
@@ -6,13 +6,18 @@ import { Loader2 } from "lucide-react";
6
6
  import { useEffect } from "react";
7
7
  import { useForm } from "react-hook-form";
8
8
  import { z } from "zod/v4";
9
- const versionFormSchema = z.object({
10
- title: z.string().min(1, "Title is required"),
11
- body: z.string().optional(),
12
- });
9
+ import { useLegalUiMessagesOrDefault } from "../i18n";
10
+ function createVersionFormSchema(messages) {
11
+ return z.object({
12
+ title: z.string().min(1, messages.policyVersionDialog.validation.titleRequired),
13
+ body: z.string().optional(),
14
+ });
15
+ }
13
16
  export function PolicyVersionDialog({ open, onOpenChange, policyId, version, onSuccess, }) {
14
17
  const isEditing = !!version;
15
18
  const { create, update } = useLegalPolicyVersionMutation();
19
+ const messages = useLegalUiMessagesOrDefault();
20
+ const versionFormSchema = createVersionFormSchema(messages);
16
21
  const form = useForm({
17
22
  resolver: zodResolver(versionFormSchema),
18
23
  defaultValues: {
@@ -44,9 +49,13 @@ export function PolicyVersionDialog({ open, onOpenChange, policyId, version, onS
44
49
  }
45
50
  onSuccess();
46
51
  };
47
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Version" : "New Version" }) }), _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: "Title" }), _jsx(Input, { ...form.register("title"), placeholder: "Version title" }), form.formState.errors.title && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.title.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Body" }), _jsx(RichTextEditor, { value: form.watch("body") ?? "", onChange: (value) => form.setValue("body", value, {
52
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
53
+ ? messages.policyVersionDialog.titles.edit
54
+ : messages.policyVersionDialog.titles.create }) }), _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: messages.policyVersionDialog.fields.title }), _jsx(Input, { ...form.register("title"), placeholder: messages.policyVersionDialog.placeholders.title }), form.formState.errors.title && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.title.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.policyVersionDialog.fields.body }), _jsx(RichTextEditor, { value: form.watch("body") ?? "", onChange: (value) => form.setValue("body", value, {
48
55
  shouldDirty: true,
49
56
  shouldTouch: true,
50
57
  shouldValidate: true,
51
- }), placeholder: "Policy content..." })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: form.formState.isSubmitting, children: [form.formState.isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing ? "Save Changes" : "Create Version"] })] })] })] }) }));
58
+ }), placeholder: messages.policyVersionDialog.placeholders.body })] })] }), _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: "mr-2 h-4 w-4 animate-spin" }), isEditing
59
+ ? messages.common.saveChanges
60
+ : messages.policyVersionDialog.actions.create] })] })] })] }) }));
52
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"signature-dialog.d.ts","sourceRoot":"","sources":["../../src/components/signature-dialog.tsx"],"names":[],"mappings":"AAmCA,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AASD,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,SAAS,GACV,EAAE,oBAAoB,2CAgHtB"}
1
+ {"version":3,"file":"signature-dialog.d.ts","sourceRoot":"","sources":["../../src/components/signature-dialog.tsx"],"names":[],"mappings":"AA6CA,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAED,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,SAAS,GACV,EAAE,oBAAoB,2CAoItB"}
@@ -6,22 +6,30 @@ import { Loader2 } from "lucide-react";
6
6
  import { useEffect } from "react";
7
7
  import { useForm } from "react-hook-form";
8
8
  import { z } from "zod/v4";
9
- const signatureFormSchema = z.object({
10
- signerName: z.string().min(1, "Signer name is required"),
11
- signerEmail: z.string().email().optional().or(z.literal("")),
12
- signerRole: z.string().optional(),
13
- method: z.enum(["manual", "electronic", "docusign", "other"]),
14
- provider: z.string().optional(),
15
- externalReference: z.string().optional(),
16
- });
17
- const METHODS = [
18
- { value: "manual", label: "Manual" },
19
- { value: "electronic", label: "Electronic" },
20
- { value: "docusign", label: "DocuSign" },
21
- { value: "other", label: "Other" },
22
- ];
9
+ import { useLegalUiMessagesOrDefault } from "../i18n";
10
+ import { legalSignatureMethods } from "../i18n/messages";
11
+ function createSignatureFormSchema(messages) {
12
+ return z.object({
13
+ signerName: z.string().min(1, messages.signatureDialog.validation.signerNameRequired),
14
+ signerEmail: z
15
+ .string()
16
+ .email(messages.signatureDialog.validation.signerEmailInvalid)
17
+ .optional()
18
+ .or(z.literal("")),
19
+ signerRole: z.string().optional(),
20
+ method: z.enum(legalSignatureMethods),
21
+ provider: z.string().optional(),
22
+ externalReference: z.string().optional(),
23
+ });
24
+ }
23
25
  export function SignatureDialog({ open, onOpenChange, contractId, onSuccess, }) {
24
26
  const { create } = useLegalContractSignatureMutation();
27
+ const messages = useLegalUiMessagesOrDefault();
28
+ const signatureFormSchema = createSignatureFormSchema(messages);
29
+ const methodItems = legalSignatureMethods.map((value) => ({
30
+ value,
31
+ label: messages.signatureDialog.methodLabels[value],
32
+ }));
25
33
  const form = useForm({
26
34
  resolver: zodResolver(signatureFormSchema),
27
35
  defaultValues: {
@@ -52,5 +60,5 @@ export function SignatureDialog({ open, onOpenChange, contractId, onSuccess, })
52
60
  });
53
61
  onSuccess();
54
62
  };
55
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Record Signature" }) }), _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: "Signer Name" }), _jsx(Input, { ...form.register("signerName"), placeholder: "Full name" }), form.formState.errors.signerName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.signerName.message }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Signer Email" }), _jsx(Input, { ...form.register("signerEmail"), type: "email", placeholder: "email@example.com" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Signer Role" }), _jsx(Input, { ...form.register("signerRole"), placeholder: "e.g. CEO, Legal Rep" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Method" }), _jsxs(Select, { items: METHODS, value: form.watch("method"), onValueChange: (v) => form.setValue("method", v), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: METHODS.map((m) => (_jsx(SelectItem, { value: m.value, children: m.label }, m.value))) })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Provider" }), _jsx(Input, { ...form.register("provider"), placeholder: "Optional" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "External Reference" }), _jsx(Input, { ...form.register("externalReference"), placeholder: "Optional" })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: form.formState.isSubmitting, children: [form.formState.isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Record Signature"] })] })] })] }) }));
63
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.signatureDialog.title }) }), _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: messages.signatureDialog.fields.signerName }), _jsx(Input, { ...form.register("signerName"), placeholder: messages.signatureDialog.placeholders.signerName }), form.formState.errors.signerName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.signerName.message }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.signatureDialog.fields.signerEmail }), _jsx(Input, { ...form.register("signerEmail"), type: "email", placeholder: messages.signatureDialog.placeholders.signerEmail })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.signatureDialog.fields.signerRole }), _jsx(Input, { ...form.register("signerRole"), placeholder: messages.signatureDialog.placeholders.signerRole })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.signatureDialog.fields.method }), _jsxs(Select, { items: methodItems, value: form.watch("method"), onValueChange: (v) => form.setValue("method", v, { shouldValidate: true }), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: methodItems.map((m) => (_jsx(SelectItem, { value: m.value, children: m.label }, m.value))) })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.signatureDialog.fields.provider }), _jsx(Input, { ...form.register("provider"), placeholder: messages.signatureDialog.placeholders.provider })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.signatureDialog.fields.externalReference }), _jsx(Input, { ...form.register("externalReference"), placeholder: messages.signatureDialog.placeholders.externalReference })] })] })] }), _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: "mr-2 h-4 w-4 animate-spin" }), messages.signatureDialog.actions.submit] })] })] })] }) }));
56
64
  }
@@ -0,0 +1,156 @@
1
+ export declare const legalUiEn: {
2
+ common: {
3
+ cancel: string;
4
+ saveChanges: string;
5
+ create: string;
6
+ edit: string;
7
+ add: string;
8
+ loading: string;
9
+ none: string;
10
+ selectPlaceholder: string;
11
+ optionalPlaceholder: string;
12
+ kilobytes: string;
13
+ };
14
+ bookingContractCard: {
15
+ heading: string;
16
+ empty: string;
17
+ generate: string;
18
+ regenerate: string;
19
+ download: string;
20
+ noAttachments: string;
21
+ issuedAt: string;
22
+ contractNumber: string;
23
+ unsaved: string;
24
+ contractStatusLabels: {
25
+ draft: string;
26
+ issued: string;
27
+ sent: string;
28
+ signed: string;
29
+ executed: string;
30
+ expired: string;
31
+ void: string;
32
+ };
33
+ };
34
+ attachmentDialog: {
35
+ titles: {
36
+ create: string;
37
+ edit: string;
38
+ };
39
+ fields: {
40
+ name: string;
41
+ kind: string;
42
+ mimeType: string;
43
+ fileSize: string;
44
+ checksum: string;
45
+ storageKey: string;
46
+ };
47
+ placeholders: {
48
+ name: string;
49
+ kind: string;
50
+ mimeType: string;
51
+ fileSize: string;
52
+ checksum: string;
53
+ storageKey: string;
54
+ };
55
+ actions: {
56
+ create: string;
57
+ };
58
+ validation: {
59
+ nameRequired: string;
60
+ };
61
+ };
62
+ policyRuleDialog: {
63
+ titles: {
64
+ create: string;
65
+ edit: string;
66
+ };
67
+ fields: {
68
+ ruleType: string;
69
+ sortOrder: string;
70
+ label: string;
71
+ daysBeforeDeparture: string;
72
+ refundPercent: string;
73
+ refundType: string;
74
+ currency: string;
75
+ flatAmountCents: string;
76
+ };
77
+ placeholders: {
78
+ label: string;
79
+ daysBeforeDeparture: string;
80
+ refundPercent: string;
81
+ flatAmountCents: string;
82
+ };
83
+ actions: {
84
+ create: string;
85
+ };
86
+ ruleTypeLabels: {
87
+ window: string;
88
+ percentage: string;
89
+ flat_amount: string;
90
+ date_range: string;
91
+ custom: string;
92
+ };
93
+ refundTypeLabels: {
94
+ cash: string;
95
+ credit: string;
96
+ cash_or_credit: string;
97
+ none: string;
98
+ };
99
+ validation: {
100
+ refundPercentMin: string;
101
+ refundPercentMax: string;
102
+ };
103
+ };
104
+ signatureDialog: {
105
+ title: string;
106
+ fields: {
107
+ signerName: string;
108
+ signerEmail: string;
109
+ signerRole: string;
110
+ method: string;
111
+ provider: string;
112
+ externalReference: string;
113
+ };
114
+ placeholders: {
115
+ signerName: string;
116
+ signerEmail: string;
117
+ signerRole: string;
118
+ provider: string;
119
+ externalReference: string;
120
+ };
121
+ actions: {
122
+ submit: string;
123
+ };
124
+ methodLabels: {
125
+ manual: string;
126
+ electronic: string;
127
+ docusign: string;
128
+ other: string;
129
+ };
130
+ validation: {
131
+ signerNameRequired: string;
132
+ signerEmailInvalid: string;
133
+ };
134
+ };
135
+ policyVersionDialog: {
136
+ titles: {
137
+ create: string;
138
+ edit: string;
139
+ };
140
+ fields: {
141
+ title: string;
142
+ body: string;
143
+ };
144
+ placeholders: {
145
+ title: string;
146
+ body: string;
147
+ };
148
+ actions: {
149
+ create: string;
150
+ };
151
+ validation: {
152
+ titleRequired: string;
153
+ };
154
+ };
155
+ };
156
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0JK,CAAA"}