Skip to content

Commit

Permalink
Update form elements style
Browse files Browse the repository at this point in the history
  • Loading branch information
harishmohanraj committed Apr 17, 2024
1 parent b102c3b commit 6a56653
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 44 deletions.
25 changes: 15 additions & 10 deletions app/src/client/app/ModelsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ import Loader from '../admin/common/Loader';
import { getModels } from '../services/modelService';
import { ModelSchema, JsonSchema } from '../interfaces/models';
import ModelFormContainer from '../components/ModelFormContainer';
import NotificationBox from '../components/NotificationBox';

const ModelsPage = () => {
const [modelsSchema, setModelsSchema] = useState<ModelSchema | null>(null);
const [initialModelSchema, setInitialModelSchema] = useState<JsonSchema | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const fetchData = async () => {
setIsLoading(true);
try {
const response = await getModels();
setModelsSchema(response);
setInitialModelSchema(response.schemas[0].json_schema);
} catch (error) {
// handle error scenario
} catch (error: any) {
setError('Something went wrong. Please try again later.');
console.log(error);
}
setIsLoading(false);
Expand All @@ -43,16 +45,19 @@ const ModelsPage = () => {
<div className='flex flex-col gap-10'>
<div className='flex flex-col gap-4'>
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='flex-col flex items-start p-6 gap-3 w-full' style={{ width: '1000px', height: '600px' }}>
<div className='flex justify-end w-full'>
<div className='flex-col flex items-start p-6 gap-3 w-full' style={{ width: '1000px', minHeight: '600px' }}>
<div className='flex justify-end w-full px-6.5 py-3'>
<Button onClick={() => fetchData()} label='Add Model' />
</div>
{modelsSchema && (
<>
<ModelFormContainer modelsSchema={modelsSchema} onModelChange={handleModelChange} />
{initialModelSchema && <DynamicFormBuilder jsonSchema={initialModelSchema} />}
</>
)}
<div className='flex-col flex w-full'>
{modelsSchema && (
<>
<ModelFormContainer modelsSchema={modelsSchema} onModelChange={handleModelChange} />
{initialModelSchema && <DynamicFormBuilder jsonSchema={initialModelSchema} />}
</>
)}
{error && <NotificationBox type={'error'} onClick={() => setError(null)} message={error} />}
</div>
</div>
</div>
</div>
Expand Down
58 changes: 31 additions & 27 deletions app/src/client/components/DynamicFormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,37 @@ const DynamicFormBuilder: React.FC<DynamicFormBuilderProps> = ({ jsonSchema }) =
};

return (
<form onSubmit={handleSubmit} className='grid grid-cols-1 gap-9 sm:grid-cols-2 p-6.5'>
{Object.entries(jsonSchema.properties).map(([key, property]) => (
<div key={key}>
<label htmlFor={key}>{property.title}</label>
{property.enum ? (
<SelectInput
id={key}
value={formData[key]}
options={property.enum}
onChange={(value) => handleChange(key, value)}
/>
) : (
<TextInput
id={key}
value={formData[key]}
placeholder={property.description || ''}
onChange={(value) => handleChange(key, value)}
/>
)}
</div>
))}
<button
type='submit'
className='rounded-md px-3.5 py-2.5 text-sm bg-airt-primary text-airt-font-base hover:bg-opacity-85 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
>
Submit
</button>
<form onSubmit={handleSubmit} className='grid grid-cols-1 md:grid-cols-2 gap-9 p-6.5'>
{Object.entries(jsonSchema.properties).map(([key, property]) =>
property?.enum?.length === 1 ? null : (
<div key={key} className='w-full'>
<label htmlFor={key}>{property.title}</label>
{property.enum ? (
<SelectInput
id={key}
value={formData[key]}
options={property.enum}
onChange={(value) => handleChange(key, value)}
/>
) : (
<TextInput
id={key}
value={formData[key]}
placeholder={property.description || ''}
onChange={(value) => handleChange(key, value)}
/>
)}
</div>
)
)}
<div className='col-span-full'>
<button
type='submit'
className='rounded-md px-3.5 py-2.5 text-sm bg-airt-primary text-airt-font-base hover:bg-opacity-85 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
>
Submit
</button>
</div>
</form>
);
};
Expand Down
2 changes: 1 addition & 1 deletion app/src/client/components/ModelFormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface ModelFormContainerProps {
const ModelFormContainer: React.FC<ModelFormContainerProps> = ({ modelsSchema, onModelChange }) => {
return (
<div className='flex flex-col gap-9'>
<div className='flex flex-col gap-5.5 p-6.5'>
<div className='flex flex-col gap-5.5 px-6.5'>
<label className='mb-3 block text-black dark:text-white'>Select Model</label>
<div className='relative z-20 bg-white dark:bg-form-input'>
<select
Expand Down
4 changes: 2 additions & 2 deletions app/src/client/components/NotificationBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const NotificationBox: React.FC<NotificationBoxProps> = ({ type, message, onClic
<div className='mt-4 text-right'>
<button
onClick={onClick}
className={`py-2 px-4 rounded text-airt-font-base focus:outline-none ${
isSuccess ? 'bg-airt-secondary' : 'bg-airt-secondary'
className={`py-2 px-4 rounded text-airt-font-base focus:outline-none hover:bg-opacity-85 ${
isSuccess ? 'bg-airt-secondary' : 'bg-airt-error'
}`}
>
OK
Expand Down
2 changes: 1 addition & 1 deletion app/src/client/components/form/SelectInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface SelectInputProps {
}

export const SelectInput: React.FC<SelectInputProps> = ({ id, value, options, onChange }) => (
<select value={value} onChange={(e) => onChange(e.target.value)} className='my-2 p-2 border rounded' id={id}>
<select value={value} onChange={(e) => onChange(e.target.value)} className='my-2 p-2 border rounded w-full' id={id}>
{options.map((option) => (
<option key={option} value={option}>
{option}
Expand Down
2 changes: 1 addition & 1 deletion app/src/client/components/form/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const TextInput: React.FC<TextInputProps> = ({ id, value, placeholder, on
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
className='my-2 p-2 border rounded'
className='my-2 p-2 border rounded w-full'
id={id}
/>
);
9 changes: 7 additions & 2 deletions app/src/client/hooks/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ export const useForm = (jsonSchema: JsonSchema) => {
useEffect(() => {
const initialValues: { [key: string]: any } = {};
Object.keys(jsonSchema.properties).forEach((key) => {
// Ensure every field starts with a defined value, use an empty string as a fallback
initialValues[key] = jsonSchema.properties[key].default ?? '';
const property = jsonSchema.properties[key];
// Check for enum with exactly one value and set it, otherwise use default or fallback to an empty string
if (property.enum && property.enum.length === 1) {
initialValues[key] = property.enum[0]; // Auto-set single enum value
} else {
initialValues[key] = property.default ?? ''; // Use default or empty string if no default
}
});
setFormData(initialValues);
}, [jsonSchema]);
Expand Down
74 changes: 74 additions & 0 deletions app/src/client/tests/DynamicFormBuilder.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { render, fireEvent, screen } from '@testing-library/react';
import { test, expect, describe, vi } from 'vitest';
import { renderInContext } from 'wasp/client/test';
import DynamicFormBuilder from '../components/DynamicFormBuilder';
import { JsonSchema } from '../interfaces/models';

const jsonSchema: JsonSchema = {
properties: {
model: {
default: 'gpt-3.5-turbo',
description: "The model to use for the OpenAI API, e.g. 'gpt-3.5-turbo'",
enum: ['gpt-4', 'gpt-3.5-turbo'],
title: 'Model',
type: 'string',
},
api_key: {
description: "The API key for the OpenAI API, e.g. 'sk-1234567890abcdef1234567890abcdef'",
title: 'API Key',
type: 'string',
},
base_url: {
default: 'https://api.openai.com/v1',
description: 'The base URL of the OpenAI API',
format: 'uri',
maxLength: 2083,
minLength: 1,
title: 'Base Url',
type: 'string',
},
api_type: {
const: 'openai',
default: 'openai',
description: "The type of the API, must be 'openai'",
enum: ['openai'],
title: 'API Type',
type: 'string',
},
},
required: [''],
title: '',
type: '',
};

describe('DynamicFormBuilder', () => {
test('renders form fields correctly', () => {
render(<DynamicFormBuilder jsonSchema={jsonSchema} />);

expect(screen.getByLabelText('Model')).toBeInTheDocument();
expect(screen.getByLabelText('API Key')).toBeInTheDocument();
expect(screen.getByLabelText('Base Url')).toBeInTheDocument();
expect(screen.queryByLabelText('API Type')).toBe(null);
});

// test('handles user input and form submission', () => {
// const logSpy = vi.spyOn(console, 'log');
// render(<DynamicFormBuilder jsonSchema={jsonSchema} />);

// fireEvent.change(screen.getByLabelText('Model'), { target: { value: 'gpt-4' } });
// fireEvent.change(screen.getByLabelText('API Key'), { target: { value: 'test-api-key' } });
// fireEvent.change(screen.getByLabelText('Base Url'), { target: { value: 'https://test.com' } });
// fireEvent.click(screen.getByText('Submit'));

// // Check if console.log is called with the correct form data
// expect(logSpy).toHaveBeenCalledWith({
// model: 'gpt-4',
// api_key: 'test-api-key',
// base_url: 'https://test.com',
// api_type: 'openai',
// });

// // Restore console.log
// logSpy.mockRestore();
// });
});
1 change: 1 addition & 0 deletions app/tailwind.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
'airt-hero-gradient-start': '#086e9f',
'airt-hero-gradient-middle': '#5cbae0',
'airt-footer-copyrights': '#003a60',
'airt-error': '#EF5C00',
current: 'currentColor',
transparent: 'transparent',
// white: '#FFFFFF',
Expand Down

0 comments on commit 6a56653

Please sign in to comment.