@voyantjs/legal-ui 0.13.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 +17 -0
- package/dist/components/attachment-dialog.d.ts +11 -0
- package/dist/components/attachment-dialog.d.ts.map +1 -0
- package/dist/components/attachment-dialog.js +64 -0
- package/dist/components/booking-contract-card.d.ts +42 -0
- package/dist/components/booking-contract-card.d.ts.map +1 -0
- package/dist/components/booking-contract-card.js +72 -0
- package/dist/components/policy-rule-dialog.d.ts +12 -0
- package/dist/components/policy-rule-dialog.d.ts.map +1 -0
- package/dist/components/policy-rule-dialog.js +89 -0
- package/dist/components/policy-version-dialog.d.ts +11 -0
- package/dist/components/policy-version-dialog.d.ts.map +1 -0
- package/dist/components/policy-version-dialog.js +52 -0
- package/dist/components/signature-dialog.d.ts +9 -0
- package/dist/components/signature-dialog.d.ts.map +1 -0
- package/dist/components/signature-dialog.js +56 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @voyantjs/legal-ui
|
|
2
|
+
|
|
3
|
+
Importable React UI components for Voyant legal. Bundler-consumed (Vite, Next.js, webpack, etc.).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @voyantjs/legal-ui @voyantjs/legal-react @voyantjs/voyant-ui @tanstack/react-query react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`@voyantjs/voyant-ui` provides the design-system primitives. `@voyantjs/legal-react` provides the data-layer hooks. Both are required peers.
|
|
12
|
+
|
|
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
|
+
|
|
15
|
+
## Not included (registry-only)
|
|
16
|
+
|
|
17
|
+
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.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type LegalContractAttachmentRecord } from "@voyantjs/legal-react";
|
|
2
|
+
type AttachmentDialogProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
contractId: string;
|
|
6
|
+
attachment?: LegalContractAttachmentRecord;
|
|
7
|
+
onSuccess: () => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function AttachmentDialog({ open, onOpenChange, contractId, attachment, onSuccess, }: AttachmentDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=attachment-dialog.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLegalContractAttachmentMutation, } from "@voyantjs/legal-react";
|
|
3
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, } from "@voyantjs/voyant-ui/components";
|
|
4
|
+
import { zodResolver } from "@voyantjs/voyant-ui/lib/zod-resolver";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
import { useForm } from "react-hook-form";
|
|
8
|
+
import { z } from "zod/v4";
|
|
9
|
+
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
|
+
});
|
|
17
|
+
export function AttachmentDialog({ open, onOpenChange, contractId, attachment, onSuccess, }) {
|
|
18
|
+
const isEditing = !!attachment;
|
|
19
|
+
const { create, update } = useLegalContractAttachmentMutation();
|
|
20
|
+
const form = useForm({
|
|
21
|
+
resolver: zodResolver(attachmentFormSchema),
|
|
22
|
+
defaultValues: {
|
|
23
|
+
name: "",
|
|
24
|
+
kind: "appendix",
|
|
25
|
+
mimeType: "",
|
|
26
|
+
fileSize: undefined,
|
|
27
|
+
storageKey: "",
|
|
28
|
+
checksum: "",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (open && attachment) {
|
|
33
|
+
form.reset({
|
|
34
|
+
name: attachment.name,
|
|
35
|
+
kind: attachment.kind,
|
|
36
|
+
mimeType: attachment.mimeType ?? "",
|
|
37
|
+
fileSize: attachment.fileSize ?? undefined,
|
|
38
|
+
storageKey: attachment.storageKey ?? "",
|
|
39
|
+
checksum: attachment.checksum ?? "",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else if (open) {
|
|
43
|
+
form.reset();
|
|
44
|
+
}
|
|
45
|
+
}, [open, attachment, form]);
|
|
46
|
+
const onSubmit = async (values) => {
|
|
47
|
+
const payload = {
|
|
48
|
+
name: values.name,
|
|
49
|
+
kind: values.kind || "appendix",
|
|
50
|
+
mimeType: values.mimeType || undefined,
|
|
51
|
+
fileSize: values.fileSize || undefined,
|
|
52
|
+
storageKey: values.storageKey || undefined,
|
|
53
|
+
checksum: values.checksum || undefined,
|
|
54
|
+
};
|
|
55
|
+
if (isEditing && attachment) {
|
|
56
|
+
await update.mutateAsync({ contractId, id: attachment.id, input: payload });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
await create.mutateAsync({ contractId, input: payload });
|
|
60
|
+
}
|
|
61
|
+
onSuccess();
|
|
62
|
+
};
|
|
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"] })] })] })] }) }));
|
|
64
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
}
|
|
14
|
+
export interface BookingContractCardProps {
|
|
15
|
+
/** Booking whose contracts we list. Required — the card filters server-side. */
|
|
16
|
+
bookingId: string;
|
|
17
|
+
/**
|
|
18
|
+
* API base for attachment download redirects (default: same origin). Use
|
|
19
|
+
* this when the operator admin app proxies through a different host than
|
|
20
|
+
* the API — the browser needs an absolute URL to open the 302 in a new
|
|
21
|
+
* tab correctly.
|
|
22
|
+
*/
|
|
23
|
+
apiBaseUrl?: string;
|
|
24
|
+
labels?: BookingContractCardLabels;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Operator booking-detail "Contract" card. Mount next to the payments / docs
|
|
28
|
+
* card on the booking detail page. Responsibilities are deliberately narrow:
|
|
29
|
+
* - List contracts linked to this booking
|
|
30
|
+
* - Show each contract's latest status + number
|
|
31
|
+
* - Let the operator download the generated PDF (opens in a new tab)
|
|
32
|
+
* - Let the operator force a regeneration when the template or booking
|
|
33
|
+
* data has changed
|
|
34
|
+
*
|
|
35
|
+
* Contract creation itself is handled by the `booking.confirmed` auto-
|
|
36
|
+
* generate subscriber (or manually from the contract-template admin page).
|
|
37
|
+
* This card is the consumer surface — if no contract exists, the empty
|
|
38
|
+
* state explains the flow rather than offering a "Create" button (which
|
|
39
|
+
* would require a template picker, out of scope here).
|
|
40
|
+
*/
|
|
41
|
+
export declare function BookingContractCard({ bookingId, apiBaseUrl, labels }: BookingContractCardProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
//# sourceMappingURL=booking-contract-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booking-contract-card.d.ts","sourceRoot":"","sources":["../../src/components/booking-contract-card.tsx"],"names":[],"mappings":"AAiCA,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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useLegalContractAttachments, useLegalContractMutation, useLegalContracts, } from "@voyantjs/legal-react";
|
|
4
|
+
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, } from "@voyantjs/voyant-ui/components";
|
|
5
|
+
import { Download, FileText, Loader2, RotateCw } from "lucide-react";
|
|
6
|
+
/**
|
|
7
|
+
* Status → badge style map. Keeps the card visually in sync with the
|
|
8
|
+
* contract detail page (same variant names, same ordering of severity).
|
|
9
|
+
*/
|
|
10
|
+
const STATUS_VARIANT = {
|
|
11
|
+
draft: "outline",
|
|
12
|
+
issued: "secondary",
|
|
13
|
+
sent: "secondary",
|
|
14
|
+
signed: "default",
|
|
15
|
+
executed: "default",
|
|
16
|
+
expired: "destructive",
|
|
17
|
+
void: "destructive",
|
|
18
|
+
};
|
|
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
|
+
/**
|
|
31
|
+
* Operator booking-detail "Contract" card. Mount next to the payments / docs
|
|
32
|
+
* card on the booking detail page. Responsibilities are deliberately narrow:
|
|
33
|
+
* - List contracts linked to this booking
|
|
34
|
+
* - Show each contract's latest status + number
|
|
35
|
+
* - Let the operator download the generated PDF (opens in a new tab)
|
|
36
|
+
* - Let the operator force a regeneration when the template or booking
|
|
37
|
+
* data has changed
|
|
38
|
+
*
|
|
39
|
+
* Contract creation itself is handled by the `booking.confirmed` auto-
|
|
40
|
+
* generate subscriber (or manually from the contract-template admin page).
|
|
41
|
+
* This card is the consumer surface — if no contract exists, the empty
|
|
42
|
+
* state explains the flow rather than offering a "Create" button (which
|
|
43
|
+
* would require a template picker, out of scope here).
|
|
44
|
+
*/
|
|
45
|
+
export function BookingContractCard({ bookingId, apiBaseUrl, labels }) {
|
|
46
|
+
const merged = { ...DEFAULT_LABELS, ...labels };
|
|
47
|
+
const contractsQuery = useLegalContracts({ bookingId, limit: 25 });
|
|
48
|
+
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)))) })] }));
|
|
50
|
+
}
|
|
51
|
+
function BookingContractRow({ contract, apiBaseUrl, labels, }) {
|
|
52
|
+
const attachmentsQuery = useLegalContractAttachments({ contractId: contract.id });
|
|
53
|
+
const attachments = attachmentsQuery.data ?? [];
|
|
54
|
+
const documentAttachments = attachments.filter((a) => a.kind === "document");
|
|
55
|
+
const { generateDocument, regenerateDocument } = useLegalContractMutation();
|
|
56
|
+
const isPending = generateDocument.isPending || regenerateDocument.isPending;
|
|
57
|
+
const hasDocument = documentAttachments.length > 0;
|
|
58
|
+
const handleGenerate = () => {
|
|
59
|
+
const mutation = hasDocument ? regenerateDocument : generateDocument;
|
|
60
|
+
mutation.mutate({ id: contract.id, input: { replaceExisting: true, kind: "document" } });
|
|
61
|
+
};
|
|
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 }))] }));
|
|
63
|
+
}
|
|
64
|
+
function AttachmentDownloadRow({ attachment, apiBaseUrl, downloadLabel, }) {
|
|
65
|
+
// The download endpoint returns a 302 to the signed URL. A plain <a> link
|
|
66
|
+
// with target="_blank" lets the browser follow it and open the file in a
|
|
67
|
+
// new tab. When apiBaseUrl is omitted we fall back to a relative URL,
|
|
68
|
+
// which is correct for same-origin admin apps.
|
|
69
|
+
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;
|
|
71
|
+
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
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type LegalPolicyRuleRecord } from "@voyantjs/legal-react";
|
|
2
|
+
export type RuleData = LegalPolicyRuleRecord;
|
|
3
|
+
type PolicyRuleDialogProps = {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onOpenChange: (open: boolean) => void;
|
|
6
|
+
versionId: string;
|
|
7
|
+
rule?: RuleData;
|
|
8
|
+
onSuccess: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function PolicyRuleDialog({ open, onOpenChange, versionId, rule, onSuccess, }: PolicyRuleDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=policy-rule-dialog.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLegalPolicyRuleMutation } from "@voyantjs/legal-react";
|
|
3
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/voyant-ui/components";
|
|
4
|
+
import { CurrencyCombobox } from "@voyantjs/voyant-ui/components/currency-combobox";
|
|
5
|
+
import { zodResolver } from "@voyantjs/voyant-ui/lib/zod-resolver";
|
|
6
|
+
import { Loader2 } from "lucide-react";
|
|
7
|
+
import { useEffect } from "react";
|
|
8
|
+
import { useForm } from "react-hook-form";
|
|
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
|
+
];
|
|
33
|
+
export function PolicyRuleDialog({ open, onOpenChange, versionId, rule, onSuccess, }) {
|
|
34
|
+
const isEditing = !!rule;
|
|
35
|
+
const { create, update } = useLegalPolicyRuleMutation();
|
|
36
|
+
const form = useForm({
|
|
37
|
+
resolver: zodResolver(ruleFormSchema),
|
|
38
|
+
defaultValues: {
|
|
39
|
+
ruleType: "window",
|
|
40
|
+
label: "",
|
|
41
|
+
daysBeforeDeparture: undefined,
|
|
42
|
+
refundPercent: undefined,
|
|
43
|
+
refundType: undefined,
|
|
44
|
+
flatAmountCents: undefined,
|
|
45
|
+
currency: "",
|
|
46
|
+
sortOrder: 0,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (open && rule) {
|
|
51
|
+
form.reset({
|
|
52
|
+
ruleType: rule.ruleType,
|
|
53
|
+
label: rule.label ?? "",
|
|
54
|
+
daysBeforeDeparture: rule.daysBeforeDeparture ?? undefined,
|
|
55
|
+
refundPercent: rule.refundPercent ?? undefined,
|
|
56
|
+
refundType: rule.refundType ?? undefined,
|
|
57
|
+
flatAmountCents: rule.flatAmountCents ?? undefined,
|
|
58
|
+
currency: rule.currency ?? "",
|
|
59
|
+
sortOrder: rule.sortOrder,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (open) {
|
|
63
|
+
form.reset();
|
|
64
|
+
}
|
|
65
|
+
}, [open, rule, form]);
|
|
66
|
+
const onSubmit = async (values) => {
|
|
67
|
+
const payload = {
|
|
68
|
+
ruleType: values.ruleType,
|
|
69
|
+
label: values.label || undefined,
|
|
70
|
+
daysBeforeDeparture: values.daysBeforeDeparture,
|
|
71
|
+
refundPercent: values.refundPercent,
|
|
72
|
+
refundType: values.refundType || undefined,
|
|
73
|
+
flatAmountCents: values.flatAmountCents,
|
|
74
|
+
currency: values.currency || undefined,
|
|
75
|
+
sortOrder: values.sortOrder ?? 0,
|
|
76
|
+
};
|
|
77
|
+
if (isEditing && rule) {
|
|
78
|
+
await update.mutateAsync({ id: rule.id, input: payload });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
await create.mutateAsync({ versionId, input: payload });
|
|
82
|
+
}
|
|
83
|
+
onSuccess();
|
|
84
|
+
};
|
|
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", {
|
|
86
|
+
shouldValidate: true,
|
|
87
|
+
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"] })] })] })] }) }));
|
|
89
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type LegalPolicyVersionRecord } from "@voyantjs/legal-react";
|
|
2
|
+
type PolicyVersionDialogProps = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
policyId: string;
|
|
6
|
+
version?: LegalPolicyVersionRecord;
|
|
7
|
+
onSuccess: () => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function PolicyVersionDialog({ open, onOpenChange, policyId, version, onSuccess, }: PolicyVersionDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=policy-version-dialog.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLegalPolicyVersionMutation } from "@voyantjs/legal-react";
|
|
3
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, RichTextEditor, } from "@voyantjs/voyant-ui/components";
|
|
4
|
+
import { zodResolver } from "@voyantjs/voyant-ui/lib/zod-resolver";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
import { useForm } from "react-hook-form";
|
|
8
|
+
import { z } from "zod/v4";
|
|
9
|
+
const versionFormSchema = z.object({
|
|
10
|
+
title: z.string().min(1, "Title is required"),
|
|
11
|
+
body: z.string().optional(),
|
|
12
|
+
});
|
|
13
|
+
export function PolicyVersionDialog({ open, onOpenChange, policyId, version, onSuccess, }) {
|
|
14
|
+
const isEditing = !!version;
|
|
15
|
+
const { create, update } = useLegalPolicyVersionMutation();
|
|
16
|
+
const form = useForm({
|
|
17
|
+
resolver: zodResolver(versionFormSchema),
|
|
18
|
+
defaultValues: {
|
|
19
|
+
title: "",
|
|
20
|
+
body: "",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (open && version) {
|
|
25
|
+
form.reset({
|
|
26
|
+
title: version.title,
|
|
27
|
+
body: version.body ?? "",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
else if (open) {
|
|
31
|
+
form.reset();
|
|
32
|
+
}
|
|
33
|
+
}, [open, version, form]);
|
|
34
|
+
const onSubmit = async (values) => {
|
|
35
|
+
const payload = {
|
|
36
|
+
title: values.title,
|
|
37
|
+
body: values.body || undefined,
|
|
38
|
+
};
|
|
39
|
+
if (isEditing && version) {
|
|
40
|
+
await update.mutateAsync({ id: version.id, input: payload });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
await create.mutateAsync({ policyId, input: payload });
|
|
44
|
+
}
|
|
45
|
+
onSuccess();
|
|
46
|
+
};
|
|
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, {
|
|
48
|
+
shouldDirty: true,
|
|
49
|
+
shouldTouch: true,
|
|
50
|
+
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"] })] })] })] }) }));
|
|
52
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type SignatureDialogProps = {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onOpenChange: (open: boolean) => void;
|
|
4
|
+
contractId: string;
|
|
5
|
+
onSuccess: () => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function SignatureDialog({ open, onOpenChange, contractId, onSuccess, }: SignatureDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=signature-dialog.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLegalContractSignatureMutation } from "@voyantjs/legal-react";
|
|
3
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/voyant-ui/components";
|
|
4
|
+
import { zodResolver } from "@voyantjs/voyant-ui/lib/zod-resolver";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
import { useForm } from "react-hook-form";
|
|
8
|
+
import { z } from "zod/v4";
|
|
9
|
+
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
|
+
];
|
|
23
|
+
export function SignatureDialog({ open, onOpenChange, contractId, onSuccess, }) {
|
|
24
|
+
const { create } = useLegalContractSignatureMutation();
|
|
25
|
+
const form = useForm({
|
|
26
|
+
resolver: zodResolver(signatureFormSchema),
|
|
27
|
+
defaultValues: {
|
|
28
|
+
signerName: "",
|
|
29
|
+
signerEmail: "",
|
|
30
|
+
signerRole: "",
|
|
31
|
+
method: "manual",
|
|
32
|
+
provider: "",
|
|
33
|
+
externalReference: "",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (open) {
|
|
38
|
+
form.reset();
|
|
39
|
+
}
|
|
40
|
+
}, [open, form]);
|
|
41
|
+
const onSubmit = async (values) => {
|
|
42
|
+
await create.mutateAsync({
|
|
43
|
+
contractId,
|
|
44
|
+
input: {
|
|
45
|
+
signerName: values.signerName,
|
|
46
|
+
signerEmail: values.signerEmail || undefined,
|
|
47
|
+
signerRole: values.signerRole || undefined,
|
|
48
|
+
method: values.method,
|
|
49
|
+
provider: values.provider || undefined,
|
|
50
|
+
externalReference: values.externalReference || undefined,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
onSuccess();
|
|
54
|
+
};
|
|
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"] })] })] })] }) }));
|
|
56
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AttachmentDialog } from "./components/attachment-dialog";
|
|
2
|
+
export { BookingContractCard, type BookingContractCardLabels, type BookingContractCardProps, } from "./components/booking-contract-card";
|
|
3
|
+
export { PolicyRuleDialog, type RuleData } from "./components/policy-rule-dialog";
|
|
4
|
+
export { PolicyVersionDialog } from "./components/policy-version-dialog";
|
|
5
|
+
export { SignatureDialog } from "./components/signature-dialog";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EACL,mBAAmB,EACnB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,GAC9B,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AttachmentDialog } from "./components/attachment-dialog";
|
|
2
|
+
export { BookingContractCard, } from "./components/booking-contract-card";
|
|
3
|
+
export { PolicyRuleDialog } from "./components/policy-rule-dialog";
|
|
4
|
+
export { PolicyVersionDialog } from "./components/policy-version-dialog";
|
|
5
|
+
export { SignatureDialog } from "./components/signature-dialog";
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voyantjs/legal-ui",
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/voyantjs/voyant.git",
|
|
8
|
+
"directory": "packages/legal-ui"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./src/index.ts",
|
|
14
|
+
"./components/*": "./src/components/*.tsx"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.build.json",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"prepack": "pnpm run build",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"lint": "biome check src/",
|
|
22
|
+
"test": "vitest run --passWithNoTests"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@tanstack/react-query": "^5.0.0",
|
|
26
|
+
"@voyantjs/legal-react": "workspace:*",
|
|
27
|
+
"@voyantjs/voyant-ui": "workspace:*",
|
|
28
|
+
"react": "^19.0.0",
|
|
29
|
+
"react-dom": "^19.0.0",
|
|
30
|
+
"react-hook-form": "^7.60.0",
|
|
31
|
+
"zod": "^3.25.76"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@tanstack/react-query": "^5.96.2",
|
|
35
|
+
"@types/react": "^19.2.14",
|
|
36
|
+
"@types/react-dom": "^19.2.3",
|
|
37
|
+
"@voyantjs/legal-react": "workspace:*",
|
|
38
|
+
"@voyantjs/voyant-typescript-config": "workspace:*",
|
|
39
|
+
"@voyantjs/voyant-ui": "workspace:*",
|
|
40
|
+
"lucide-react": "^0.475.0",
|
|
41
|
+
"react": "^19.2.4",
|
|
42
|
+
"react-dom": "^19.2.4",
|
|
43
|
+
"react-hook-form": "^7.60.0",
|
|
44
|
+
"typescript": "^6.0.2",
|
|
45
|
+
"vitest": "^4.1.2",
|
|
46
|
+
"zod": "^3.25.76"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist"
|
|
50
|
+
],
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"exports": {
|
|
54
|
+
".": {
|
|
55
|
+
"types": "./dist/index.d.ts",
|
|
56
|
+
"import": "./dist/index.js"
|
|
57
|
+
},
|
|
58
|
+
"./components/*": {
|
|
59
|
+
"types": "./dist/components/*.d.ts",
|
|
60
|
+
"import": "./dist/components/*.js"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"main": "./dist/index.js",
|
|
64
|
+
"types": "./dist/index.d.ts"
|
|
65
|
+
}
|
|
66
|
+
}
|