import { 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 { TeamProfile } from '@prisma/client'; import { motion } from 'framer-motion'; import { AnimatePresence } from 'framer-motion'; import { CheckSquareIcon, CopyIcon } from 'lucide-react'; import { useForm } from 'react-hook-form'; import type { z } from 'zod'; import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard'; import { useSession } from '@documenso/lib/client-only/providers/session'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { isPersonalLayout } from '@documenso/lib/utils/organisations'; import { formatUserProfilePath } from '@documenso/lib/utils/public-profiles'; import { MAX_PROFILE_BIO_LENGTH, ZUpdateTeamRequestSchema, } from '@documenso/trpc/server/team-router/update-team.types'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { Textarea } from '@documenso/ui/primitives/textarea'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { useCurrentTeam } from '~/providers/team'; export const ZPublicProfileFormSchema = ZUpdateTeamRequestSchema.shape.data.pick({ profileBio: true, url: true, }); export type TPublicProfileFormSchema = z.infer; export type PublicProfileFormProps = { className?: string; onProfileUpdate: (data: TPublicProfileFormSchema) => Promise; profile: TeamProfile; }; export const PublicProfileForm = ({ className, profile, onProfileUpdate, }: PublicProfileFormProps) => { const { _ } = useLingui(); const { toast } = useToast(); const [, copy] = useCopyToClipboard(); const [copiedTimeout, setCopiedTimeout] = useState(null); const { organisations } = useSession(); const isPersonalLayoutMode = isPersonalLayout(organisations); const team = useCurrentTeam(); const form = useForm({ values: { url: team.url, profileBio: profile?.bio ?? '', }, resolver: zodResolver(ZPublicProfileFormSchema), }); const isSubmitting = form.formState.isSubmitting; const onFormSubmit = async (data: TPublicProfileFormSchema) => { try { await onProfileUpdate(data); toast({ title: _(msg`Success`), description: _(msg`Your public profile has been updated.`), duration: 5000, }); form.reset({ url: data.url, profileBio: data.profileBio, }); } catch (err) { const error = AppError.parseError(err); switch (error.code) { case AppErrorCode.ALREADY_EXISTS: case 'PREMIUM_PROFILE_URL': case 'PROFILE_URL_TAKEN': form.setError('url', { type: 'manual', message: error.message, }); break; default: toast({ title: _(msg`An unknown error occurred`), description: _( msg`We encountered an unknown error while attempting to update your public profile. Please try again later.`, ), variant: 'destructive', }); } } }; const onCopy = async () => { await copy(formatUserProfilePath(form.getValues('url') ?? '')).then(() => { toast({ title: _(msg`Copied to clipboard`), description: _(msg`The profile link has been copied to your clipboard`), }); }); if (copiedTimeout) { clearTimeout(copiedTimeout); } setCopiedTimeout( setTimeout(() => { setCopiedTimeout(null); }, 2000), ); }; return (
( Public profile URL {!isPersonalLayoutMode && (

You can update the profile URL by updating the team URL in the general settings page.

)}
{!form.formState.errors.url && (
{field.value ? (
) : (

A unique URL to access your profile

)}
)}
)} /> { const remaningLength = MAX_PROFILE_BIO_LENGTH - (field.value || '').length; return ( Bio