import { SETTINGS_PLAYGROUND_FORM_SCHEMA_SELECT_OPTIONS } from '@/settings/playground/constants/SettingsPlaygroundFormSchemaSelectOptions'; import { playgroundApiKeyState } from '@/settings/playground/states/playgroundApiKeyState'; import { PlaygroundSchemas } from '@/settings/playground/types/PlaygroundSchemas'; import { PlaygroundTypes } from '@/settings/playground/types/PlaygroundTypes'; import { SettingsPath } from '@/types/SettingsPath'; import { Select } from '@/ui/input/components/Select'; import { TextInput } from '@/ui/input/components/TextInput'; import styled from '@emotion/styled'; import { zodResolver } from '@hookform/resolvers/zod'; import { useLingui } from '@lingui/react/macro'; import { Controller, useForm } from 'react-hook-form'; import { useRecoilState } from 'recoil'; import { IconApi, IconBrandGraphql } from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; import { z } from 'zod'; import { REACT_APP_SERVER_BASE_URL } from '~/config'; import { useNavigateSettings } from '~/hooks/useNavigateSettings'; const playgroundSetupFormSchema = z.object({ apiKeyForPlayground: z.string(), schema: z.nativeEnum(PlaygroundSchemas), playgroundType: z.nativeEnum(PlaygroundTypes), }); type PlaygroundSetupFormValues = z.infer; const StyledForm = styled.form` display: grid; grid-template-columns: 1.5fr 1fr 1fr 0.5fr; align-items: end; gap: ${({ theme }) => theme.spacing(2)}; margin-bottom: ${({ theme }) => theme.spacing(2)}; width: 100%; `; export const PlaygroundSetupForm = () => { const { t } = useLingui(); const navigateSettings = useNavigateSettings(); const [playgroundApiKey, setPlaygroundApiKey] = useRecoilState( playgroundApiKeyState, ); const { control, handleSubmit, formState: { isSubmitting }, setError, } = useForm({ mode: 'onTouched', resolver: zodResolver(playgroundSetupFormSchema), defaultValues: { schema: PlaygroundSchemas.CORE, playgroundType: PlaygroundTypes.REST, apiKeyForPlayground: playgroundApiKey || '', }, }); const validateApiKey = async (values: PlaygroundSetupFormValues) => { try { // Validate by fetching the schema (but not storing it) const response = await fetch( `${REACT_APP_SERVER_BASE_URL}/rest/open-api/${values.schema}`, { headers: { Authorization: `Bearer ${values.apiKeyForPlayground}` }, }, ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const openAPIReference = await response.json(); if (!openAPIReference.tags) { throw new Error('Invalid API Key'); } return true; } catch (error) { throw new Error(t`Invalid API key`); } }; const onSubmit = async (values: PlaygroundSetupFormValues) => { try { await validateApiKey(values); setPlaygroundApiKey(values.apiKeyForPlayground); const path = values.playgroundType === PlaygroundTypes.GRAPHQL ? SettingsPath.GraphQLPlayground : SettingsPath.RestPlayground; navigateSettings(path, { schema: values.schema.toLowerCase(), }); } catch (error) { setError('apiKeyForPlayground', { type: 'manual', message: error instanceof Error ? error.message : t`An unexpected error occurred`, }); } }; return ( ( { onChange(newValue); setPlaygroundApiKey(newValue); }} error={error?.message} required /> )} /> ( )} />