feat: highlight problematic fields (#1330)

This commit is contained in:
Catalin Pit
2024-10-08 13:55:20 +03:00
committed by GitHub
parent cd3d9b701b
commit d40ed94b74
14 changed files with 158 additions and 68 deletions

View File

@ -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>
)}
</>
);
};

View File

@ -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?.();