import { useState, useTransition } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; import { Trans } from '@lingui/react/macro'; import type { ApiToken } from '@prisma/client'; import { AnimatePresence, motion } from 'framer-motion'; import { useForm } from 'react-hook-form'; import { match } from 'ts-pattern'; import { z } from 'zod'; import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { trpc } from '@documenso/trpc/react'; import type { TCreateTokenMutationSchema } from '@documenso/trpc/server/api-token-router/schema'; import { ZCreateTokenMutationSchema } from '@documenso/trpc/server/api-token-router/schema'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Card, CardContent } from '@documenso/ui/primitives/card'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@documenso/ui/primitives/select'; import { Switch } from '@documenso/ui/primitives/switch'; import { useToast } from '@documenso/ui/primitives/use-toast'; export const EXPIRATION_DATES = { ONE_WEEK: msg`7 days`, ONE_MONTH: msg`1 month`, THREE_MONTHS: msg`3 months`, SIX_MONTHS: msg`6 months`, ONE_YEAR: msg`12 months`, } as const; const ZCreateTokenFormSchema = ZCreateTokenMutationSchema.extend({ enabled: z.boolean(), }); type TCreateTokenFormSchema = z.infer; type NewlyCreatedToken = { id: number; token: string; }; export type ApiTokenFormProps = { className?: string; teamId?: number; tokens?: Pick[]; }; export const ApiTokenForm = ({ className, teamId, tokens }: ApiTokenFormProps) => { const [isTransitionPending, startTransition] = useTransition(); const [, copy] = useCopyToClipboard(); const { _ } = useLingui(); const { toast } = useToast(); const [newlyCreatedToken, setNewlyCreatedToken] = useState(); const [noExpirationDate, setNoExpirationDate] = useState(false); const { mutateAsync: createTokenMutation } = trpc.apiToken.createToken.useMutation({ onSuccess(data) { setNewlyCreatedToken(data); }, }); const form = useForm({ resolver: zodResolver(ZCreateTokenFormSchema), defaultValues: { tokenName: '', expirationDate: '', enabled: false, }, }); const copyToken = async (token: string) => { try { const copied = await copy(token); if (!copied) { throw new Error('Unable to copy the token'); } toast({ title: _(msg`Token copied to clipboard`), description: _(msg`The token was copied to your clipboard.`), }); } catch (error) { toast({ title: _(msg`Unable to copy token`), description: _(msg`We were unable to copy the token to your clipboard. Please try again.`), variant: 'destructive', }); } }; const onSubmit = async ({ tokenName, expirationDate }: TCreateTokenMutationSchema) => { try { await createTokenMutation({ teamId, tokenName, expirationDate: noExpirationDate ? null : expirationDate, }); toast({ title: _(msg`Token created`), description: _(msg`A new token was created successfully.`), duration: 5000, }); form.reset(); } catch (err) { const error = AppError.parseError(err); const errorMessage = match(error.code) .with( AppErrorCode.UNAUTHORIZED, () => msg`You do not have permission to create a token for this team`, ) .otherwise(() => msg`Something went wrong. Please try again later.`); toast({ title: _(msg`An error occurred`), description: _(errorMessage), variant: 'destructive', duration: 5000, }); } }; return (
( Token name
Please enter a meaningful name for your token. This will help you identify it later.
)} />
( Token expiration date
)} /> ( Never expire
{ setNoExpirationDate((prev) => !prev); field.onChange(val); }} />
)} />
{newlyCreatedToken && tokens && tokens.find((token) => token.id === newlyCreatedToken.id) && (

Your token was created successfully! Make sure to copy it because you won't be able to see it again!

{newlyCreatedToken.token}

)}
); };