mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Compare commits
1 Commits
v1.12.0-rc
...
exp/custom
| Author | SHA1 | Date | |
|---|---|---|---|
| 986030cc38 |
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Field" ADD COLUMN "label" TEXT;
|
||||||
@ -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)
|
||||||
|
|||||||
@ -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),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user