import * as React from 'react'; import { Box, Button, Typography, Divider, CircularProgress, } from '@mui/material'; import { ResourceConfig } from '../types/config'; import { useUpload } from '../providers/UploadProvider'; import { useQueries } from '@tanstack/react-query'; import { useResource } from '../hooks/useResource'; import FormField from './fields/FormField'; import { ConfigContext } from '../App'; interface GenericFormProps { config: ResourceConfig; initialData?: any; onSave: (data: any) => Promise; onCancel: () => void; loading?: boolean; readOnly?: boolean; onEditClick?: () => void; } export default function GenericForm({ config, initialData = {}, onSave, onCancel, loading: saving, readOnly = false, onEditClick, }: GenericFormProps) { initialData = initialData || {}; const [formData, setFormData] = React.useState(initialData); const { uploadFile, uploading } = useUpload(); const appConfig = React.useContext(ConfigContext); // 1. Identify all unique relations in the schema (including nested ones) const getRelationFields = (fields: Record): string[] => { let relations: string[] = []; Object.values(fields).forEach(field => { if (field.relation) relations.push(field.relation); if (field.schema) relations = [...relations, ...getRelationFields(field.schema)]; }); return Array.from(new Set(relations)); }; const allRelations = React.useMemo(() => getRelationFields(config.fields), [config.fields]); // 2. Parallel fetch for all related resource lists const queries = useQueries({ queries: allRelations.map(relName => { const relatedRes = appConfig?.resources.find(r => r.name === relName); // eslint-disable-next-line react-hooks/rules-of-hooks const { getListQueryOptions } = useResource(relatedRes!); return { ...getListQueryOptions(), enabled: !!relatedRes, }; }), }); const isLoadingRelations = queries.some(q => q.isLoading); const relationDataMap = React.useMemo(() => { const map: Record = {}; allRelations.forEach((relName, index) => { map[relName] = queries[index].data || []; }); return map; }, [allRelations, queries]); const handleChange = (key: string, value: any) => { if (readOnly) return; setFormData((prev: any) => ({ ...prev, [key]: value })); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (readOnly) return; onSave(formData); }; const getTitle = () => { if (readOnly) return `View ${config.label}`; return initialData[config.primaryKey] ? `Edit ${config.label}` : `New ${config.label}`; }; if (isLoadingRelations) { return ( Loading relationships... ); } return ( {getTitle()} {Object.entries(config.fields).map(([key, field]) => ( handleChange(key, val)} disabled={readOnly || field.readOnly} uploadFile={uploadFile} uploading={uploading} baseUrl={appConfig?.baseUrl || ""} relationDataMap={relationDataMap} /> ))} {readOnly ? ( ) : ( )} ); }