Compare commits

...

1 Commits

Author SHA1 Message Date
986030cc38 chore: failed attempt at adding custom field labels
I have spent all day on it and for some reason, I can't figure it out
2024-02-18 16:01:03 +00:00
9 changed files with 101 additions and 26 deletions

View File

@ -20,6 +20,7 @@ export interface SetFieldsForDocumentOptions {
pageY: number; pageY: number;
pageWidth: number; pageWidth: number;
pageHeight: number; pageHeight: number;
label: string;
}[]; }[];
requestMetadata?: RequestMetadata; requestMetadata?: RequestMetadata;
} }

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Field" ADD COLUMN "label" TEXT;

View File

@ -210,15 +210,15 @@ model DocumentData {
} }
model DocumentMeta { model DocumentMeta {
id String @id @default(cuid()) id String @id @default(cuid())
subject String? subject String?
message String? message String?
timezone String? @default("Etc/UTC") @db.Text timezone String? @default("Etc/UTC") @db.Text
password String? password String?
dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
documentId Int @unique documentId Int @unique
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
redirectUrl String? redirectUrl String?
} }
enum ReadStatus { enum ReadStatus {
@ -283,6 +283,7 @@ model Field {
documentId Int? documentId Int?
templateId Int? templateId Int?
recipientId Int? recipientId Int?
label String?
type FieldType type FieldType
page Int page Int
positionX Decimal @default(0) positionX Decimal @default(0)

View File

@ -33,6 +33,7 @@ export const fieldRouter = router({
pageY: field.pageY, pageY: field.pageY,
pageWidth: field.pageWidth, pageWidth: field.pageWidth,
pageHeight: field.pageHeight, pageHeight: field.pageHeight,
label: field.label,
})), })),
requestMetadata: extractNextApiRequestMetadata(ctx.req), requestMetadata: extractNextApiRequestMetadata(ctx.req),
}); });

View File

@ -15,6 +15,7 @@ export const ZAddFieldsMutationSchema = z.object({
pageY: z.number().min(0), pageY: z.number().min(0),
pageWidth: z.number().min(0), pageWidth: z.number().min(0),
pageHeight: z.number().min(0), pageHeight: z.number().min(0),
label: z.string(),
}), }),
), ),
}); });

View File

