mirror of
https://github.com/documenso/documenso.git
synced 2025-11-17 10:11:35 +10:00
fix: various envelope updates
This commit is contained in:
@ -68,7 +68,7 @@ test.describe('API V2 Envelopes', () => {
|
||||
const formData = new FormData();
|
||||
formData.append('payload', JSON.stringify(payload));
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/create`, {
|
||||
const res = await request.post(`${baseUrl}/envelope/create`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
multipart: formData,
|
||||
});
|
||||
@ -100,7 +100,7 @@ test.describe('API V2 Envelopes', () => {
|
||||
formData.append('files', new File([file.data], file.name, { type: 'application/pdf' }));
|
||||
}
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/create`, {
|
||||
const res = await request.post(`${baseUrl}/envelope/create`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
multipart: formData,
|
||||
});
|
||||
@ -232,14 +232,14 @@ test.describe('API V2 Envelopes', () => {
|
||||
}
|
||||
|
||||
// Should error since folder is not owned by the user.
|
||||
const invalidRes = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/create`, {
|
||||
const invalidRes = await request.post(`${baseUrl}/envelope/create`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
multipart: formData,
|
||||
});
|
||||
|
||||
expect(invalidRes.ok()).toBeFalsy();
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/create`, {
|
||||
const res = await request.post(`${baseUrl}/envelope/create`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
multipart: formData,
|
||||
});
|
||||
@ -378,21 +378,24 @@ test.describe('API V2 Envelopes', () => {
|
||||
new File([alignmentPdf], 'field-font-alignment.pdf', { type: 'application/pdf' }),
|
||||
);
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/create`, {
|
||||
const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
multipart: formData,
|
||||
});
|
||||
|
||||
const response: TCreateEnvelopeResponse = await res.json();
|
||||
expect(createEnvelopeRequest.ok()).toBeTruthy();
|
||||
expect(createEnvelopeRequest.status()).toBe(200);
|
||||
|
||||
const createdEnvelope: TGetEnvelopeResponse = await request
|
||||
.get(`${baseUrl}/envelope/${response.id}`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
})
|
||||
.then(async (res) => await res.json());
|
||||
const { id: createdEnvelopeId }: TCreateEnvelopeResponse = await createEnvelopeRequest.json();
|
||||
|
||||
const getEnvelopeRequest = await request.get(`${baseUrl}/envelope/${createdEnvelopeId}`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
});
|
||||
|
||||
const createdEnvelope: TGetEnvelopeResponse = await getEnvelopeRequest.json();
|
||||
|
||||
// Might as well testing access control here as well.
|
||||
const unauthRequest = await request.get(`${baseUrl}/envelope/${response.id}`, {
|
||||
const unauthRequest = await request.get(`${baseUrl}/envelope/${createdEnvelopeId}`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
|
||||
@ -78,7 +78,6 @@ test.describe('Signing Certificate Tests', () => {
|
||||
},
|
||||
});
|
||||
|
||||
// Todo: Envelopes
|
||||
const firstDocumentData = completedDocument.envelopeItems[0].documentData;
|
||||
|
||||
const completedDocumentData = await getFile(firstDocumentData);
|
||||
@ -169,7 +168,6 @@ test.describe('Signing Certificate Tests', () => {
|
||||
},
|
||||
});
|
||||
|
||||
// Todo: Envelopes
|
||||
const firstDocumentData = completedDocument.envelopeItems[0].documentData;
|
||||
|
||||
const completedDocumentData = await getFile(firstDocumentData);
|
||||
|
||||
@ -165,10 +165,7 @@ export const useEditorFields = ({
|
||||
const index = localFields.findIndex((field) => field.formId === formId);
|
||||
|
||||
if (index !== -1) {
|
||||
update(index, {
|
||||
...localFields[index],
|
||||
id,
|
||||
});
|
||||
form.setValue(`fields.${index}.id`, id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import Konva from 'konva';
|
||||
import type { RenderParameters } from 'pdfjs-dist/types/src/display/api';
|
||||
@ -25,6 +25,8 @@ export function usePageRenderer(renderFunction: RenderFunction) {
|
||||
const stage = useRef<Konva.Stage | null>(null);
|
||||
const pageLayer = useRef<Konva.Layer | null>(null);
|
||||
|
||||
const [renderError, setRenderError] = useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* The raw viewport with no scaling. Basically the actual PDF size.
|
||||
*/
|
||||
@ -122,5 +124,7 @@ export function usePageRenderer(renderFunction: RenderFunction) {
|
||||
unscaledViewport,
|
||||
scaledViewport,
|
||||
pageContext,
|
||||
renderError,
|
||||
setRenderError,
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,6 +27,9 @@ type EnvelopeRenderProviderValue = {
|
||||
setCurrentEnvelopeItem: (envelopeItemId: string) => void;
|
||||
fields: TEnvelope['fields'];
|
||||
getRecipientColorKey: (recipientId: number) => TRecipientColor;
|
||||
|
||||
renderError: boolean;
|
||||
setRenderError: (renderError: boolean) => void;
|
||||
};
|
||||
|
||||
interface EnvelopeRenderProviderProps {
|
||||
@ -74,6 +77,8 @@ export const EnvelopeRenderProvider = ({
|
||||
|
||||
const [currentItem, setItem] = useState<EnvelopeRenderItem | null>(null);
|
||||
|
||||
const [renderError, setRenderError] = useState<boolean>(false);
|
||||
|
||||
const envelopeItems = useMemo(
|
||||
() => envelope.envelopeItems.sort((a, b) => a.order - b.order),
|
||||
[envelope.envelopeItems],
|
||||
@ -164,6 +169,8 @@ export const EnvelopeRenderProvider = ({
|
||||
setCurrentEnvelopeItem,
|
||||
fields: fields ?? [],
|
||||
getRecipientColorKey,
|
||||
renderError,
|
||||
setRenderError,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -250,7 +250,7 @@ export const sendDocument = async ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
if (isValid && checkedIndices.length > 0) {
|
||||
fieldsToAutoInsert.push({
|
||||
fieldId,
|
||||
customText: toCheckboxCustomText(checkedIndices),
|
||||
|
||||
@ -153,6 +153,11 @@ export const createFieldHoverInteraction = ({
|
||||
const hoverColor = RECIPIENT_COLOR_STYLES[options.color].baseRingHover;
|
||||
|
||||
fieldGroup.on('mouseover', () => {
|
||||
const layer = fieldRect.getLayer();
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Konva.Tween({
|
||||
node: fieldRect,
|
||||
duration: 0.3,
|
||||
@ -161,6 +166,11 @@ export const createFieldHoverInteraction = ({
|
||||
});
|
||||
|
||||
fieldGroup.on('mouseout', () => {
|
||||
const layer = fieldRect.getLayer();
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Konva.Tween({
|
||||
node: fieldRect,
|
||||
duration: 0.3,
|
||||
@ -169,6 +179,11 @@ export const createFieldHoverInteraction = ({
|
||||
});
|
||||
|
||||
fieldGroup.on('transformstart', () => {
|
||||
const layer = fieldRect.getLayer();
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Konva.Tween({
|
||||
node: fieldRect,
|
||||
duration: 0.3,
|
||||
@ -177,6 +192,11 @@ export const createFieldHoverInteraction = ({
|
||||
});
|
||||
|
||||
fieldGroup.on('transformend', () => {
|
||||
const layer = fieldRect.getLayer();
|
||||
if (!layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Konva.Tween({
|
||||
node: fieldRect,
|
||||
duration: 0.3,
|
||||
|
||||
@ -63,16 +63,15 @@ export const renderCheckboxFieldElement = (
|
||||
const rectWidth = fieldRect.width() * groupScaleX;
|
||||
const rectHeight = fieldRect.height() * groupScaleY;
|
||||
|
||||
// Todo: Envelopes - check sorting more than 10
|
||||
// arr.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
||||
|
||||
const squares = fieldGroup
|
||||
.find('.checkbox-square')
|
||||
.sort((a, b) => a.id().localeCompare(b.id()));
|
||||
.sort((a, b) => a.id().localeCompare(b.id(), undefined, { numeric: true }));
|
||||
const checkmarks = fieldGroup
|
||||
.find('.checkbox-checkmark')
|
||||
.sort((a, b) => a.id().localeCompare(b.id()));
|
||||
const text = fieldGroup.find('.checkbox-text').sort((a, b) => a.id().localeCompare(b.id()));
|
||||
.sort((a, b) => a.id().localeCompare(b.id(), undefined, { numeric: true }));
|
||||
const text = fieldGroup
|
||||
.find('.checkbox-text')
|
||||
.sort((a, b) => a.id().localeCompare(b.id(), undefined, { numeric: true }));
|
||||
|
||||
const groupedItems = squares.map((square, i) => ({
|
||||
squareElement: square,
|
||||
|
||||
@ -8,9 +8,9 @@ import type { TRecipientColor } from '@documenso/ui/lib/recipient-colors';
|
||||
import type { TFieldMetaSchema } from '../../types/field-meta';
|
||||
import { renderCheckboxFieldElement } from './render-checkbox-field';
|
||||
import { renderDropdownFieldElement } from './render-dropdown-field';
|
||||
import { renderGenericTextFieldElement } from './render-generic-text-field';
|
||||
import { renderRadioFieldElement } from './render-radio-field';
|
||||
import { renderSignatureFieldElement } from './render-signature-field';
|
||||
import { renderTextFieldElement } from './render-text-field';
|
||||
|
||||
export const MIN_FIELD_HEIGHT_PX = 12;
|
||||
export const MIN_FIELD_WIDTH_PX = 36;
|
||||
@ -43,9 +43,9 @@ type RenderFieldOptions = {
|
||||
*
|
||||
* @default 'edit'
|
||||
*
|
||||
* - `edit` - The field is rendered in edit mode.
|
||||
* - `sign` - The field is rendered in sign mode. No interactive elements.
|
||||
* - `export` - The field is rendered in export mode. No backgrounds, interactive elements, etc.
|
||||
* - `edit` - The field is rendered in editor page.
|
||||
* - `sign` - The field is rendered for the signing page.
|
||||
* - `export` - The field is rendered for exporting and sealing into the PDF. No backgrounds, interactive elements, etc.
|
||||
*/
|
||||
mode: 'edit' | 'sign' | 'export';
|
||||
|
||||
@ -76,10 +76,21 @@ export const renderField = ({
|
||||
};
|
||||
|
||||
return match(field.type)
|
||||
.with(FieldType.TEXT, () => renderTextFieldElement(field, options))
|
||||
.with(
|
||||
FieldType.INITIALS,
|
||||
FieldType.NAME,
|
||||
FieldType.EMAIL,
|
||||
FieldType.DATE,
|
||||
FieldType.TEXT,
|
||||
FieldType.NUMBER,
|
||||
() => renderGenericTextFieldElement(field, options),
|
||||
)
|
||||
.with(FieldType.CHECKBOX, () => renderCheckboxFieldElement(field, options))
|
||||
.with(FieldType.RADIO, () => renderRadioFieldElement(field, options))
|
||||
.with(FieldType.DROPDOWN, () => renderDropdownFieldElement(field, options))
|
||||
.with(FieldType.SIGNATURE, () => renderSignatureFieldElement(field, options))
|
||||
.otherwise(() => renderTextFieldElement(field, options)); // Todo: Envelopes
|
||||
.with(FieldType.FREE_SIGNATURE, () => {
|
||||
throw new Error('Free signature fields are not supported');
|
||||
})
|
||||
.exhaustive();
|
||||
};
|
||||
|
||||
@ -12,6 +12,8 @@ import {
|
||||
import type { FieldToRender, RenderFieldElementOptions } from './field-renderer';
|
||||
import { calculateFieldPosition } from './field-renderer';
|
||||
|
||||
const DEFAULT_TEXT_ALIGN = 'left';
|
||||
|
||||
const upsertFieldText = (field: FieldToRender, options: RenderFieldElementOptions): Konva.Text => {
|
||||
const { pageWidth, pageHeight, mode = 'edit', pageLayer, translations } = options;
|
||||
|
||||
@ -31,8 +33,8 @@ const upsertFieldText = (field: FieldToRender, options: RenderFieldElementOption
|
||||
// Calculate text positioning based on alignment
|
||||
const textX = 0;
|
||||
const textY = 0;
|
||||
let textAlign: 'left' | 'center' | 'right' = textMeta?.textAlign || 'left';
|
||||
let textVerticalAlign: 'top' | 'middle' | 'bottom' = 'top';
|
||||
let textAlign: 'left' | 'center' | 'right' = textMeta?.textAlign || DEFAULT_TEXT_ALIGN;
|
||||
const textVerticalAlign: 'top' | 'middle' | 'bottom' = 'middle';
|
||||
const textFontSize = textMeta?.fontSize || DEFAULT_STANDARD_FONT_SIZE;
|
||||
const textPadding = 10;
|
||||
|
||||
@ -40,51 +42,33 @@ const upsertFieldText = (field: FieldToRender, options: RenderFieldElementOption
|
||||
|
||||
// Handle edit mode.
|
||||
if (mode === 'edit') {
|
||||
textToRender = fieldTypeName;
|
||||
textAlign = 'center';
|
||||
textVerticalAlign = 'middle';
|
||||
|
||||
if (textMeta?.label) {
|
||||
textToRender = textMeta.label;
|
||||
} else if (textMeta?.text) {
|
||||
if (textMeta?.text) {
|
||||
textToRender = textMeta.text;
|
||||
textAlign = textMeta.textAlign || 'center'; // Todo: Envelopes - What is the default
|
||||
|
||||
// Todo: Envelopes - Handle this on signatures
|
||||
if (textMeta.characterLimit) {
|
||||
textToRender = textToRender.slice(0, textMeta.characterLimit);
|
||||
}
|
||||
} else if (textMeta?.label) {
|
||||
textToRender = textMeta.label;
|
||||
} else {
|
||||
// Show field name which is centered for the edit mode if no label/text is avaliable.
|
||||
textToRender = fieldTypeName;
|
||||
textAlign = 'center';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle sign mode.
|
||||
if (mode === 'sign' || mode === 'export') {
|
||||
textToRender = fieldTypeName;
|
||||
textAlign = 'center';
|
||||
textVerticalAlign = 'middle';
|
||||
|
||||
if (textMeta?.label) {
|
||||
textToRender = textMeta.label;
|
||||
}
|
||||
|
||||
if (textMeta?.text) {
|
||||
textToRender = textMeta.text;
|
||||
textAlign = textMeta.textAlign || 'center'; // Todo: Envelopes - What is the default
|
||||
|
||||
// Todo: Envelopes - Handle this on signatures
|
||||
if (textMeta.characterLimit) {
|
||||
textToRender = textToRender.slice(0, textMeta.characterLimit);
|
||||
if (!field.inserted) {
|
||||
if (textMeta?.text) {
|
||||
textToRender = textMeta.text;
|
||||
} else if (textMeta?.label) {
|
||||
textToRender = textMeta.label;
|
||||
} else if (mode === 'sign') {
|
||||
// Only show the field name in sign mode if no text/label is avaliable.
|
||||
textToRender = fieldTypeName;
|
||||
textAlign = 'center';
|
||||
}
|
||||
}
|
||||
|
||||
if (field.inserted) {
|
||||
textToRender = field.customText;
|
||||
textAlign = textMeta?.textAlign || 'center'; // Todo: Envelopes - What is the default
|
||||
|
||||
// Todo: Envelopes - Handle this on signatures
|
||||
if (textMeta?.characterLimit) {
|
||||
textToRender = textToRender.slice(0, textMeta.characterLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +90,7 @@ const upsertFieldText = (field: FieldToRender, options: RenderFieldElementOption
|
||||
return fieldText;
|
||||
};
|
||||
|
||||
export const renderTextFieldElement = (
|
||||
export const renderGenericTextFieldElement = (
|
||||
field: FieldToRender,
|
||||
options: RenderFieldElementOptions,
|
||||
) => {
|
||||
@ -104,7 +104,6 @@ export const extractFieldInsertionValues = ({
|
||||
const numberFieldParsedMeta = ZNumberFieldMeta.parse(field.fieldMeta);
|
||||
const errors = validateNumberField(fieldValue.value.toString(), numberFieldParsedMeta, true);
|
||||
|
||||
// Todo
|
||||
if (errors.length > 0) {
|
||||
throw new AppError(AppErrorCode.INVALID_BODY, {
|
||||
message: 'Invalid number',
|
||||
@ -127,7 +126,6 @@ export const extractFieldInsertionValues = ({
|
||||
const parsedTextFieldMeta = ZTextFieldMeta.parse(field.fieldMeta);
|
||||
const errors = validateTextField(fieldValue.value, parsedTextFieldMeta, true);
|
||||
|
||||
// Todo
|
||||
if (errors.length > 0) {
|
||||
throw new AppError(AppErrorCode.INVALID_BODY, {
|
||||
message: 'Invalid email',
|
||||
@ -189,7 +187,6 @@ export const extractFieldInsertionValues = ({
|
||||
(sign) => sign.label === validationRule,
|
||||
);
|
||||
|
||||
// Todo: Envelopes - Test this.
|
||||
if (checkboxValidationRule) {
|
||||
const isValid = validateCheckboxLength(
|
||||
selectedValues.length,
|
||||
@ -224,7 +221,6 @@ export const extractFieldInsertionValues = ({
|
||||
const parsedDropdownFieldMeta = ZDropdownFieldMeta.parse(field.fieldMeta);
|
||||
const errors = validateDropdownField(fieldValue.value, parsedDropdownFieldMeta, true);
|
||||
|
||||
// Todo: Envelopes
|
||||
if (errors.length > 0) {
|
||||
throw new AppError(AppErrorCode.INVALID_BODY, {
|
||||
message: 'Invalid dropdown value',
|
||||
|
||||
@ -13,6 +13,7 @@ export const ZSetEnvelopeFieldsRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
envelopeType: z.nativeEnum(EnvelopeType),
|
||||
fields: z.array(
|
||||
// Todo: Envelopes - Use strict schema for types + field meta.
|
||||
z.object({
|
||||
id: z
|
||||
.number()
|
||||
|
||||
@ -133,6 +133,49 @@ export const signEnvelopeFieldRoute = procedure
|
||||
|
||||
const insertionValues = extractFieldInsertionValues({ fieldValue, field, documentMeta });
|
||||
|
||||
// Early return for uninserting fields.
|
||||
if (!insertionValues.inserted) {
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const updatedField = await tx.field.update({
|
||||
where: {
|
||||
id: field.id,
|
||||
},
|
||||
data: {
|
||||
customText: '',
|
||||
inserted: false,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.signature.deleteMany({
|
||||
where: {
|
||||
fieldId: field.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (recipient.role !== RecipientRole.ASSISTANT) {
|
||||
await tx.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED,
|
||||
envelopeId: envelope.id,
|
||||
user: {
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
},
|
||||
requestMetadata: metadata.requestMetadata,
|
||||
data: {
|
||||
field: field.type,
|
||||
fieldId: field.secondaryId,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
signedField: updatedField,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const derivedRecipientActionAuth = await validateFieldAuth({
|
||||
documentAuthOptions: envelope.authOptions,
|
||||
recipient,
|
||||
|
||||
@ -2,11 +2,14 @@ import React, { Suspense, lazy } from 'react';
|
||||
|
||||
import { type PDFDocumentProxy } from 'pdfjs-dist';
|
||||
|
||||
import type { PdfViewerRendererMode } from './pdf-viewer-konva';
|
||||
|
||||
export type LoadedPDFDocument = PDFDocumentProxy;
|
||||
|
||||
export type PDFViewerProps = {
|
||||
className?: string;
|
||||
onDocumentLoad?: () => void;
|
||||
renderer: PdfViewerRendererMode;
|
||||
[key: string]: unknown;
|
||||
} & Omit<React.HTMLAttributes<HTMLDivElement>, 'onPageClick'>;
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { MessageDescriptor } from '@lingui/core';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import Konva from 'konva';
|
||||
import { Loader } from 'lucide-react';
|
||||
import { type PDFDocumentProxy } from 'pdfjs-dist';
|
||||
@ -8,6 +10,7 @@ import { Document as PDFDocument, Page as PDFPage, pdfjs } from 'react-pdf';
|
||||
|
||||
import { useCurrentEnvelopeRender } from '@documenso/lib/client-only/providers/envelope-render-provider';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||
|
||||
export type LoadedPDFDocument = PDFDocumentProxy;
|
||||
|
||||
@ -29,10 +32,31 @@ const PDFLoader = () => (
|
||||
</>
|
||||
);
|
||||
|
||||
export type PdfViewerRendererMode = 'editor' | 'preview' | 'signing';
|
||||
|
||||
const RendererErrorMessages: Record<
|
||||
PdfViewerRendererMode,
|
||||
{ title: MessageDescriptor; description: MessageDescriptor }
|
||||
> = {
|
||||
editor: {
|
||||
title: msg`Configuration Error`,
|
||||
description: msg`There was an issue rendering some fields, please review the fields and try again.`,
|
||||
},
|
||||
preview: {
|
||||
title: msg`Configuration Error`,
|
||||
description: msg`Something went wrong while rendering the document, some fields may be missing or corrupted.`,
|
||||
},
|
||||
signing: {
|
||||
title: msg`Configuration Error`,
|
||||
description: msg`Something went wrong while rendering the document, some fields may be missing or corrupted.`,
|
||||
},
|
||||
};
|
||||
|
||||
export type PdfViewerKonvaProps = {
|
||||
className?: string;
|
||||
onDocumentLoad?: () => void;
|
||||
customPageRenderer?: React.FunctionComponent;
|
||||
renderer: PdfViewerRendererMode;
|
||||
[key: string]: unknown;
|
||||
} & Omit<React.HTMLAttributes<HTMLDivElement>, 'onPageClick'>;
|
||||
|
||||
@ -40,11 +64,14 @@ export const PdfViewerKonva = ({
|
||||
className,
|
||||
onDocumentLoad,
|
||||
customPageRenderer,
|
||||
renderer,
|
||||
...props
|
||||
}: PdfViewerKonvaProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const $el = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { getPdfBuffer, currentEnvelopeItem } = useCurrentEnvelopeRender();
|
||||
const { getPdfBuffer, currentEnvelopeItem, renderError } = useCurrentEnvelopeRender();
|
||||
|
||||
const [width, setWidth] = useState(0);
|
||||
const [numPages, setNumPages] = useState(0);
|
||||
@ -92,6 +119,13 @@ export const PdfViewerKonva = ({
|
||||
|
||||
return (
|
||||
<div ref={$el} className={cn('w-full max-w-[800px]', className)} {...props}>
|
||||
{renderError && (
|
||||
<Alert variant="destructive" className="mb-4 max-w-[800px]">
|
||||
<AlertTitle>{t(RendererErrorMessages[renderer].title)}</AlertTitle>
|
||||
<AlertDescription>{t(RendererErrorMessages[renderer].description)}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{envelopeItemFile && Konva ? (
|
||||
<PDFDocument
|
||||
file={envelopeItemFile}
|
||||
|
||||
@ -66,7 +66,7 @@ export const DocumentDropzone = ({
|
||||
const heading = {
|
||||
document: msg`Upload Document`,
|
||||
template: msg`Upload Template Document`,
|
||||
envelope: msg`Envelope (beta)`,
|
||||
envelope: msg`Upload Envelope`,
|
||||
};
|
||||
|
||||
if (disabled && IS_BILLING_ENABLED()) {
|
||||
|
||||
Reference in New Issue
Block a user