mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 08:13:56 +10:00
feat: highlight problematic fields (#1330)
This commit is contained in:
@ -5,6 +5,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Caveat } from 'next/font/google';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import {
|
||||
CalendarDays,
|
||||
Check,
|
||||
@ -32,6 +33,7 @@ import {
|
||||
ZFieldMetaSchema,
|
||||
} from '@documenso/lib/types/field-meta';
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { validateFieldsUninserted } from '@documenso/lib/utils/fields';
|
||||
import {
|
||||
canRecipientBeModified,
|
||||
canRecipientFieldsBeModified,
|
||||
@ -39,6 +41,7 @@ import {
|
||||
import type { Field, Recipient } from '@documenso/prisma/client';
|
||||
import { FieldType, RecipientRole, SendStatus } from '@documenso/prisma/client';
|
||||
|
||||
import { FieldToolTip } from '../../components/field/field-tooltip';
|
||||
import { getSignerColorStyles, useSignerColors } from '../../lib/signer-colors';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Alert, AlertDescription } from '../alert';
|
||||
@ -96,12 +99,6 @@ export type AddFieldsFormProps = {
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
/*
|
||||
I hate this, but due to TailwindCSS JIT, I couldnn't find a better way to do this for now.
|
||||
|
||||
TODO: Try to find a better way to do this.
|
||||
*/
|
||||
|
||||
export const AddFieldsFormPartial = ({
|
||||
documentFlow,
|
||||
hideRecipients = false,
|
||||
@ -195,6 +192,7 @@ export const AddFieldsFormPartial = ({
|
||||
const selectedSignerStyles = useSignerColors(
|
||||
selectedSignerIndex === -1 ? 0 : selectedSignerIndex,
|
||||
);
|
||||
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
|
||||
|
||||
const filterFieldsWithEmptyValues = (fields: typeof localFields, fieldType: string) =>
|
||||
fields
|
||||
@ -228,6 +226,38 @@ export const AddFieldsFormPartial = ({
|
||||
const hasErrors =
|
||||
emptyCheckboxFields.length > 0 || emptyRadioFields.length > 0 || emptySelectFields.length > 0;
|
||||
|
||||
const fieldsWithError = useMemo(() => {
|
||||
const fields = localFields.filter((field) => {
|
||||
const hasError =
|
||||
((field.type === FieldType.CHECKBOX ||
|
||||
field.type === FieldType.RADIO ||
|
||||
field.type === FieldType.DROPDOWN) &&
|
||||
field.fieldMeta === undefined) ||
|
||||
(field.fieldMeta && 'values' in field.fieldMeta && field?.fieldMeta?.values?.length === 0);
|
||||
|
||||
return hasError;
|
||||
});
|
||||
|
||||
const mappedFields = fields.map((field) => ({
|
||||
id: field.nativeId ?? 0,
|
||||
secondaryId: field.formId,
|
||||
documentId: null,
|
||||
templateId: null,
|
||||
recipientId: 0,
|
||||
type: field.type,
|
||||
page: field.pageNumber,
|
||||
positionX: new Prisma.Decimal(field.pageX),
|
||||
positionY: new Prisma.Decimal(field.pageY),
|
||||
width: new Prisma.Decimal(field.pageWidth),
|
||||
height: new Prisma.Decimal(field.pageHeight),
|
||||
customText: '',
|
||||
inserted: true,
|
||||
fieldMeta: field.fieldMeta ?? null,
|
||||
}));
|
||||
|
||||
return mappedFields;
|
||||
}, [localFields]);
|
||||
|
||||
const isFieldsDisabled = useMemo(() => {
|
||||
if (!selectedSigner) {
|
||||
return true;
|
||||
@ -515,6 +545,14 @@ export const AddFieldsFormPartial = ({
|
||||
|
||||
if (!everySignerHasSignature) {
|
||||
setIsMissingSignatureDialogVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setValidateUninsertedFields(true);
|
||||
const isFieldsValid = validateFieldsUninserted();
|
||||
|
||||
if (!isFieldsValid) {
|
||||
return;
|
||||
} else {
|
||||
void onFormSubmit();
|
||||
}
|
||||
@ -566,6 +604,10 @@ export const AddFieldsFormPartial = ({
|
||||
{isDocumentPdfLoaded &&
|
||||
localFields.map((field, index) => {
|
||||
const recipientIndex = recipients.findIndex((r) => r.email === field.signerEmail);
|
||||
const hasFieldError =
|
||||
emptyCheckboxFields.find((f) => f.formId === field.formId) ||
|
||||
emptyRadioFields.find((f) => f.formId === field.formId) ||
|
||||
emptySelectFields.find((f) => f.formId === field.formId);
|
||||
|
||||
return (
|
||||
<FieldItem
|
||||
@ -590,6 +632,7 @@ export const AddFieldsFormPartial = ({
|
||||
handleAdvancedSettings();
|
||||
}}
|
||||
hideRecipients={hideRecipients}
|
||||
hasErrors={!!hasFieldError}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -1018,7 +1061,6 @@ export const AddFieldsFormPartial = ({
|
||||
<DocumentFlowFormContainerActions
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
disableNextStep={hasErrors}
|
||||
onGoBackClick={() => {
|
||||
previousStep();
|
||||
remove();
|
||||
@ -1035,6 +1077,11 @@ export const AddFieldsFormPartial = ({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{validateUninsertedFields && fieldsWithError[0] && (
|
||||
<FieldToolTip key={fieldsWithError[0].id} field={fieldsWithError[0]} color="warning">
|
||||
<Trans>Empty field</Trans>
|
||||
</FieldToolTip>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -44,6 +44,7 @@ export type FieldItemProps = {
|
||||
onBlur?: () => void;
|
||||
recipientIndex?: number;
|
||||
hideRecipients?: boolean;
|
||||
hasErrors?: boolean;
|
||||
};
|
||||
|
||||
export const FieldItem = ({
|
||||
@ -61,6 +62,7 @@ export const FieldItem = ({
|
||||
onAdvancedSettings,
|
||||
recipientIndex = 0,
|
||||
hideRecipients = false,
|
||||
hasErrors,
|
||||
}: FieldItemProps) => {
|
||||
const [active, setActive] = useState(false);
|
||||
const [coords, setCoords] = useState({
|
||||
@ -201,10 +203,15 @@ export const FieldItem = ({
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex h-full w-full items-center justify-center bg-white',
|
||||
!hasErrors && signerStyles.default.base,
|
||||
!hasErrors && signerStyles.default.fieldItem,
|
||||
{
|
||||
'rounded-lg border border-red-400 bg-red-400/20 shadow-[0_0_0_5px_theme(colors.red.500/10%),0_0_0_2px_theme(colors.red.500/40%),0_0_0_0.5px_theme(colors.red.500)]':
|
||||
hasErrors,
|
||||
},
|
||||
!fixedSize && '[container-type:size]',
|
||||
signerStyles.default.base,
|
||||
signerStyles.default.fieldItem,
|
||||
)}
|
||||
data-error={hasErrors ? 'true' : undefined}
|
||||
onClick={() => {
|
||||
setSettingsActive((prev) => !prev);
|
||||
onFocus?.();
|
||||
|
||||
Reference in New Issue
Block a user