This is a minor rework of PR #10738. I noticed an inconsistency with how Select options are passed as props. Many files use constants stored in external files to pass options props to Select objects. This allows for code reusability. Some files are not passing options in this format. I modified more files so that they use this method of passing options props. I made changes to: - WorkerQueueMetricsSection.tsx - SettingsDataModelFieldBooleanForm.tsx - SettingsDataModelFieldTextForm.tsx - SettingsDataModelFieldNumberForm.tsx - PlaygroundSetupForm.tsx - ViewPickerContentCreateMode.tsx I also noticed that some of these files were incorrectly using useLingui(), so I fixed the import and usage where needed. --------- Co-authored-by: Beau Smith <bsmith26@iastate.edu> Co-authored-by: Charles Bochet <charles@twenty.com>
180 lines
5.2 KiB
TypeScript
180 lines
5.2 KiB
TypeScript
import { PLAYGROUND_SETUP_SELECT_OPTIONS } from '@/settings/playground/constants/PlaygroundSetupSelectOptions';
|
|
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<typeof playgroundSetupFormSchema>;
|
|
|
|
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<PlaygroundSetupFormValues>({
|
|
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}/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 (
|
|
<StyledForm onSubmit={handleSubmit(onSubmit)}>
|
|
<Controller
|
|
name="apiKeyForPlayground"
|
|
control={control}
|
|
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
|
<TextInput
|
|
label={t`API Key`}
|
|
placeholder="Enter your API key"
|
|
value={value}
|
|
onChange={(newValue) => {
|
|
onChange(newValue);
|
|
setPlaygroundApiKey(newValue);
|
|
}}
|
|
error={error?.message}
|
|
required
|
|
/>
|
|
)}
|
|
/>
|
|
<Controller
|
|
name="schema"
|
|
control={control}
|
|
defaultValue={PlaygroundSchemas.CORE}
|
|
render={({ field: { onChange, value } }) => (
|
|
<Select
|
|
dropdownId="schema"
|
|
label={t`Schema`}
|
|
options={PLAYGROUND_SETUP_SELECT_OPTIONS.map((option) => ({
|
|
...option,
|
|
label: t(option.label),
|
|
}))}
|
|
value={value}
|
|
onChange={onChange}
|
|
/>
|
|
)}
|
|
/>
|
|
<Controller
|
|
name="playgroundType"
|
|
control={control}
|
|
defaultValue={PlaygroundTypes.REST}
|
|
render={({ field: { onChange, value } }) => (
|
|
<Select
|
|
dropdownId="apiPlaygroundType"
|
|
label={t`API`}
|
|
options={[
|
|
{
|
|
value: PlaygroundTypes.REST,
|
|
label: t`REST`,
|
|
Icon: IconApi,
|
|
},
|
|
{
|
|
value: PlaygroundTypes.GRAPHQL,
|
|
label: t`GraphQL`,
|
|
Icon: IconBrandGraphql,
|
|
},
|
|
]}
|
|
value={value}
|
|
onChange={onChange}
|
|
/>
|
|
)}
|
|
/>
|
|
<Button
|
|
title={t`Launch`}
|
|
variant="primary"
|
|
accent="blue"
|
|
type="submit"
|
|
disabled={isSubmitting}
|
|
/>
|
|
</StyledForm>
|
|
);
|
|
};
|