Add export document pdf options
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
164
apps/console/src/components/documents/BulkExportDialog.tsx
Normal file
164
apps/console/src/components/documents/BulkExportDialog.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Field,
|
||||
useDialogRef,
|
||||
Spinner,
|
||||
Checkbox,
|
||||
} from "@probo/ui";
|
||||
import { type ReactNode, useImperativeHandle, forwardRef } from "react";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
|
||||
const bulkExportSchema = z.object({
|
||||
withWatermark: z.boolean(),
|
||||
watermarkEmail: z.string().optional().or(z.literal("")),
|
||||
withSignatures: z.boolean(),
|
||||
}).refine((data) => {
|
||||
if (data.withWatermark && (!data.watermarkEmail || data.watermarkEmail === "")) {
|
||||
return false;
|
||||
}
|
||||
if (data.withWatermark && data.watermarkEmail && !z.string().email().safeParse(data.watermarkEmail).success) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, {
|
||||
message: "Please enter a valid email address",
|
||||
path: ["watermarkEmail"],
|
||||
});
|
||||
|
||||
type BulkExportFormData = z.infer<typeof bulkExportSchema>;
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
onExport: (options: BulkExportFormData) => void;
|
||||
isLoading?: boolean;
|
||||
defaultEmail?: string;
|
||||
selectedCount: number;
|
||||
};
|
||||
|
||||
export type BulkExportDialogRef = {
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
export const BulkExportDialog = forwardRef<BulkExportDialogRef, Props>(
|
||||
({ children, onExport, isLoading = false, defaultEmail = "", selectedCount }, ref) => {
|
||||
const { __ } = useTranslate();
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
const { register, handleSubmit, formState, watch, setValue } = useFormWithSchema(
|
||||
bulkExportSchema,
|
||||
{
|
||||
defaultValues: {
|
||||
withWatermark: false,
|
||||
watermarkEmail: defaultEmail,
|
||||
withSignatures: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const watchWatermark = watch("withWatermark");
|
||||
const watchSignatures = watch("withSignatures");
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
open: () => dialogRef.current?.open(),
|
||||
close: () => dialogRef.current?.close(),
|
||||
}));
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
const options = {
|
||||
...data,
|
||||
watermarkEmail: data.withWatermark ? data.watermarkEmail : undefined,
|
||||
};
|
||||
onExport(options);
|
||||
dialogRef.current?.close();
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div onClick={() => dialogRef.current?.open()}>{children}</div>
|
||||
<Dialog
|
||||
className="max-w-md"
|
||||
ref={dialogRef}
|
||||
title={sprintf(__("Export %s Documents"), selectedCount)}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent className="space-y-4" padded>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox
|
||||
checked={watchSignatures}
|
||||
onChange={(checked) => setValue("withSignatures", checked)}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<label className="text-sm font-medium text-txt-primary cursor-pointer">
|
||||
{__("Include signatures")}
|
||||
</label>
|
||||
<p className="text-xs text-txt-secondary mt-1">
|
||||
{__("Show signature information and approval details in the PDFs")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox
|
||||
checked={watchWatermark}
|
||||
onChange={(checked) => setValue("withWatermark", checked)}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<label className="text-sm font-medium text-txt-primary cursor-pointer">
|
||||
{__("Add watermark")}
|
||||
</label>
|
||||
<p className="text-xs text-txt-secondary mt-1">
|
||||
{__("Add confidential watermark with email and timestamp to all PDFs")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{watchWatermark && (
|
||||
<div className="ml-6">
|
||||
<Field
|
||||
label={__("Watermark email")}
|
||||
{...register("watermarkEmail")}
|
||||
type="email"
|
||||
placeholder={__("Enter email address")}
|
||||
error={formState.errors.watermarkEmail?.message}
|
||||
autoComplete="off"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-level-1 p-3 rounded-lg border border-border-subtle">
|
||||
<p className="text-sm text-txt-secondary">
|
||||
{__("The documents will be exported as individual PDFs in a ZIP file. You will receive an email when the export is ready for download.")}
|
||||
</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Spinner size={16} />
|
||||
{__("Exporting...")}
|
||||
</>
|
||||
) : (
|
||||
__("Export Documents")
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
141
apps/console/src/components/documents/PdfDownloadDialog.tsx
Normal file
141
apps/console/src/components/documents/PdfDownloadDialog.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Field,
|
||||
useDialogRef,
|
||||
Spinner,
|
||||
Checkbox,
|
||||
} from "@probo/ui";
|
||||
import { type ReactNode, useImperativeHandle, forwardRef } from "react";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
|
||||
const pdfDownloadSchema = z.object({
|
||||
withWatermark: z.boolean(),
|
||||
watermarkEmail: z.string().email("Please enter a valid email address").optional().or(z.literal("")),
|
||||
withSignatures: z.boolean(),
|
||||
});
|
||||
|
||||
type PdfDownloadFormData = z.infer<typeof pdfDownloadSchema>;
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
onDownload: (options: PdfDownloadFormData) => void;
|
||||
isLoading?: boolean;
|
||||
defaultEmail?: string;
|
||||
};
|
||||
|
||||
export type PdfDownloadDialogRef = {
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
export const PdfDownloadDialog = forwardRef<PdfDownloadDialogRef, Props>(
|
||||
({ children, onDownload, isLoading = false, defaultEmail = "" }, ref) => {
|
||||
const { __ } = useTranslate();
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
const { register, handleSubmit, formState, watch, setValue } = useFormWithSchema(
|
||||
pdfDownloadSchema,
|
||||
{
|
||||
defaultValues: {
|
||||
withWatermark: false,
|
||||
watermarkEmail: defaultEmail,
|
||||
withSignatures: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const watchWatermark = watch("withWatermark");
|
||||
const watchSignatures = watch("withSignatures");
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
open: () => dialogRef.current?.open(),
|
||||
close: () => dialogRef.current?.close(),
|
||||
}));
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
const options = {
|
||||
...data,
|
||||
watermarkEmail: data.withWatermark ? data.watermarkEmail : undefined,
|
||||
};
|
||||
onDownload(options);
|
||||
dialogRef.current?.close();
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div onClick={() => dialogRef.current?.open()}>{children}</div>
|
||||
<Dialog className="max-w-md" ref={dialogRef} title={__("Download PDF Options")}>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent className="space-y-4" padded>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox
|
||||
checked={watchSignatures}
|
||||
onChange={(checked) => setValue("withSignatures", checked)}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<label className="text-sm font-medium text-txt-primary cursor-pointer">
|
||||
{__("Include signatures")}
|
||||
</label>
|
||||
<p className="text-xs text-txt-secondary mt-1">
|
||||
{__("Show signature information and approval details in the PDF")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox
|
||||
checked={watchWatermark}
|
||||
onChange={(checked) => setValue("withWatermark", checked)}
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<label className="text-sm font-medium text-txt-primary cursor-pointer">
|
||||
{__("Add watermark")}
|
||||
</label>
|
||||
<p className="text-xs text-txt-secondary mt-1">
|
||||
{__("Add confidential watermark with email and timestamp")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{watchWatermark && (
|
||||
<div className="ml-6">
|
||||
<Field
|
||||
label={__("Watermark email")}
|
||||
{...register("watermarkEmail")}
|
||||
type="email"
|
||||
placeholder={__("Enter email address")}
|
||||
error={formState.errors.watermarkEmail?.message}
|
||||
autoComplete="off"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Spinner size={16} />
|
||||
{__("Downloading...")}
|
||||
</>
|
||||
) : (
|
||||
__("Download PDF")
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user