Merge branch 'main' of https://github.com/documenso/documenso into feat/redirect-templates

This commit is contained in:
Adithya Krishna
2024-04-18 17:49:08 +05:30
31 changed files with 395 additions and 191 deletions

View File

@ -55,6 +55,8 @@ type AvatarWithTextProps = {
primaryText: React.ReactNode;
secondaryText?: React.ReactNode;
rightSideComponent?: React.ReactNode;
// Optional class to hide/show the text beside avatar
textSectionClassName?: string;
};
const AvatarWithText = ({
@ -64,6 +66,7 @@ const AvatarWithText = ({
primaryText,
secondaryText,
rightSideComponent,
textSectionClassName,
}: AvatarWithTextProps) => (
<div className={cn('flex w-full max-w-xs items-center gap-2', className)}>
<Avatar
@ -72,7 +75,7 @@ const AvatarWithText = ({
<AvatarFallback className="text-xs text-gray-400">{avatarFallback}</AvatarFallback>
</Avatar>
<div className="flex flex-col text-left text-sm font-normal">
<div className={cn('flex flex-col text-left text-sm font-normal', textSectionClassName)}>
<span className="text-foreground truncate">{primaryText}</span>
<span className="text-muted-foreground truncate text-xs">{secondaryText}</span>
</div>

View File

@ -32,7 +32,11 @@ type CommandDialogProps = DialogProps & {
const CommandDialog = ({ children, commandProps, ...props }: CommandDialogProps) => {
return (
<Dialog {...props}>
<DialogContent className="overflow-hidden p-0 shadow-2xl">
<DialogContent
className="w-11/12 items-center overflow-hidden rounded-lg p-0 shadow-2xl lg:mt-0"
position="center"
overlayClassName="bg-background/60"
>
<Command
{...commandProps}
className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-0 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-4 [&_[cmdk-item]_svg]:w-4"

View File

@ -54,28 +54,35 @@ const DialogContent = React.forwardRef<
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
position?: 'start' | 'end' | 'center';
hideClose?: boolean;
/* Below prop is to add additional classes to the overlay */
overlayClassName?: string;
}
>(({ className, children, position = 'start', hideClose = false, ...props }, ref) => (
<DialogPortal position={position}>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
'bg-background animate-in data-[state=open]:fade-in-90 sm:zoom-in-90 data-[state=open]:slide-in-from-bottom-10 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg',
className,
)}
{...props}
>
{children}
{!hideClose && (
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
));
>(
(
{ className, children, overlayClassName, position = 'start', hideClose = false, ...props },
ref,
) => (
<DialogPortal position={position}>
<DialogOverlay className={cn(overlayClassName)} />
<DialogPrimitive.Content
ref={ref}
className={cn(
'bg-background animate-in data-[state=open]:fade-in-90 sm:zoom-in-90 data-[state=open]:slide-in-from-bottom-10 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg',
className,
)}
{...props}
>
{children}
{!hideClose && (
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
),
);
DialogContent.displayName = DialogPrimitive.Content.displayName;

View File

@ -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,8 @@ export const AddSignersFormPartial = ({
}: AddSignersFormProps) => {
const { toast } = useToast();
const { remaining } = useLimits();
const { data: session } = useSession();
const user = session?.user;
const initialId = useId();
@ -135,6 +138,16 @@ export const AddSignersFormPartial = ({
);
};
const onAddSelfSigner = () => {
appendSigner({
formId: nanoid(12),
name: user?.name ?? '',
email: user?.email ?? '',
role: RecipientRole.SIGNER,
actionAuth: undefined,
});
};
const onAddSigner = () => {
appendSigner({
formId: nanoid(12),
@ -209,8 +222,12 @@ export const AddSignersFormPartial = ({
<Input
type="email"
placeholder="Email"
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
{...field}
disabled={
isSubmitting ||
hasBeenSentToRecipientId(signer.nativeId) ||
signers[index].email === user?.email
}
onKeyDown={onKeyDown}
/>
</FormControl>
@ -237,8 +254,12 @@ export const AddSignersFormPartial = ({
<FormControl>
<Input
placeholder="Name"
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
{...field}
disabled={
isSubmitting ||
hasBeenSentToRecipientId(signer.nativeId) ||
signers[index].email === user?.email
}
onKeyDown={onKeyDown}
/>
</FormControl>
@ -340,29 +361,83 @@ export const AddSignersFormPartial = ({
<SelectContent align="end">
<SelectItem value={RecipientRole.SIGNER}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.SIGNER]}</span>
Signer
</div>
</SelectItem>
<SelectItem value={RecipientRole.CC}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.CC]}</span>
Receives copy
<div className="flex w-[150px] items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.SIGNER]}</span>
Needs to sign
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is required to sign the document for it to be
completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
<SelectItem value={RecipientRole.APPROVER}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.APPROVER]}</span>
Approver
<div className="flex w-[150px] items-center">
<span className="mr-2">
{ROLE_ICONS[RecipientRole.APPROVER]}
</span>
Needs to approve
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is required to approve the document for it to
be completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
<SelectItem value={RecipientRole.VIEWER}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.VIEWER]}</span>
Viewer
<div className="flex w-[150px] items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.VIEWER]}</span>
Needs to view
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is required to view the document for it to be
completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
<SelectItem value={RecipientRole.CC}>
<div className="flex items-center">
<div className="flex w-[150px] items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.CC]}</span>
Receives copy
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is not required to take any action and
receives a copy of the document after it is completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
</SelectContent>
@ -403,32 +478,46 @@ 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>
{!alwaysShowAdvancedSettings && isDocumentEnterprise && (
<div className="flex flex-row items-center">
<Checkbox
id="showAdvancedRecipientSettings"
className="h-5 w-5"
checkClassName="dark:text-white text-primary"
checked={showAdvancedSettings}
onCheckedChange={(value) => setShowAdvancedSettings(Boolean(value))}
/>
<label
className="text-muted-foreground ml-2 text-sm"
htmlFor="showAdvancedRecipientSettings"
>
Show advanced settings
</label>
</div>
)}
<Button
type="button"
variant="secondary"
className="dark:bg-muted dark:hover:bg-muted/80 bg-black/5 hover:bg-black/10"
disabled={
isSubmitting ||
form.getValues('signers').some((signer) => signer.email === user?.email)
}
onClick={() => onAddSelfSigner()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
Add myself
</Button>
</div>
{!alwaysShowAdvancedSettings && isDocumentEnterprise && (
<div className="mt-4 flex flex-row items-center">
<Checkbox
id="showAdvancedRecipientSettings"
className="h-5 w-5"
checkClassName="dark:text-white text-primary"
checked={showAdvancedSettings}
onCheckedChange={(value) => setShowAdvancedSettings(Boolean(value))}
/>
<label
className="text-muted-foreground ml-2 text-sm"
htmlFor="showAdvancedRecipientSettings"
>
Show advanced settings
</label>
</div>
)}
</Form>
</AnimateGenericFadeInOut>
</DocumentFlowFormContainerContent>

View File

@ -4,7 +4,8 @@ import React, { useId, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { AnimatePresence, motion } from 'framer-motion';
import { Plus, Trash } from 'lucide-react';
import { InfoIcon, Plus, Trash } from 'lucide-react';
import { useSession } from 'next-auth/react';
import { Controller, useFieldArray, useForm } from 'react-hook-form';
import { nanoid } from '@documenso/lib/universal/id';
@ -24,6 +25,7 @@ import type { DocumentFlowStep } from '../document-flow/types';
import { ROLE_ICONS } from '../recipient-role-icons';
import { Select, SelectContent, SelectItem, SelectTrigger } from '../select';
import { useStep } from '../stepper';
import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';
import type { TAddTemplatePlacholderRecipientsFormSchema } from './add-template-placeholder-recipients.types';
import { ZAddTemplatePlacholderRecipientsFormSchema } from './add-template-placeholder-recipients.types';
@ -41,6 +43,8 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
onSubmit,
}: AddTemplatePlaceholderRecipientsFormProps) => {
const initialId = useId();
const { data: session } = useSession();
const user = session?.user;
const [placeholderRecipientCount, setPlaceholderRecipientCount] = useState(() =>
recipients.length > 1 ? recipients.length + 1 : 2,
);
@ -50,6 +54,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
const {
control,
handleSubmit,
getValues,
formState: { errors, isSubmitting },
} = useForm<TAddTemplatePlacholderRecipientsFormSchema>({
resolver: zodResolver(ZAddTemplatePlacholderRecipientsFormSchema),
@ -86,6 +91,15 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
name: 'signers',
});
const onAddPlaceholderSelfRecipient = () => {
appendSigner({
formId: nanoid(12),
name: user?.name ?? '',
email: user?.email ?? '',
role: RecipientRole.SIGNER,
});
};
const onAddPlaceholderRecipient = () => {
appendSigner({
formId: nanoid(12),
@ -155,29 +169,81 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
<SelectContent className="" align="end">
<SelectItem value={RecipientRole.SIGNER}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.SIGNER]}</span>
Signer
</div>
</SelectItem>
<SelectItem value={RecipientRole.CC}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.CC]}</span>
Receives copy
<div className="flex w-[150px] items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.SIGNER]}</span>
Needs to sign
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is required to sign the document for it to be
completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
<SelectItem value={RecipientRole.APPROVER}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.APPROVER]}</span>
Approver
<div className="flex w-[150px] items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.APPROVER]}</span>
Needs to approve
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is required to approve the document for it to be
completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
<SelectItem value={RecipientRole.VIEWER}>
<div className="flex items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.VIEWER]}</span>
Viewer
<div className="flex w-[150px] items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.VIEWER]}</span>
Needs to view
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is required to view the document for it to be
completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
<SelectItem value={RecipientRole.CC}>
<div className="flex items-center">
<div className="flex w-[150px] items-center">
<span className="mr-2">{ROLE_ICONS[RecipientRole.CC]}</span>
Receives copy
</div>
<Tooltip>
<TooltipTrigger>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
<p>
The recipient is not required to take any action and receives a
copy of the document after it is completed.
</p>
</TooltipContent>
</Tooltip>
</div>
</SelectItem>
</SelectContent>
@ -212,11 +278,27 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
error={'signers__root' in errors && errors['signers__root']}
/>
<div className="mt-4">
<Button type="button" disabled={isSubmitting} onClick={() => onAddPlaceholderRecipient()}>
<div className="mt-4 flex flex-row items-center space-x-4">
<Button
type="button"
className="flex-1"
disabled={isSubmitting}
onClick={() => onAddPlaceholderRecipient()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
Add Placeholder Recipient
</Button>
<Button
type="button"
className="dark:bg-muted dark:hover:bg-muted/80 bg-black/5 hover:bg-black/10"
disabled={
isSubmitting || getValues('signers').some((signer) => signer.email === user?.email)
}
onClick={() => onAddPlaceholderSelfRecipient()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
Add Myself
</Button>
</div>
</DocumentFlowFormContainerContent>