import { zodResolver } from '@hookform/resolvers/zod' import dayjs from 'dayjs' import { ExternalLink } from 'lucide-react' import { useState } from 'react' import { useForm, type SubmitHandler } from 'react-hook-form' import { toast } from 'sonner' import { Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogSection, DialogSectionSeparator, DialogTitle, Form, FormControl, FormField, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, WarningIcon, } from 'ui' import { Admonition } from 'ui-patterns' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { z } from 'zod' import { CUSTOM_EXPIRY_VALUE, EXPIRES_AT_OPTIONS, NON_EXPIRING_TOKEN_VALUE, } from '../AccessToken.constants' import { getExpirationDate } from '../AccessToken.utils' import { DatePicker } from '@/components/ui/DatePicker' import { useAccessTokenCreateMutation, type NewAccessToken, } from '@/data/access-tokens/access-tokens-create-mutation' import { useTrack } from '@/lib/telemetry/track' const formId = 'new-access-token-form' const TokenSchema = z.object({ tokenName: z.string().min(1, 'Please enter a name for the token'), expiresAt: z.preprocess( (val) => (val === NON_EXPIRING_TOKEN_VALUE ? undefined : val), z.string().optional() ), }) export interface NewAccessTokenDialogProps { open: boolean tokenScope: 'V0' | undefined onOpenChange: (open: boolean) => void onCreateToken: (token: NewAccessToken) => void } export const NewTokenDialog = ({ open, tokenScope, onOpenChange, onCreateToken, }: NewAccessTokenDialogProps) => { const [customExpiryDate, setCustomExpiryDate] = useState<{ date: string } | undefined>(undefined) const [isCustomExpiry, setIsCustomExpiry] = useState(false) const form = useForm>({ resolver: zodResolver(TokenSchema as any), defaultValues: { tokenName: '', expiresAt: EXPIRES_AT_OPTIONS['month'].value }, mode: 'onChange', }) const track = useTrack() const { mutate: createAccessToken, isPending } = useAccessTokenCreateMutation() const onSubmit: SubmitHandler> = async (values) => { let expiresAt: string | undefined if (isCustomExpiry && customExpiryDate) { expiresAt = customExpiryDate.date } else { expiresAt = getExpirationDate(values.expiresAt || '') } createAccessToken( { name: values.tokenName, scope: tokenScope, expires_at: expiresAt }, { onSuccess: (data) => { track('access_token_created', { tokenType: 'classic', expiryPreset: values.expiresAt || 'never', }) toast.success('Access token created successfully') onCreateToken(data) handleClose() }, } ) } const handleClose = () => { form.reset({ tokenName: '' }) setCustomExpiryDate(undefined) setIsCustomExpiry(false) onOpenChange(false) } const handleExpiryChange = (value: string) => { if (value === CUSTOM_EXPIRY_VALUE) { setIsCustomExpiry(true) // Set a default custom date (today at 23:59:59) const defaultCustomDate = { date: dayjs().endOf('day').toISOString(), } setCustomExpiryDate(defaultCustomDate) form.setValue('expiresAt', value) } else { setIsCustomExpiry(false) setCustomExpiryDate(undefined) form.setValue('expiresAt', value) } } const handleCustomDateChange = (value: { date: string }) => { setCustomExpiryDate(value) } return ( { if (!open) { form.reset() setCustomExpiryDate(undefined) setIsCustomExpiry(false) } onOpenChange(open) }} > {tokenScope === 'V0' ? 'Generate token for experimental API' : 'Generate New Token'} {tokenScope === 'V0' ? (

These include deleting organizations and projects which cannot be undone. As such, be very careful when using this API.

} /> ) : ( )}
( )} /> (
{isCustomExpiry && ( { if (date.to) handleCustomDateChange({ date: date.to }) }} /> )}
{field.value === NON_EXPIRING_TOKEN_VALUE && (
Make sure to keep your non-expiring token safe and secure.
)}
)} />
) }