Skip to content

Commit

Permalink
Add filter guessers
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Nov 7, 2024
1 parent 9a15381 commit eaf7117
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 29 deletions.
87 changes: 65 additions & 22 deletions packages/ra-supabase-ui-materialui/src/guessers/ListGuesser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ListView,
InferredElement,
listFieldTypes,
editFieldTypes,
} from 'react-admin';
import type { ListProps, ListViewProps } from 'react-admin';
import { capitalize, singularize } from 'inflection';
Expand Down Expand Up @@ -56,6 +57,9 @@ export const ListGuesserView = (
const { data: schema, error, isPending } = useAPISchema();
const resource = useResourceContext();
const [child, setChild] = React.useState<ReactNode>(null);
const [filters, setFilters] = React.useState<
React.ReactElement[] | undefined
>(undefined);
if (!resource) {
throw new Error('ListGuesser must be used withing a ResourceContext');
}
Expand All @@ -67,7 +71,6 @@ export const ListGuesserView = (
return;
}
const resourceDefinition = schema.definitions?.[resource];
const requiredFields = resourceDefinition?.required || [];
if (!resourceDefinition || !resourceDefinition.properties) {
throw new Error(
`The resource ${resource} is not defined in the API schema`
Expand All @@ -76,7 +79,7 @@ export const ListGuesserView = (
if (!resourceDefinition || !resourceDefinition.properties) {
return;
}
const inferredInputs = Object.keys(resourceDefinition.properties).map(
const inferredFields = Object.keys(resourceDefinition.properties).map(
(source: string) =>
inferElementFromType({
name: source,
Expand All @@ -90,40 +93,76 @@ export const ListGuesserView = (
'string'
? resourceDefinition.properties![source].type
: 'string') as string,
requiredFields,
})
);
const inferredForm = new InferredElement(
const inferredTable = new InferredElement(
listFieldTypes.table,
null,
inferredInputs
inferredFields
);
setChild(inferredForm.getElement());
setChild(inferredTable.getElement());

const rowFilters =
schema!
.paths![`/${resource}`].get!.parameters?.filter(obj =>
obj['$ref'].includes('rowFilter')
)
.map(obj => obj['$ref'].split('.').pop()) ?? [];
const inferredInputsForFilters = rowFilters.map(source => {
const field = resourceDefinition.properties![source];
return inferElementFromType({
name: source,
types: editFieldTypes,
description: field.description,
format: field.format,
type: field.type as string,
});
});
if (inferredInputsForFilters.length > 0) {
const filterElements = inferredInputsForFilters.map(inferredInput =>
inferredInput.getElement()
);
setFilters(filterElements.filter(el => el != null));
}

if (!enableLog) return;

const representation = inferredForm.getRepresentation();
const tableRepresentation = inferredTable.getRepresentation();

const components = ['List']
.concat(
Array.from(
new Set(
Array.from(representation.matchAll(/<([^/\s>]+)/g))
.map(match => match[1])
.filter(component => component !== 'span')
)
)
)
.sort();
const filterRepresentation =
inferredInputsForFilters.length > 0
? `const filters = [
${inferredInputsForFilters
.map(inferredInput => ' ' + inferredInput.getRepresentation())
.join(',\n')}
];
`
: '';

const fieldComponents = Array.from(
tableRepresentation.matchAll(/<([^/\s>]+)/g)
)
.map(match => match[1])
.filter(component => component !== 'span');
const filterComponents = Array.from(
filterRepresentation.matchAll(/<([^/\s>]+)/g)
)
.map(match => match[1])
.filter(component => component !== 'span');
const components = Array.from(
new Set(['List', ...fieldComponents, ...filterComponents])
).sort();

// eslint-disable-next-line no-console
console.log(
`Guessed List:
import { ${components.join(', ')} } from 'react-admin';
${filterRepresentation}
export const ${capitalize(singularize(resource))}List = () => (
<List>
${representation}
<List${filterRepresentation ? ' filters={filters}' : ''}>
${tableRepresentation}
</List>
);`
);
Expand All @@ -132,5 +171,9 @@ ${representation}
if (isPending) return <Loading />;
if (error) return <p>Error: {error.message}</p>;

return <ListView {...rest}>{child}</ListView>;
return (
<ListView filters={filters} {...rest}>
{child}
</ListView>
);
};
10 changes: 5 additions & 5 deletions packages/ra-supabase-ui-materialui/src/guessers/ShowGuesser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const ShowGuesserView = (
if (!resourceDefinition || !resourceDefinition.properties) {
return;
}
const inferredInputs = Object.keys(resourceDefinition.properties).map(
const inferredFields = Object.keys(resourceDefinition.properties).map(
(source: string) =>
inferElementFromType({
name: source,
Expand All @@ -74,15 +74,15 @@ export const ShowGuesserView = (
requiredFields,
})
);
const inferredForm = new InferredElement(
const inferredLayout = new InferredElement(
showFieldTypes.show,
null,
inferredInputs
inferredFields
);
setChild(inferredForm.getElement());
setChild(inferredLayout.getElement());
if (!enableLog) return;

const representation = inferredForm.getRepresentation();
const representation = inferredLayout.getRepresentation();

const components = ['Show']
.concat(
Expand Down
4 changes: 2 additions & 2 deletions packages/ra-supabase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ const App = () => (
export default App;
```

This generates an admin app with working CRUD for all resources.
This generates a dataProvider, and authProvider, and an admin app with working CRUD for all resources.

![Demo](./assets/demo.png)

The generated admin is fully functional:

- All public tables are listed in the sidebar
- Lists are sortable and paginated
- Lists are paginated, sortable, and filterable
- Creating, editing, and deleting records is possible
- Forms use the correct input component based on the field type
- Relationships are displayed as links in show views and as autocomplete inputs in edit views
Expand Down
Binary file modified packages/ra-supabase/assets/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit eaf7117

Please sign in to comment.