Skip to content

Commit

Permalink
Fetch model schema from backend
Browse files Browse the repository at this point in the history
  • Loading branch information
harishmohanraj committed Apr 17, 2024
1 parent 84966d3 commit c1e3548
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 47 deletions.
4 changes: 2 additions & 2 deletions app/src/client/admin/common/Loader/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const Loader = () => {
return (
<div className="flex h-screen items-center justify-center bg-white">
<div className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent"></div>
<div className='flex h-screen items-center justify-center'>
<div className='h-16 w-16 animate-spin rounded-full border-4 border-solid border-airt-primary border-t-transparent'></div>
</div>
);
};
Expand Down
84 changes: 46 additions & 38 deletions app/src/client/app/ModelsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,66 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import CustomLayout from './layout/CustomLayout';
import CustomBreadcrumb from '../components/CustomBreadcrumb';
import Button from '../components/Button';
import { getModels } from 'wasp/client/operations';
import DynamicFormBuilder from '../components/DynamicFormBuilder';
import Loader from '../admin/common/Loader';
import { getModels } from '../services/modelService';
import { ModelSchema, JsonSchema } from '../interfaces/models';
import ModelFormContainer from '../components/ModelFormContainer';

interface JsonSchemaProperty {
default?: string;
description: string;
enum?: string[];
title: string;
type: string;
const?: string;
format?: string;
maxLength?: number;
minLength?: number;
}

