Refactor the React OpenAPI admin framework to support fully customizable field rendering and UI composition. (#11)

# Summary

Refactor the React OpenAPI admin framework to support fully customizable field rendering and UI composition.

## Changes

### Admin UI Customization

* Added support for custom:

  * Dashboard component
  * Layout component
  * Login page component
* Introduced `AdminAppProps` and extended `Admin` configuration API.
* Renamed internal dashboard implementation to `DefaultDashboard`.

### Field Component Architecture

* Extracted field rendering into dedicated field components:

  * TextField
  * NumberField
  * BooleanField
  * DateField
  * EnumField
  * RelationField
  * ObjectField
  * FallbackField
  * DateRangeField
  * NumberRangeField
* Added `defaultFieldComponents` registry.
* Refactored `FormField` to resolve components dynamically from a component map instead of hardcoded field type handling.

### Resource Customization

* Added `FieldComponents` support across:

  * Admin
  * ResourceView
  * GenericForm
  * useResource
* Introduced wrapped `FormField` and `GenericForm` components generated from configured field overrides.

### Table Customization

* Added `EnhancedTableComponents`.
* Added support for custom cell renderers per field type.
* Enabled custom rendering for both desktop and mobile table layouts.

### Filter Improvements

* Exported `FilterAutocomplete`.
* Added support for custom date-range and number-range filter components.
* Added filter component extension points.
* Updated filter option label resolution to support `displayFormat`.

### Display Formatting

* Replaced `displayField` usage with `displayFormat`.
* Added template-based display rendering support through `resolveTemplate`.
* Improved relation display configuration handling.

### TypeScript Improvements

* Added TypeScript as a project dependency.
* Removed multiple `@ts-ignore` usages.
* Added strongly typed Axios wrapper methods with generic response support.
* Improved typing across hooks and component interfaces.

### OpenAPI Configuration Validation

* Added validation for enum fields without enum values.
* Added validation for relation resources missing `referenceOptions.enumOption`.
* Improved relation metadata propagation during schema parsing.

### Library Exports

* Exported:

  * Field component types
  * Override types
  * EnhancedTable
  * GenericForm
  * ResourceView
  * Field components and defaults
* Expanded public API surface for consumers extending the framework.

## Benefits

* Enables complete UI customization without modifying framework internals.
* Simplifies creation of custom field types and renderers.
* Improves type safety and developer experience.
* Provides consistent extension points for forms, tables, filters, and admin layouts.
* Makes the framework more suitable for reusable library distribution.

Reviewed-on: #11
Co-authored-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
Co-committed-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
This commit is contained in:
2026-06-07 12:35:52 +00:00
committed by aetos
parent e6ce62a166
commit 7bd946ec7a
35 changed files with 1028 additions and 618 deletions

View File

@@ -2,9 +2,9 @@ import * as React from 'react';
import { Box, Paper, CircularProgress } from '@mui/material';
import { ResourceConfig } from '../types/config';
import type { ResourceField } from '../types/config';
import { FieldComponents } from '../types/overrides';
import { useResource } from '../hooks/useResource';
import { resolveTemplate } from '../utils/options';
import GenericForm from './GenericForm';
import EnhancedTable from './EnhancedTable';
import FilterBar from './FilterBar';
import { useParams, useLocation, useNavigate } from 'react-router-dom';
@@ -12,6 +12,7 @@ import { useParams, useLocation, useNavigate } from 'react-router-dom';
interface ResourceViewProps {
config: ResourceConfig;
onNavigateToResource?: (resourceName: string, id: string) => void;
fieldComponents: FieldComponents;
}
import { GridPaginationModel } from '@mui/x-data-grid';
@@ -96,7 +97,7 @@ function applyClientFilters(
);
}
export default function ResourceView({ config, onNavigateToResource }: ResourceViewProps) {
export default function ResourceView({ config, onNavigateToResource, fieldComponents }: ResourceViewProps) {
const { id } = useParams();
const location = useLocation();
const navigate = useNavigate();
@@ -115,10 +116,10 @@ export default function ResourceView({ config, onNavigateToResource }: ResourceV
const [appliedFilters, setAppliedFilters] = React.useState<Record<string, any>>({});
const { useList, useRead, useCreate, useUpdate, useDelete } = useResource(config);
const { useList, useRead, useCreate, useUpdate, useDelete, components } = useResource(config, { fieldComponents });
const queryParams = React.useMemo(() => {
if (!isServer) return { limit: 10000 };
if (!isServer) return { limit: 10 };
return {
skip: paginationModel.page * paginationModel.pageSize,
limit: paginationModel.pageSize,
@@ -183,6 +184,7 @@ export default function ResourceView({ config, onNavigateToResource }: ResourceV
appliedValues={appliedFilters}
onApply={setAppliedFilters}
onClear={() => setAppliedFilters({})}
fieldComponents={components}
/>
)}
<EnhancedTable
@@ -200,7 +202,7 @@ export default function ResourceView({ config, onNavigateToResource }: ResourceV
</Box>
) : (
<Paper sx={{ p: 4 }}>
<GenericForm
{components && <components.GenericForm
config={config}
initialData={isCreate ? null : itemQuery.data}
onSave={handleSave}
@@ -208,7 +210,7 @@ export default function ResourceView({ config, onNavigateToResource }: ResourceV
loading={createMutation.isPending || updateMutation.isPending}
readOnly={isView}
onEditClick={() => navigate(`/admin/${config.name}/edit/${id}`)}
/>
/>}
</Paper>
)}
</Box>