@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Caveat } from 'next/font/google'; import { Caveat } from 'next/font/google';
import { Label } from '@radix-ui/react-label';
import { Check, ChevronsUpDown, Info } from 'lucide-react'; import { Check, ChevronsUpDown, Info } from 'lucide-react';
import { useFieldArray, useForm } from 'react-hook-form'; import { useFieldArray, useForm } from 'react-hook-form';
@ -20,6 +21,7 @@ import { cn } from '../../lib/utils';
import { Button } from '../button'; import { Button } from '../button';
import { Card, CardContent } from '../card'; import { Card, CardContent } from '../card';
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '../command'; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '../command';
import { Input } from '../input';
import { Popover, PopoverContent, PopoverTrigger } from '../popover'; import { Popover, PopoverContent, PopoverTrigger } from '../popover';
import { useStep } from '../stepper'; import { useStep } from '../stepper';
import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';
@ -32,6 +34,7 @@ import {
DocumentFlowFormContainerStep, DocumentFlowFormContainerStep,
} from './document-flow-root'; } from './document-flow-root';
import { FieldItem } from './field-item'; import { FieldItem } from './field-item';
import type { TDocumentFlowFormSchema } from './types';
import { type DocumentFlowStep, FRIENDLY_FIELD_TYPE } from './types'; import { type DocumentFlowStep, FRIENDLY_FIELD_TYPE } from './types';
const fontCaveat = Caveat({ const fontCaveat = Caveat({
@ -47,6 +50,8 @@ const DEFAULT_WIDTH_PERCENT = 15;
const MIN_HEIGHT_PX = 60; const MIN_HEIGHT_PX = 60;
const MIN_WIDTH_PX = 200; const MIN_WIDTH_PX = 200;
type ActiveField = TDocumentFlowFormSchema['fields'][0];
export type AddFieldsFormProps = { export type AddFieldsFormProps = {
documentFlow: DocumentFlowStep; documentFlow: DocumentFlowStep;
hideRecipients?: boolean; hideRecipients?: boolean;
@ -69,6 +74,7 @@ export const AddFieldsFormPartial = ({
control, control,
handleSubmit, handleSubmit,
formState: { isSubmitting }, formState: { isSubmitting },
setValue,
} = useForm<TAddFieldsFormSchema>({ } = useForm<TAddFieldsFormSchema>({
defaultValues: { defaultValues: {
fields: fields.map((field) => ({ fields: fields.map((field) => ({
@ -82,11 +88,23 @@ export const AddFieldsFormPartial = ({
pageHeight: Number(field.height), pageHeight: Number(field.height),
signerEmail: signerEmail:
recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '', recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '',
label: field.label ?? '',
})), })),
}, },
}); });
// const addLabelForm = useForm<TAddCustomLabelFormSchema>({
// defaultValues: {
// label: '',
// },
// });
// const onCustomLabelFormSubmit = (data: TAddCustomLabelFormSchema) => {
// console.log('Custom label', data);
// };
const onFormSubmit = handleSubmit(onSubmit); const onFormSubmit = handleSubmit(onSubmit);
// const onAddCustomLabelFormSubmit = addLabelForm.handleSubmit(onCustomLabelFormSubmit);
const { const {
append, append,
@ -101,6 +119,8 @@ export const AddFieldsFormPartial = ({
const [selectedField, setSelectedField] = useState<FieldType | null>(null); const [selectedField, setSelectedField] = useState<FieldType | null>(null);
const [selectedSigner, setSelectedSigner] = useState<Recipient | null>(null); const [selectedSigner, setSelectedSigner] = useState<Recipient | null>(null);
const [showRecipientsSelector, setShowRecipientsSelector] = useState(false); const [showRecipientsSelector, setShowRecipientsSelector] = useState(false);
const [activeField, setActiveField] = useState<ActiveField | null>(null);
const [fieldLabel, setFieldLabel] = useState<Record<string, string> | null>({});
const hasSelectedSignerBeenSent = selectedSigner?.sendStatus === SendStatus.SENT; const hasSelectedSignerBeenSent = selectedSigner?.sendStatus === SendStatus.SENT;
@ -186,12 +206,13 @@ export const AddFieldsFormPartial = ({
pageWidth: fieldPageWidth, pageWidth: fieldPageWidth,
pageHeight: fieldPageHeight, pageHeight: fieldPageHeight,
signerEmail: selectedSigner.email, signerEmail: selectedSigner.email,
label: activeField?.label ?? fieldLabel?.[activeField?.formId ?? ''] ?? '',
}); });
setIsFieldWithinBounds(false); setIsFieldWithinBounds(false);
setSelectedField(null); setSelectedField(null);
}, },
[append, isWithinPageBounds, selectedField, selectedSigner, getPage], [append, isWithinPageBounds, selectedField, selectedSigner, activeField, fieldLabel, getPage],
); );
const onFieldResize = useCallback( const onFieldResize = useCallback(
@ -257,7 +278,7 @@ export const AddFieldsFormPartial = ({
window.removeEventListener('mousemove', onMouseMove); window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', onMouseClick); window.removeEventListener('mouseup', onMouseClick);
}; };
}, [onMouseClick, onMouseMove, selectedField]); }, [onMouseClick, onMouseMove, selectedField, activeField]);
useEffect(() => { useEffect(() => {
const observer = new MutationObserver((_mutations) => { const observer = new MutationObserver((_mutations) => {
@ -311,6 +332,8 @@ export const AddFieldsFormPartial = ({
); );
}, [recipientsByRole]); }, [recipientsByRole]);
console.log(localFields[0].label);
return ( return (
<> <>
<DocumentFlowFormContainerHeader <DocumentFlowFormContainerHeader
@ -342,19 +365,24 @@ export const AddFieldsFormPartial = ({
</Card> </Card>
)} )}
{localFields.map((field, index) => ( {localFields.map((field, index) => {
<FieldItem return (
key={index} <FieldItem
field={field} key={index}
disabled={selectedSigner?.email !== field.signerEmail || hasSelectedSignerBeenSent} field={field}
minHeight={fieldBounds.current.height} disabled={selectedSigner?.email !== field.signerEmail || hasSelectedSignerBeenSent}
minWidth={fieldBounds.current.width} minHeight={fieldBounds.current.height}
passive={isFieldWithinBounds && !!selectedField} minWidth={fieldBounds.current.width}
onResize={(options) => onFieldResize(options, index)} passive={isFieldWithinBounds && !!selectedField}
onMove={(options) => onFieldMove(options, index)} onResize={(options) => onFieldResize(options, index)}
onRemove={() => remove(index)} onClick={(field) => {
/> setActiveField(field);
))} }}
onMove={(options) => onFieldMove(options, index)}
onRemove={() => remove(index)}
/>
);
})}
{!hideRecipients && ( {!hideRecipients && (
<Popover open={showRecipientsSelector} onOpenChange={setShowRecipientsSelector}> <Popover open={showRecipientsSelector} onOpenChange={setShowRecipientsSelector}>
@ -462,7 +490,7 @@ export const AddFieldsFormPartial = ({
</Popover> </Popover>
)} )}
<div className="-mx-2 flex-1 overflow-y-auto px-2"> <div className="-mx-2 flex-1 px-2">
<fieldset disabled={isFieldsDisabled} className="grid grid-cols-2 gap-x-4 gap-y-8"> <fieldset disabled={isFieldsDisabled} className="grid grid-cols-2 gap-x-4 gap-y-8">
<button <button
type="button" type="button"
@ -554,6 +582,40 @@ export const AddFieldsFormPartial = ({
</button> </button>
</fieldset> </fieldset>
</div> </div>
{activeField && (
<div className="-mx-2 my-8 flex-1 gap-1.5 px-2">
<Label htmlFor="form-label">Custom Label</Label>
<div className="mt-2 flex w-full items-center space-x-2">
<Input
type="text"
className="w-full"
placeholder="Label"
id="form-label"
value={fieldLabel?.[activeField.formId] ?? activeField.label}
onChange={(e) => {
setFieldLabel((prev) => ({
...prev,
[activeField.formId]: e.target.value,
}));
setValue(
'fields',
localFields.map((field) => {
if (field.formId === activeField.formId) {
return {
...field,
label: fieldLabel?.[activeField.formId] ?? activeField.label ?? '',
};
}
return field;
}),
);
}}
/>
</div>
</div>
)}
</div> </div>
</DocumentFlowFormContainerContent> </DocumentFlowFormContainerContent>

View File

@ -14,6 +14,7 @@ export const ZAddFieldsFormSchema = z.object({
pageY: z.number().min(0), pageY: z.number().min(0),
pageWidth: z.number().min(0), pageWidth: z.number().min(0),
pageHeight: z.number().min(0), pageHeight: z.number().min(0),
label: z.string().min(1),
}), }),
), ),
}); });

View File

@ -23,6 +23,7 @@ export type FieldItemProps = {
minWidth?: number; minWidth?: number;
onResize?: (_node: HTMLElement) => void; onResize?: (_node: HTMLElement) => void;
onMove?: (_node: HTMLElement) => void; onMove?: (_node: HTMLElement) => void;
onClick?: (field: Field) => void;
onRemove?: () => void; onRemove?: () => void;
}; };
@ -35,6 +36,7 @@ export const FieldItem = ({
onResize, onResize,
onMove, onMove,
onRemove, onRemove,
onClick,
}: FieldItemProps) => { }: FieldItemProps) => {
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
const [coords, setCoords] = useState({ const [coords, setCoords] = useState({
@ -106,7 +108,10 @@ export const FieldItem = ({
width: coords.pageWidth, width: coords.pageWidth,
}} }}
bounds={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`} bounds={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`}
onDragStart={() => setActive(true)} onDragStart={() => {
setActive(true);
onClick?.(field);
}}
onResizeStart={() => setActive(true)} onResizeStart={() => setActive(true)}
onResizeStop={(_e, _d, ref) => { onResizeStop={(_e, _d, ref) => {
setActive(false); setActive(false);

View File

@ -30,6 +30,7 @@ export const ZDocumentFlowFormSchema = z.object({
pageY: z.number().min(0), pageY: z.number().min(0),
pageWidth: z.number().min(0), pageWidth: z.number().min(0),
pageHeight: z.number().min(0), pageHeight: z.number().min(0),
label: z.string().optional(),
}), }),
), ),