Compare commits
2 Commits
feat/check
...
feat/updat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc035ed08c | ||
|
|
fb2fd17ad8 |
@@ -2,10 +2,8 @@
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import launchWeekTwoImage from '@documenso/assets/images/background-lw-2.png';
|
||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
@@ -48,16 +46,8 @@ export default function MarketingLayout({ children }: MarketingLayoutProps) {
|
||||
})}
|
||||
>
|
||||
{showProfilesAnnouncementBar && (
|
||||
<div className="relative inline-flex w-full items-center justify-center overflow-hidden px-4 py-2.5">
|
||||
<div className="absolute inset-0 -z-[1]">
|
||||
<Image
|
||||
src={launchWeekTwoImage}
|
||||
className="h-full w-full object-cover"
|
||||
alt="Launch Week 2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="text-background text-center text-sm text-white">
|
||||
<div className="relative inline-flex w-full items-center justify-center overflow-hidden bg-[#e7f3df] px-4 py-2.5">
|
||||
<div className="text-foreground text-center text-sm font-medium">
|
||||
Claim your documenso public profile username now!{' '}
|
||||
<span className="hidden font-semibold md:inline">documenso.com/u/yourname</span>
|
||||
<div className="mt-1.5 block md:ml-4 md:mt-0 md:inline-block">
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useTransition } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
|
||||
import type { Recipient } from '@documenso/prisma/client';
|
||||
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Checkbox } from '@documenso/ui/primitives/checkbox';
|
||||
import { Form, FormControl, FormField } from '@documenso/ui/primitives/form/form';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { SigningFieldContainer } from './signing-field-container';
|
||||
|
||||
export type CheckboxFieldProps = {
|
||||
field: FieldWithSignature;
|
||||
recipient: Recipient;
|
||||
};
|
||||
|
||||
const CheckBoxSchema = z.object({
|
||||
check: z.boolean().default(false).optional(),
|
||||
});
|
||||
|
||||
export const CheckboxField = ({ field, recipient }: CheckboxFieldProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
// const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
|
||||
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||
|
||||
const {
|
||||
mutateAsync: removeSignedFieldWithToken,
|
||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||
|
||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||
|
||||
const form = useForm<z.infer<typeof CheckBoxSchema>>({
|
||||
resolver: zodResolver(CheckBoxSchema),
|
||||
defaultValues: {
|
||||
check: false,
|
||||
},
|
||||
});
|
||||
|
||||
const onSign = useCallback(
|
||||
async (authOptions?: TRecipientActionAuth) => {
|
||||
try {
|
||||
await signFieldWithToken({
|
||||
token: recipient.token,
|
||||
fieldId: field.id,
|
||||
value: 'checked',
|
||||
isBase64: true,
|
||||
authOptions,
|
||||
});
|
||||
|
||||
startTransition(() => router.refresh());
|
||||
} catch (err) {
|
||||
const error = AppError.parseError(err);
|
||||
|
||||
if (error.code === AppErrorCode.UNAUTHORIZED) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while signing the document.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
},
|
||||
[field.id, recipient.token, router, signFieldWithToken, toast],
|
||||
);
|
||||
|
||||
const onRemove = async () => {
|
||||
try {
|
||||
await removeSignedFieldWithToken({
|
||||
token: recipient.token,
|
||||
fieldId: field.id,
|
||||
});
|
||||
|
||||
startTransition(() => router.refresh());
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while removing the text.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SigningFieldContainer
|
||||
field={field}
|
||||
onSign={onSign}
|
||||
onRemove={onRemove}
|
||||
type="Checkbox"
|
||||
raw={true}
|
||||
>
|
||||
<Form {...form}>
|
||||
<form>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="check"
|
||||
render={({ field }) => (
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
className="h-8 w-8"
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
</SigningFieldContainer>
|
||||
);
|
||||
};
|
||||
@@ -35,10 +35,8 @@ export type SignatureFieldProps = {
|
||||
*/
|
||||
onSign?: (documentAuthValue?: TRecipientActionAuth) => Promise<void> | void;
|
||||
onRemove?: () => Promise<void> | void;
|
||||
type?: 'Date' | 'Email' | 'Name' | 'Signature' | 'Checkbox';
|
||||
type?: 'Date' | 'Email' | 'Name' | 'Signature';
|
||||
tooltipText?: string | null;
|
||||
|
||||
raw?: boolean;
|
||||
};
|
||||
|
||||
export const SigningFieldContainer = ({
|
||||
@@ -50,7 +48,6 @@ export const SigningFieldContainer = ({
|
||||
children,
|
||||
type,
|
||||
tooltipText,
|
||||
raw = false,
|
||||
}: SignatureFieldProps) => {
|
||||
const { executeActionAuthProcedure, isAuthRedirectRequired } = useRequiredDocumentAuthContext();
|
||||
|
||||
@@ -106,8 +103,8 @@ export const SigningFieldContainer = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<FieldRootContainer raw={raw} field={field}>
|
||||
{!field.inserted && !loading && !raw && (
|
||||
<FieldRootContainer field={field}>
|
||||
{!field.inserted && !loading && (
|
||||
<button
|
||||
type="submit"
|
||||
className="absolute inset-0 z-10 h-full w-full"
|
||||
|
||||
@@ -12,7 +12,6 @@ import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
||||
|
||||
import { truncateTitle } from '~/helpers/truncate-title';
|
||||
|
||||
import { CheckboxField } from './checkbox-field';
|
||||
import { DateField } from './date-field';
|
||||
import { EmailField } from './email-field';
|
||||
import { SigningForm } from './form';
|
||||
@@ -95,9 +94,6 @@ export const SigningPageView = ({ document, recipient, fields }: SigningPageView
|
||||
.with(FieldType.TEXT, () => (
|
||||
<TextField key={field.id} field={field} recipient={recipient} />
|
||||
))
|
||||
.with(FieldType.CHECKBOX, () => (
|
||||
<CheckboxField key={field.id} field={field} recipient={recipient} />
|
||||
))
|
||||
.otherwise(() => null),
|
||||
)}
|
||||
</ElementVisible>
|
||||
|
||||
@@ -188,17 +188,10 @@ export const signFieldWithToken = async ({
|
||||
type,
|
||||
data: signatureImageAsBase64 || typedSignature || '',
|
||||
}))
|
||||
.with(
|
||||
FieldType.DATE,
|
||||
FieldType.EMAIL,
|
||||
FieldType.NAME,
|
||||
FieldType.TEXT,
|
||||
FieldType.CHECKBOX,
|
||||
(type) => ({
|
||||
type,
|
||||
data: updatedField.customText,
|
||||
}),
|
||||
)
|
||||
.with(FieldType.DATE, FieldType.EMAIL, FieldType.NAME, FieldType.TEXT, (type) => ({
|
||||
type,
|
||||
data: updatedField.customText,
|
||||
}))
|
||||
.exhaustive(),
|
||||
fieldSecurity: derivedRecipientActionAuth
|
||||
? {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
|
||||
import fontkit from '@pdf-lib/fontkit';
|
||||
import type { PDFDocument } from 'pdf-lib';
|
||||
import { StandardFonts } from 'pdf-lib';
|
||||
import { PDFDocument, StandardFonts } from 'pdf-lib';
|
||||
|
||||
import {
|
||||
DEFAULT_HANDWRITING_FONT_SIZE,
|
||||
@@ -19,7 +18,6 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
||||
);
|
||||
|
||||
const isSignatureField = isSignatureFieldType(field.type);
|
||||
const isCheckboxField = field.type === FieldType.CHECKBOX;
|
||||
|
||||
pdf.registerFontkit(fontkit);
|
||||
|
||||
@@ -75,28 +73,6 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
});
|
||||
} else if (isCheckboxField) {
|
||||
const form = pdf.getForm();
|
||||
const checkBox = form.createCheckBox(`checkBox.field.${field.id}`);
|
||||
|
||||
const textX = fieldX + fieldWidth / 2;
|
||||
let textY = fieldY + fieldHeight / 2;
|
||||
|
||||
textY = pageHeight - textY;
|
||||
|
||||
checkBox.addToPage(page, {
|
||||
x: textX,
|
||||
y: textY,
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderWidth: 1,
|
||||
});
|
||||
|
||||
if (field.customText === '✓') {
|
||||
checkBox.check();
|
||||
}
|
||||
|
||||
form.getField(`checkBox.field.${field.id}`).enableReadOnly();
|
||||
} else {
|
||||
const longestLineInTextForWidth = field.customText
|
||||
.split('\n')
|
||||
@@ -126,3 +102,14 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
||||
|
||||
return pdf;
|
||||
};
|
||||
|
||||
export const insertFieldInPDFBytes = async (
|
||||
pdf: ArrayBuffer | Uint8Array | string,
|
||||
field: FieldWithSignature,
|
||||
) => {
|
||||
const pdfDoc = await PDFDocument.load(pdf);
|
||||
|
||||
await insertFieldInPDF(pdfDoc, field);
|
||||
|
||||
return await pdfDoc.save();
|
||||
};
|
||||
|
||||
@@ -231,10 +231,6 @@ export const ZDocumentAuditLogEventDocumentFieldInsertedSchema = z.object({
|
||||
type: z.literal(FieldType.TEXT),
|
||||
data: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.CHECKBOX),
|
||||
data: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.union([z.literal(FieldType.SIGNATURE), z.literal(FieldType.FREE_SIGNATURE)]),
|
||||
data: z.string(),
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "FieldType" ADD VALUE 'CHECKBOX';
|
||||
@@ -379,7 +379,6 @@ enum FieldType {
|
||||
EMAIL
|
||||
DATE
|
||||
TEXT
|
||||
CHECKBOX
|
||||
}
|
||||
|
||||
model Field {
|
||||
|
||||
@@ -23,7 +23,6 @@ export const mapField = (
|
||||
.with(FieldType.EMAIL, () => signer.email)
|
||||
.with(FieldType.NAME, () => signer.name)
|
||||
.with(FieldType.TEXT, () => signer.customText)
|
||||
.with(FieldType.CHECKBOX, () => signer.customText)
|
||||
.otherwise(() => '');
|
||||
|
||||
return {
|
||||
|
||||
@@ -18,7 +18,6 @@ export type FieldRootContainerProps = {
|
||||
export type FieldContainerPortalProps = {
|
||||
field: Field;
|
||||
className?: string;
|
||||
raw?: boolean;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
@@ -45,7 +44,7 @@ export function FieldContainerPortal({
|
||||
);
|
||||
}
|
||||
|
||||
export function FieldRootContainer({ field, children, raw = false }: FieldContainerPortalProps) {
|
||||
export function FieldRootContainer({ field, children }: FieldContainerPortalProps) {
|
||||
const [isValidating, setIsValidating] = useState(false);
|
||||
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
@@ -72,36 +71,21 @@ export function FieldRootContainer({ field, children, raw = false }: FieldContai
|
||||
|
||||
return (
|
||||
<FieldContainerPortal field={field}>
|
||||
{!raw && (
|
||||
<Card
|
||||
id={`field-${field.id}`}
|
||||
className={cn(
|
||||
'field-card-container bg-background relative z-20 h-full w-full transition-all',
|
||||
{
|
||||
'border-orange-300 ring-1 ring-orange-300': !field.inserted && isValidating,
|
||||
},
|
||||
)}
|
||||
ref={ref}
|
||||
data-inserted={field.inserted ? 'true' : 'false'}
|
||||
>
|
||||
<CardContent className="text-foreground hover:shadow-primary-foreground group flex h-full w-full flex-col items-center justify-center p-2">
|
||||
{children}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{raw && (
|
||||
<div
|
||||
id={`field-${field.id}`}
|
||||
className={cn('field-card-container bg-background relative z-20 transition-all', {
|
||||
<Card
|
||||
id={`field-${field.id}`}
|
||||
className={cn(
|
||||
'field-card-container bg-background relative z-20 h-full w-full transition-all',
|
||||
{
|
||||
'border-orange-300 ring-1 ring-orange-300': !field.inserted && isValidating,
|
||||
})}
|
||||
ref={ref}
|
||||
data-inserted={field.inserted ? 'true' : 'false'}
|
||||
>
|
||||
},
|
||||
)}
|
||||
ref={ref}
|
||||
data-inserted={field.inserted ? 'true' : 'false'}
|
||||
>
|
||||
<CardContent className="text-foreground hover:shadow-primary-foreground group flex h-full w-full flex-col items-center justify-center p-2">
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</FieldContainerPortal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import { FieldType, SendStatus } from '@documenso/prisma/client';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Button } from '../button';
|
||||
import { Card, CardContent } from '../card';
|
||||
import { Checkbox } from '../checkbox';
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '../command';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../popover';
|
||||
import { useStep } from '../stepper';
|
||||
@@ -136,17 +135,11 @@ export const AddFieldsFormPartial = ({
|
||||
);
|
||||
|
||||
setCoords({
|
||||
x:
|
||||
selectedField === FieldType.CHECKBOX
|
||||
? event.clientX - 16
|
||||
: event.clientX - fieldBounds.current.width / 2,
|
||||
y:
|
||||
selectedField === FieldType.CHECKBOX
|
||||
? event.clientY - 16
|
||||
: event.clientY - fieldBounds.current.height / 2,
|
||||
x: event.clientX - fieldBounds.current.width / 2,
|
||||
y: event.clientY - fieldBounds.current.height / 2,
|
||||
});
|
||||
},
|
||||
[isWithinPageBounds, selectedField],
|
||||
[isWithinPageBounds],
|
||||
);
|
||||
|
||||
const onMouseClick = useCallback(
|
||||
@@ -156,7 +149,6 @@ export const AddFieldsFormPartial = ({
|
||||
}
|
||||
|
||||
const $page = getPage(event, PDF_VIEWER_PAGE_SELECTOR);
|
||||
const isCheckboxField = selectedField === FieldType.CHECKBOX;
|
||||
|
||||
if (
|
||||
!$page ||
|
||||
@@ -180,8 +172,8 @@ export const AddFieldsFormPartial = ({
|
||||
let pageY = ((event.pageY - top) / height) * 100;
|
||||
|
||||
// Get the bounds as a percentage of the page width and height
|
||||
const fieldPageWidth = ((isCheckboxField ? 32 : fieldBounds.current.width) / width) * 100;
|
||||
const fieldPageHeight = ((isCheckboxField ? 32 : fieldBounds.current.height) / height) * 100;
|
||||
const fieldPageWidth = (fieldBounds.current.width / width) * 100;
|
||||
const fieldPageHeight = (fieldBounds.current.height / height) * 100;
|
||||
|
||||
// And center it based on the bounds
|
||||
pageX -= fieldPageWidth / 2;
|
||||
@@ -330,8 +322,7 @@ export const AddFieldsFormPartial = ({
|
||||
|
||||
<DocumentFlowFormContainerContent>
|
||||
<div className="flex flex-col">
|
||||
{/* When it is not a checkbox field */}
|
||||
{selectedField && selectedField !== FieldType.CHECKBOX && (
|
||||
{selectedField && (
|
||||
<Card
|
||||
className={cn(
|
||||
'bg-field-card/80 pointer-events-none fixed z-50 cursor-pointer border-2 backdrop-blur-[1px]',
|
||||
@@ -353,27 +344,6 @@ export const AddFieldsFormPartial = ({
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Checkbox Field */}
|
||||
{selectedField && selectedField === FieldType.CHECKBOX && (
|
||||
<div
|
||||
className="pointer-events-none fixed z-50"
|
||||
style={{
|
||||
top: coords.y,
|
||||
left: coords.x,
|
||||
height: 6 * 4,
|
||||
width: 6 * 4,
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
className={cn(
|
||||
'bg-field-card/80 h-8 w-8 border-2 backdrop-blur-[1px]',
|
||||
'shadow-[0_0_0_4px_theme(colors.gray.100/70%),0_0_0_1px_theme(colors.gray.100/70%),0_0_0_0.5px_theme(colors.primary.DEFAULT/70%)]',
|
||||
'border-field-card-border',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isDocumentPdfLoaded &&
|
||||
localFields.map((field, index) => (
|
||||
<FieldItem
|
||||
@@ -607,28 +577,6 @@ export const AddFieldsFormPartial = ({
|
||||
</CardContent>
|
||||
</Card>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="group h-full w-full"
|
||||
onClick={() => setSelectedField(FieldType.CHECKBOX)}
|
||||
onMouseDown={() => setSelectedField(FieldType.CHECKBOX)}
|
||||
data-selected={selectedField === FieldType.CHECKBOX ? true : undefined}
|
||||
>
|
||||
<Card className="group-data-[selected]:border-documenso h-full w-full cursor-pointer group-disabled:opacity-50">
|
||||
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
|
||||
<p
|
||||
className={cn(
|
||||
'text-muted-foreground group-data-[selected]:text-foreground text-xl font-medium',
|
||||
)}
|
||||
>
|
||||
{'Checkbox'}
|
||||
</p>
|
||||
|
||||
<p className="text-muted-foreground mt-2 text-xs">Checkbox</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</button>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,11 +147,6 @@ export const AddSignatureFormPartial = ({
|
||||
return !form.formState.errors.customText;
|
||||
}
|
||||
|
||||
if (fieldType === FieldType.CHECKBOX) {
|
||||
await form.trigger('customText');
|
||||
return !form.formState.errors.customText;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { useId, useMemo, useState } from 'react';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { motion } from 'framer-motion';
|
||||
import { InfoIcon, Plus, Trash } from 'lucide-react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
|
||||
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
||||
@@ -60,6 +61,9 @@ export const AddSignersFormPartial = ({
|
||||
}: AddSignersFormProps) => {
|
||||
const { toast } = useToast();
|
||||
const { remaining } = useLimits();
|
||||
const { data: session } = useSession();
|
||||
const user = session?.user;
|
||||
const [selfSignerFormId, setSelfSignerFormId] = useState<string | undefined>(undefined);
|
||||
|
||||
const initialId = useId();
|
||||
|
||||
@@ -135,6 +139,20 @@ export const AddSignersFormPartial = ({
|
||||
);
|
||||
};
|
||||
|
||||
const onAddSelfSigner = () => {
|
||||
const newSelfSignerId = nanoid(12);
|
||||
|
||||
appendSigner({
|
||||
formId: newSelfSignerId,
|
||||
name: user?.name ?? '',
|
||||
email: user?.email ?? '',
|
||||
role: RecipientRole.SIGNER,
|
||||
actionAuth: undefined,
|
||||
});
|
||||
|
||||
setSelfSignerFormId(newSelfSignerId);
|
||||
};
|
||||
|
||||
const onAddSigner = () => {
|
||||
appendSigner({
|
||||
formId: nanoid(12),
|
||||
@@ -158,6 +176,10 @@ export const AddSignersFormPartial = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (signer.formId === selfSignerFormId) {
|
||||
setSelfSignerFormId(undefined);
|
||||
}
|
||||
|
||||
removeSigner(index);
|
||||
};
|
||||
|
||||
@@ -209,8 +231,13 @@ export const AddSignersFormPartial = ({
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
|
||||
{...field}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
hasBeenSentToRecipientId(signer.nativeId) ||
|
||||
signer.formId === selfSignerFormId ||
|
||||
signers[index].email === user?.email
|
||||
}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -237,8 +264,13 @@ export const AddSignersFormPartial = ({
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="Name"
|
||||
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
|
||||
{...field}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
hasBeenSentToRecipientId(signer.nativeId) ||
|
||||
signer.formId === selfSignerFormId ||
|
||||
signers[index].email === user?.email
|
||||
}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -258,7 +290,12 @@ export const AddSignersFormPartial = ({
|
||||
<Select
|
||||
{...field}
|
||||
onValueChange={field.onChange}
|
||||
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
hasBeenSentToRecipientId(signer.nativeId) ||
|
||||
signer.formId === selfSignerFormId ||
|
||||
signers[index].email === user?.email
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="bg-background text-muted-foreground">
|
||||
<SelectValue placeholder="Inherit authentication method" />
|
||||
@@ -330,7 +367,12 @@ export const AddSignersFormPartial = ({
|
||||
<Select
|
||||
{...field}
|
||||
onValueChange={field.onChange}
|
||||
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
hasBeenSentToRecipientId(signer.nativeId) ||
|
||||
signer.formId === selfSignerFormId ||
|
||||
signers[index].email === user?.email
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="bg-background w-[60px]">
|
||||
{/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */}
|
||||
@@ -403,12 +445,28 @@ export const AddSignersFormPartial = ({
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
className="flex-1"
|
||||
disabled={isSubmitting || signers.length >= remaining.recipients}
|
||||
onClick={() => onAddSigner()}
|
||||
>
|
||||
<Plus className="-ml-1 mr-2 h-5 w-5" />
|
||||
Add Signer
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 bg-black/5 hover:bg-black/10"
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
signers.length >= remaining.recipients ||
|
||||
!!selfSignerFormId ||
|
||||
signers.some((signer) => signer.email === user?.email)
|
||||
}
|
||||
onClick={() => onAddSelfSigner()}
|
||||
>
|
||||
<Plus className="-ml-1 mr-2 h-5 w-5" />
|
||||
Add myself
|
||||
</Button>
|
||||
|
||||
{!alwaysShowAdvancedSettings && isDocumentEnterprise && (
|
||||
<div className="flex flex-row items-center">
|
||||
|
||||
@@ -10,10 +10,8 @@ import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Card, CardContent } from '../card';
|
||||
import { Checkbox } from '../checkbox';
|
||||
import type { TDocumentFlowFormSchema } from './types';
|
||||
import { FRIENDLY_FIELD_TYPE } from './types';
|
||||
import { FieldType } from '.prisma/client';
|
||||
|
||||
type Field = TDocumentFlowFormSchema['fields'][0];
|
||||
|
||||
@@ -46,8 +44,6 @@ export const FieldItem = ({
|
||||
pageWidth: 0,
|
||||
});
|
||||
|
||||
const isCheckboxField = field.type === FieldType.CHECKBOX;
|
||||
|
||||
const calculateCoords = useCallback(() => {
|
||||
const $page = document.querySelector<HTMLElement>(
|
||||
`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`,
|
||||
@@ -106,8 +102,8 @@ export const FieldItem = ({
|
||||
default={{
|
||||
x: coords.pageX,
|
||||
y: coords.pageY,
|
||||
height: isCheckboxField ? 32 : coords.pageHeight,
|
||||
width: isCheckboxField ? 32 : coords.pageWidth,
|
||||
height: coords.pageHeight,
|
||||
width: coords.pageWidth,
|
||||
}}
|
||||
bounds={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`}
|
||||
onDragStart={() => setActive(true)}
|
||||
@@ -123,13 +119,7 @@ export const FieldItem = ({
|
||||
>
|
||||
{!disabled && (
|
||||
<button
|
||||
className={cn(
|
||||
'text-muted-foreground/50 hover:text-muted-foreground/80 bg-background absolute -right-2 -top-2 z-20 flex items-center justify-center rounded-full border',
|
||||
{
|
||||
'h-8 w-8': !isCheckboxField,
|
||||
'h-6 w-6': isCheckboxField,
|
||||
},
|
||||
)}
|
||||
className="text-muted-foreground/50 hover:text-muted-foreground/80 bg-background absolute -right-2 -top-2 z-20 flex h-8 w-8 items-center justify-center rounded-full border"
|
||||
onClick={() => onRemove?.()}
|
||||
onTouchEnd={() => onRemove?.()}
|
||||
>
|
||||
@@ -137,40 +127,25 @@ export const FieldItem = ({
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isCheckboxField && (
|
||||
<Card
|
||||
className={cn('bg-field-card/80 h-full w-full backdrop-blur-[1px]', {
|
||||
'border-field-card-border': !disabled,
|
||||
'border-field-card-border/80': active,
|
||||
})}
|
||||
>
|
||||
<CardContent
|
||||
className={cn(
|
||||
'text-field-card-foreground flex h-full w-full flex-col items-center justify-center p-2',
|
||||
{
|
||||
'text-field-card-foreground/50': disabled,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{FRIENDLY_FIELD_TYPE[field.type]}
|
||||
|
||||
<p className="w-full truncate text-center text-xs">{field.signerEmail}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{isCheckboxField && (
|
||||
<Checkbox
|
||||
<Card
|
||||
className={cn('bg-field-card/80 h-full w-full backdrop-blur-[1px]', {
|
||||
'border-field-card-border': !disabled,
|
||||
'border-field-card-border/80': active,
|
||||
})}
|
||||
>
|
||||
<CardContent
|
||||
className={cn(
|
||||
'bg-field-card/80 h-8 w-8 border-2 backdrop-blur-[1px]',
|
||||
'shadow-[0_0_0_4px_theme(colors.gray.100/70%),0_0_0_1px_theme(colors.gray.100/70%),0_0_0_0.5px_theme(colors.primary.DEFAULT/70%)]',
|
||||
'text-field-card-foreground flex h-full w-full flex-col items-center justify-center p-2',
|
||||
{
|
||||
'border-field-card-border': !disabled,
|
||||
'border-field-card-border/80': active,
|
||||
'text-field-card-foreground/50': disabled,
|
||||
},
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{FRIENDLY_FIELD_TYPE[field.type]}
|
||||
|
||||
<p className="w-full truncate text-center text-xs">{field.signerEmail}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Rnd>,
|
||||
document.body,
|
||||
);
|
||||
|
||||
@@ -4,11 +4,9 @@ import type { Prisma } from '@prisma/client';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { useFieldPageCoords } from '@documenso/lib/client-only/hooks/use-field-page-coords';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Card, CardContent } from '../card';
|
||||
import { Checkbox } from '../checkbox';
|
||||
import { FRIENDLY_FIELD_TYPE } from './types';
|
||||
|
||||
export type ShowFieldItemProps = {
|
||||
@@ -21,7 +19,6 @@ export const ShowFieldItem = ({ field, recipients }: ShowFieldItemProps) => {
|
||||
|
||||
const signerEmail =
|
||||
recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '';
|
||||
const isCheckboxField = field.type === FieldType.CHECKBOX;
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
@@ -33,30 +30,19 @@ export const ShowFieldItem = ({ field, recipients }: ShowFieldItemProps) => {
|
||||
width: `${coords.width}px`,
|
||||
}}
|
||||
>
|
||||
{!isCheckboxField && (
|
||||
<Card className={cn('bg-background h-full w-full')}>
|
||||
<CardContent
|
||||
className={cn(
|
||||
'text-muted-foreground/50 flex h-full w-full flex-col items-center justify-center p-2',
|
||||
)}
|
||||
>
|
||||
{FRIENDLY_FIELD_TYPE[field.type]}
|
||||
|
||||
<p className="text-muted-foreground/50 w-full truncate text-center text-xs">
|
||||
{signerEmail}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{isCheckboxField && (
|
||||
<Checkbox
|
||||
<Card className={cn('bg-background h-full w-full')}>
|
||||
<CardContent
|
||||
className={cn(
|
||||
'h-8 w-8',
|
||||
'shadow-[0_0_0_4px_theme(colors.gray.100/70%),0_0_0_1px_theme(colors.gray.100/70%),0_0_0_0.5px_theme(colors.primary.DEFAULT/70%)]',
|
||||
'text-muted-foreground/50 flex h-full w-full flex-col items-center justify-center p-2',
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{FRIENDLY_FIELD_TYPE[field.type]}
|
||||
|
||||
<p className="text-muted-foreground/50 w-full truncate text-center text-xs">
|
||||
{signerEmail}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
|
||||
@@ -48,7 +48,6 @@ export const FRIENDLY_FIELD_TYPE: Record<FieldType, string> = {
|
||||
[FieldType.DATE]: 'Date',
|
||||
[FieldType.EMAIL]: 'Email',
|
||||
[FieldType.NAME]: 'Name',
|
||||
[FieldType.CHECKBOX]: 'Checkbox',
|
||||
};
|
||||
|
||||
export interface DocumentFlowStep {
|
||||
|
||||
Reference in New Issue
Block a user