diff --git a/app/src/client/admin/common/Loader/index.tsx b/app/src/client/admin/common/Loader/index.tsx index 0915c82..4221fda 100644 --- a/app/src/client/admin/common/Loader/index.tsx +++ b/app/src/client/admin/common/Loader/index.tsx @@ -1,7 +1,7 @@ const Loader = () => { return ( -
-
+
+
); }; diff --git a/app/src/client/app/ModelsPage.tsx b/app/src/client/app/ModelsPage.tsx index 1346868..5143dbd 100644 --- a/app/src/client/app/ModelsPage.tsx +++ b/app/src/client/app/ModelsPage.tsx @@ -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; - required: string[]; - title: string; - type: string; -} +const ModelsPage = () => { + const [modelsSchema, setModelsSchema] = useState(null); + const [initialModelSchema, setInitialModelSchema] = useState(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(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 (
-
-
+ {modelsSchema && ( + <> + + {initialModelSchema && } + + )}
+ {isLoading && ( +
+ +
+ )}
); diff --git a/app/src/client/app/layout/CustomLayout.tsx b/app/src/client/app/layout/CustomLayout.tsx index c7fe2d2..b5fdf4c 100644 --- a/app/src/client/app/layout/CustomLayout.tsx +++ b/app/src/client/app/layout/CustomLayout.tsx @@ -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 { @@ -10,16 +11,18 @@ interface Props { const CustomLayout: FC = ({ children }) => { const [sidebarOpen, setSidebarOpen] = useState(false); - const { data: user } = useAuth(); + const { data: user, isError, isSuccess, isLoading } = useAuth(); const scrollRef = useRef(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]); diff --git a/app/src/client/components/CustomSidebar.tsx b/app/src/client/components/CustomSidebar.tsx index f8a7d90..a37d144 100644 --- a/app/src/client/components/CustomSidebar.tsx +++ b/app/src/client/components/CustomSidebar.tsx @@ -137,7 +137,7 @@ const CustomSidebar = ({ sidebarOpen, setSidebarOpen }: SidebarProps) => { fill='' /> - ToolBox + ToolBoxes {/* */} diff --git a/app/src/client/components/DynamicFormBuilder.tsx b/app/src/client/components/DynamicFormBuilder.tsx new file mode 100644 index 0000000..b782d6a --- /dev/null +++ b/app/src/client/components/DynamicFormBuilder.tsx @@ -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 = ({ jsonSchema }) => { + const { formData, handleChange } = useForm(jsonSchema); + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + console.log('Submitted Data:', formData); + // Add submission logic here + }; + + return ( +
+ {Object.entries(jsonSchema.properties).map(([key, property]) => ( +
+ + {property.enum ? ( + handleChange(key, value)} + /> + ) : ( + handleChange(key, value)} + /> + )} +
+ ))} + +
+ ); +}; + +export default DynamicFormBuilder; diff --git a/app/src/client/components/ModelFormContainer.tsx b/app/src/client/components/ModelFormContainer.tsx new file mode 100644 index 0000000..d0d49e4 --- /dev/null +++ b/app/src/client/components/ModelFormContainer.tsx @@ -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 = ({ modelsSchema, onModelChange }) => { + return ( +
+
+ +
+ +
+
+
+ ); +}; + +export default ModelFormContainer; diff --git a/app/src/client/components/form/SelectInput.tsx b/app/src/client/components/form/SelectInput.tsx new file mode 100644 index 0000000..700228a --- /dev/null +++ b/app/src/client/components/form/SelectInput.tsx @@ -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 = ({ id, value, options, onChange }) => ( + +); diff --git a/app/src/client/components/form/TextInput.tsx b/app/src/client/components/form/TextInput.tsx new file mode 100644 index 0000000..72a91ed --- /dev/null +++ b/app/src/client/components/form/TextInput.tsx @@ -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 = ({ id, value, placeholder, onChange }) => ( + onChange(e.target.value)} + className='my-2 p-2 border rounded' + id={id} + /> +); diff --git a/app/src/client/hooks/useForm.ts b/app/src/client/hooks/useForm.ts new file mode 100644 index 0000000..037c33e --- /dev/null +++ b/app/src/client/hooks/useForm.ts @@ -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, + }; +}; diff --git a/app/src/client/interfaces/models.ts b/app/src/client/interfaces/models.ts new file mode 100644 index 0000000..54112bb --- /dev/null +++ b/app/src/client/interfaces/models.ts @@ -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; + required: string[]; + title: string; + type: string; +} + +export interface Schema { + name: string; + json_schema: JsonSchema; +} + +export interface ModelSchema { + schemas: Schema[]; +} diff --git a/app/src/client/services/modelService.ts b/app/src/client/services/modelService.ts new file mode 100644 index 0000000..d6e0841 --- /dev/null +++ b/app/src/client/services/modelService.ts @@ -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; + } +};