interface JsonSchema {
properties: Record<string, JsonSchemaProperty>;
required: string[];
title: string;
type: string;
}
const ModelsPage = () => {
const [modelsSchema, setModelsSchema] = useState<ModelSchema | null>(null);
const [initialModelSchema, setInitialModelSchema] = useState<JsonSchema | null>(null);
const [isLoading, setIsLoading] = useState(false);

interface Schema {
name: string;
json_schema: JsonSchema;
}
const fetchData = async () => {
setIsLoading(true);
try {
const response = await getModels();
setModelsSchema(response);
setInitialModelSchema(response.schemas[0].json_schema);
} catch (error) {
// handle error scenario
console.log(error);
}
setIsLoading(false);
};

interface ModelSchema {
schemas: Schema[];
}
// useEffect(() => {
// fetchData();
// }, []);

const ModelsPage = () => {
const [modelsSchema, setModelsSchema] = useState<ModelSchema | null>(null);
const logout = async () => {
const response = await getModels();
setModelsSchema(response);
const handleModelChange = (newModel: string) => {
const foundSchema = modelsSchema?.schemas.find((schema) => schema.name === newModel);
if (foundSchema) {
setInitialModelSchema(foundSchema.json_schema);
}
};

return (
<CustomLayout>
<CustomBreadcrumb pageName='Models' />
<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 justify-between p-6 gap-3 w-full'
style={{ width: '1000px', height: '600px' }}
>
<Button onClick={logout} label='Add Model' />
<p>{modelsSchema?.schemas.map((schema: any) => schema.name)}</p>
<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'>
<Button onClick={() => fetchData()} label='Add Model' />
</div>
{modelsSchema && (
<>
<ModelFormContainer modelsSchema={modelsSchema} onModelChange={handleModelChange} />
{initialModelSchema && <DynamicFormBuilder jsonSchema={initialModelSchema} />}
</>
)}
</div>
</div>
</div>
{isLoading && (
<div className='absolute inset-0 flex items-center justify-center bg-white bg-opacity-50'>
<Loader />
</div>
)}
</div>
</CustomLayout>
);
Expand Down
15 changes: 9 additions & 6 deletions app/src/client/app/layout/CustomLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useAuth } from 'wasp/client/auth';
import { useState, ReactNode, FC, useRef, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import CustomSidebar from '../../components/CustomSidebar';
import LoadingComponent from '../../components/LoadingComponent';
import { cn } from '../../../shared/utils';

interface Props {
Expand All @@ -10,16 +11,18 @@ interface Props {

const CustomLayout: FC<Props> = ({ children }) => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const { data: user } = useAuth();
const { data: user, isError, isSuccess, isLoading } = useAuth();
const scrollRef = useRef<HTMLDivElement>(null);
const history = useHistory();

useEffect(() => {
if (!user) {
history.push('/login');
} else {
if (!user.hasPaid && user.isSignUpComplete) {
history.push('/pricing');
if (isSuccess) {
if (!user) {
history.push('/login');
} else {
if (!user.hasPaid && user.isSignUpComplete) {
history.push('/pricing');
}
}
}
}, [user, history]);
Expand Down
2 changes: 1 addition & 1 deletion app/src/client/components/CustomSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const CustomSidebar = ({ sidebarOpen, setSidebarOpen }: SidebarProps) => {
fill=''
/>
</svg>
ToolBox
ToolBoxes
</NavLink>

{/* <!-- Menu Item Dashboard --> */}
Expand Down
52 changes: 52 additions & 0 deletions app/src/client/components/DynamicFormBuilder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import { useForm } from '../hooks/useForm';
import { JsonSchema } from '../interfaces/models';
import { TextInput } from './form/TextInput';
import { SelectInput } from './form/SelectInput';

interface DynamicFormBuilderProps {
jsonSchema: JsonSchema;
}

const DynamicFormBuilder: React.FC<DynamicFormBuilderProps> = ({ jsonSchema }) => {
const { formData, handleChange } = useForm(jsonSchema);

const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
console.log('Submitted Data:', formData);
// Add submission logic here
};

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>
);
};

export default DynamicFormBuilder;
32 changes: 32 additions & 0 deletions app/src/client/components/ModelFormContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// components/ModelFormContainer.tsx
import React from 'react';
import { ModelSchema } from '../interfaces/models';

interface ModelFormContainerProps {
modelsSchema: ModelSchema;
onModelChange: (selectedModel: string) => void;
}

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'>
<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
className='w-full rounded border bg-transparent py-3 px-12 outline-none focus:border-primary'
onChange={(e) => onModelChange(e.target.value)}
>
{modelsSchema.schemas.map((schema) => (
<option key={schema.name} value={schema.name}>
{schema.name}
</option>
))}
</select>
</div>
</div>
</div>
);
};

export default ModelFormContainer;
18 changes: 18 additions & 0 deletions app/src/client/components/form/SelectInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';

interface SelectInputProps {
id: string;
value: string;
options: string[];
onChange: (value: string) => void;
}

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}>
{options.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
);
19 changes: 19 additions & 0 deletions app/src/client/components/form/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';

interface TextInputProps {
id: string;
value: string;
placeholder: string;
onChange: (value: string) => void;
}

export const TextInput: React.FC<TextInputProps> = ({ id, value, placeholder, onChange }) => (
<input
type='text'
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
className='my-2 p-2 border rounded'
id={id}
/>
);
28 changes: 28 additions & 0 deletions app/src/client/hooks/useForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// hooks/useForm.ts
import { useState, useEffect } from 'react';
import { JsonSchema } from '../interfaces/models';

export const useForm = (jsonSchema: JsonSchema) => {
const [formData, setFormData] = useState<{ [key: string]: any }>({});

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 ?? '';
});
setFormData(initialValues);
}, [jsonSchema]);

const handleChange = (key: string, value: any) => {
setFormData((prev) => ({
...prev,
[key]: value,
}));
};

return {
formData,
handleChange,
};
};
28 changes: 28 additions & 0 deletions app/src/client/interfaces/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// interfaces/models.ts
export interface JsonSchemaProperty {
default?: string;
description: string;
enum?: string[];
title: string;
type: string;
const?: string;
format?: string;
maxLength?: number;
minLength?: number;
}

export interface JsonSchema {
properties: Record<string, JsonSchemaProperty>;
required: string[];
title: string;
type: string;
}

export interface Schema {
name: string;
json_schema: JsonSchema;
}

export interface ModelSchema {
schemas: Schema[];
}
12 changes: 12 additions & 0 deletions app/src/client/services/modelService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// services/modelService.ts
import { getModels as apiGetModels } from 'wasp/client/operations';

export const getModels = async () => {
try {
const response = await apiGetModels();
return response;
} catch (error) {
console.error('Failed to fetch models:', error);
throw error;
}
};

0 comments on commit c1e3548

Please sign in to comment.