import { useEffect, useMemo, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; import { Plural, Trans } from '@lingui/react/macro'; import type { Template, TemplateDirectLink } from '@prisma/client'; import { TemplateType } from '@prisma/client'; import type * as DialogPrimitive from '@radix-ui/react-dialog'; import { CheckCircle2Icon, CircleIcon } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { P, match } from 'ts-pattern'; import { z } from 'zod'; import { trpc } from '@documenso/trpc/react'; import { MAX_TEMPLATE_PUBLIC_DESCRIPTION_LENGTH, MAX_TEMPLATE_PUBLIC_TITLE_LENGTH, } from '@documenso/trpc/server/template-router/schema'; import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out'; import { Button } from '@documenso/ui/primitives/button'; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@documenso/ui/primitives/dialog'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@documenso/ui/primitives/table'; import { Textarea } from '@documenso/ui/primitives/textarea'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { useCurrentTeam } from '~/providers/team'; export type ManagePublicTemplateDialogProps = { directTemplates: (Template & { directLink: Pick; })[]; initialTemplateId?: number | null; initialStep?: ProfileTemplateStep; trigger?: React.ReactNode; isOpen?: boolean; onIsOpenChange?: (value: boolean) => unknown; } & Omit; const ZUpdatePublicTemplateFormSchema = z.object({ publicTitle: z .string() .min(1, { message: 'Title is required' }) .max(MAX_TEMPLATE_PUBLIC_TITLE_LENGTH, { message: `Title cannot be longer than ${MAX_TEMPLATE_PUBLIC_TITLE_LENGTH} characters`, }), publicDescription: z .string() .min(1, { message: 'Description is required' }) .max(MAX_TEMPLATE_PUBLIC_DESCRIPTION_LENGTH, { message: `Description cannot be longer than ${MAX_TEMPLATE_PUBLIC_DESCRIPTION_LENGTH} characters`, }), }); type TUpdatePublicTemplateFormSchema = z.infer; type ProfileTemplateStep = 'SELECT_TEMPLATE' | 'MANAGE' | 'CONFIRM_DISABLE'; export const ManagePublicTemplateDialog = ({ directTemplates, trigger, initialTemplateId = null, initialStep = 'SELECT_TEMPLATE', isOpen = false, onIsOpenChange, ...props }: ManagePublicTemplateDialogProps) => { const { _, i18n } = useLingui(); const { toast } = useToast(); const [open, onOpenChange] = useState(isOpen); const team = useCurrentTeam(); const [selectedTemplateId, setSelectedTemplateId] = useState(initialTemplateId); const [currentStep, setCurrentStep] = useState(() => { if (initialStep) { return initialStep; } return selectedTemplateId ? 'MANAGE' : 'SELECT_TEMPLATE'; }); const form = useForm({ resolver: zodResolver(ZUpdatePublicTemplateFormSchema), defaultValues: { publicTitle: '', publicDescription: '', }, }); const { mutateAsync: updateTemplateSettings, isPending: isUpdatingTemplateSettings } = trpc.template.updateTemplate.useMutation(); const setTemplateToPrivate = async (templateId: number) => { try { await updateTemplateSettings({ templateId, data: { type: TemplateType.PRIVATE, }, }); toast({ title: _(msg`Success`), description: _(msg`Template has been removed from your public profile.`), duration: 5000, }); handleOnOpenChange(false); } catch { toast({ title: _(msg`An unknown error occurred`), description: _( msg`We encountered an unknown error while attempting to remove this template from your profile. Please try again later.`, ), variant: 'destructive', }); } }; const onFormSubmit = async ({ publicTitle, publicDescription, }: TUpdatePublicTemplateFormSchema) => { if (!selectedTemplateId) { return; } try { await updateTemplateSettings({ templateId: selectedTemplateId, data: { type: TemplateType.PUBLIC, publicTitle, publicDescription, }, }); toast({ title: _(msg`Success`), description: _(msg`Template has been updated.`), duration: 5000, }); onOpenChange(false); } catch { toast({ title: _(msg`An unknown error occurred`), description: _( msg`We encountered an unknown error while attempting to update the template. Please try again later.`, ), variant: 'destructive', }); } }; const selectedTemplate = useMemo( () => directTemplates.find((template) => template.id === selectedTemplateId), [directTemplates, selectedTemplateId], ); const onManageStep = () => { if (!selectedTemplate) { return; } form.reset({ publicTitle: selectedTemplate.publicTitle, publicDescription: selectedTemplate.publicDescription, }); setCurrentStep('MANAGE'); }; const isLoading = isUpdatingTemplateSettings || form.formState.isSubmitting; useEffect(() => { const initialTemplate = directTemplates.find((template) => template.id === initialTemplateId); if (initialTemplate) { setSelectedTemplateId(initialTemplate.id); form.reset({ publicTitle: initialTemplate.publicTitle, publicDescription: initialTemplate.publicDescription, }); } else { setSelectedTemplateId(null); } const step = initialStep || (selectedTemplateId ? 'MANAGE' : 'SELECT_TEMPLATE'); setCurrentStep(step); // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialTemplateId, initialStep, open, isOpen]); const handleOnOpenChange = (value: boolean) => { if (isLoading || typeof value !== 'boolean') { return; } onOpenChange(value); onIsOpenChange?.(value); }; return (
{trigger} {match({ templateId: selectedTemplateId, currentStep }) .with({ currentStep: 'SELECT_TEMPLATE' }, () => ( {team?.name ? ( {team.name} direct signing templates ) : ( Your direct signing templates )} {team ? ( Select a template you'd like to display on your team's public profile ) : ( Select a template you'd like to display on your public profile )}
Template Created {directTemplates.length === 0 && (

No valid direct templates found

)} {directTemplates.map((row) => ( setSelectedTemplateId(row.id)} > {row.title} {i18n.date(row.createdAt)} {selectedTemplateId === row.id ? ( ) : ( )} ))}
)) .with({ templateId: P.number, currentStep: 'MANAGE' }, () => ( Configure template Manage details for this public template
( Title )} /> { const remaningLength = MAX_TEMPLATE_PUBLIC_DESCRIPTION_LENGTH - (field.value || '').length; return ( Description