React Admin Enterprise Edition - February 2021 Update

François Zaninotto
François ZaninottoFebruary 04, 2021
#react#react-admin

The Enterprise Edition of react-admin speeds up the development of complex admin apps by offering 9 additional modules, ranging from editable datagrid to tree structure handling. We've been continuously adding new functionality to these modules, in reaction to feedback from our customers. We've also added 3 entirely new modules! Here is a summary of the most important changes of the past 3 months.

First, let's introduce the 3 new modules:

  • ra-search: Plug your search engine and let users search the entire admin via a smart Omnibox.
  • ra-calendar: Display and manipulate events, drag and resize appointments, and browse a calendar
  • ra-enterprise: Alternative ready-to-use components for the react-admin enterprise edition

Next: check out these exciting new features added to existing modules:

  • ra-preferences: Saved filters
  • ra-relationships: Support for multiple many-to-many inputs in the same form

Note: There are many more changes in the Open-Source Edition or react-admin. We've already described them in a separate blog post.

Now, let's go into the details.

ra-search: Plug your search engine and let users search the entire admin via a smart Omnibox

In large admins, users need several clicks to get to one record. For repetitive tasks, this ends up costing minutes every day. The ra-search Omnibox simplifies navigation by providing a global, always-on search engine for records.

ra-search demo

Ra-search can take advantage of a search engine like ElasticSearch if you have one, or it can rely on your REST API by searching across multiple resources in parallel.

Ra-search passes search strings to the dataProvider.search() method, which should return an array of search results:

dataProvider.search('roll').then(response => console.log(response));
// {
//     data: [
//         { id: 'a7535', type: 'artist', url: '/artists/7535', content: { label: 'The Rolling Stones', description: 'English rock band formed in London in 1962'  } }
//         { id: 'a5352', type: 'artist', url: '/artists/5352', content: { label: 'Sonny Rollins', description: 'American jazz tenor saxophonist'  } }
//         { id: 't7524', type: 'track', url: '/tracks/7524', content: { label: 'Like a Rolling Stone', year: 1965, recordCompany: 'Columbia', artistId: 345, albumId: 435456 } }
//         { id: 't2386', type: 'track', url: '/tracks/2386', content: { label: "It's Only Rock 'N Roll (But I Like It)", year: 1974, artistId: 7535, albumId: 6325 } }
//         { id: 'a6325', type: 'album', url: '/albums/6325', content: { label: "It's Only rock 'N Roll", year: 1974, artistId: 7535 }}
//     ],
//     total: 5
// }

It is your responsibility to add this search method to your dataProvider.

If your API doesn't expose a full-text search endpoint, no worries! You can use the simple addSearchMethod helper function. It adds the search() method to an existing dataProvider, relying on the dataProvider.getList() method on the configured resources.

import simpleRestProvider from 'ra-data-simple-rest';
import { addSearchMethod } from '@react-admin/ra-search';

const dataProvider = simpleRestProvider('http://path.to.my.api/');

const dataProviderWithSearch = addSearchMethod(dataProvider, ['artists', 'tracks', 'albums']);

Now calling dataProvider.search('roll') will issue the following queries in parallel and aggregate the results into a single response:

dataProvider.getList('artists', { filter: { q: "roll" }})
dataProvider.getList('tracks',  { filter: { q: "roll" }})
dataProvider.getList('albumns', { filter: { q: "roll" }})

To integrate the search omnibox in the app bar or a react-admin application, use the <Search> component:

import { Admin, AppBar, Layout, Resource } from 'react-admin';
import { Typography } from '@material-ui/core';
import { Search } from '@react-admin/ra-search';

const MyAppbar = (props) => (
    <AppBar {...props}>
        <Typography id="react-admin-title" variant="h6" color="inherit" />
        <Search />
    </AppBar>
);

const MyLayout = (props) => (
    <Layout {...props} appBar={MyAppbar} />
);

export const App = () => (
    <Admin dataProvider={searchDataProvider} layout={MyLayout}>
        {...}
    </Admin>
);

And there you go:

Basic ra-search integration

You can customize pretty much everything in the way search results appear: grouping, search result items, etc. Refer to the ra-search documentation for details.

ra-calendar: Display and manipulate events, drag and resize appointments, and browse a calendar

To manipulate events, meetings, schedules, the best UI is that of a calendar. Don't build it yourself! The ra-calendar module integrates Full Calendar with react-admin:

ra-calendar demo

It offers an impressive amount of features:

  • month, week, day views
  • list view
  • drag and resize events
  • whole-day events
  • creating an event by clicking on the calendar
  • edition of the event title, and metadata
  • events spanning multiple days
  • recurring events
  • background events
  • theming
  • locales and timezones
  • resource time grid (e.g. rooms) (requires an additional license from Full Calendar)

The easiest way to use it is via <CompleteCalendar>. It's an all-in-one component that renders a calendar, as well as a form to edit or create new events that opens in a dialog. It is deeply integrated with react-admin, and benefits from the same speed optimizations.

Use it as the list prop of a <Resource>. No need to specify an edit or create prop for this resource, but you'll have to pass a form component (like <SimpleForm>) as the child of <CompleteCalendar> to define the event edition form.

Here is an example:

import React, { FC } from 'react';
import {
    Admin,
    Resource,
    List,
    ListProps,
    SimpleForm,
    TextInput,
    DateTimeInput,
} from 'react-admin';
import { CompleteCalendar } from '@react-admin/ra-calendar';

import dataProvider from './dataProvider';

const EventList: FC<ListProps> = props => (
    <CompleteCalendar {...props}>
        <SimpleForm>
            <TextInput source="title" autoFocus />
            <DateTimeInput source="start" />
            <DateTimeInput source="end" />
        </SimpleForm>
    </CompleteCalendar>
);

export const Basic: FC = () => (
    <Admin dataProvider={dataProvider}>
        <Resource name="events" list={EventList} />
    </Admin>
);

<CompleteCalendar> lets you customize pretty much everything about the calendar view and the event edition and creation forms. The ra-calendar documentation page gives all the details.

ra-enterprise: Alternative ready-to-use components for the react-admin enterprise edition

Adding react-admin Enterprise Edition modules to your app may require a bit of configuration. To facilitate the installation of the private modules, ra-enterprise lets you change only one import by providing alternative components to <Admin>, <Layout>, <AppBar>, and <Breadcrumb>.

- import { Admin, Resource } from 'react-admin';
+ import { Admin } from '@react-admin/ra-enterprise';
+ import { Resource } from 'react-admin';
import posts from './posts';

const App = () => (
    <Admin dataProvider={dataProvider}>
        <Resource name="posts" {...posts}>
    </Admin>
);

This alternative <Admin> component pre-configures many of the ra-enterprise modules. It comes with:

This modifies the default look and feel of react-admin:

ra-enterprise Admin

To be compared with the default admin:

Classic Admin

ra-enterprise makes it easy to start a new admin app with React-admin Enterprise Edition. It comes with sensible defaults that match most common cases. You should be able to start working on your business logic in no time!

Saved Filters: Store The Current Query For Later Reuse

Some lists offer many individual filters and sort options, and users may need to repeatedly apply a certain combination of those - in other words, a custom query. ra-preferences offers users a way to store the current query in local storage, so as to find it later in a list of "saved queries".

Saved Queries

If your list uses the <FilterList> sidebar, add the <SavedQueriesList> component before the first <FilterList> to enable saved queries:

import { FilterList, FilterListItem, List, Datagrid } from 'react-admin';
import { Card, CardContent } from '@material-ui/core';

+import { SavedQueriesList } from '@react-admin/ra-preferences';

const SongFilterSidebar: FC = () => (
    <Card>
        <CardContent>
+           <SavedQueriesList />
            <FilterList label="Record Company" icon={<BusinessIcon />}>
                ...
            </FilterList>
            <FilterList label="Released" icon={<DateRangeeIcon />}>
               ...
            </FilterList>
        </CardContent>
    </Card>
);

const SongList: FC<Props> = props => (
    <List {...props} aside={<SongFilterSidebar />}>
        <Datagrid>
            ...
        </Datagrid>
    </List>
);

If your list uses the <Filter> Button/Form Combo, replace react-admin's <Filter> with ra-preference's <FilterWithSave> to enable saved queries:

import {
-   Filter,
    SelectInput,
    DateInput,
    List,
    Datagrid,
    TextField,
    NumberField,
    DateField
} from 'react-admin';
+import { FilterWithSave } from '@react-admin/ra-preferences';

const SongFilter: FC = props => (
-   <Filter {...props}>
+   <FilterWithSave {...props}>
        <SelectInput
            choices={[
                { id: 'Apple', name: 'Apple' },
                { id: 'Atlantic', name: 'Atlantic' },
                { id: 'Capitol', name: 'Capitol' },
                { id: 'Chess', name: 'Chess' },
                { id: 'Columbia', name: 'Columbia' },
                { id: 'DGC', name: 'DGC' },
                { id: 'London', name: 'London' },
                { id: 'Tamla', name: 'Tamla' },
            ]}
            source="recordCompany"
        />
        <DateInput source="released_gte" label="Released after" />
        <DateInput source="released_lte" label="Released before" />
-   </Filter>
+   </FilterWithSave>
);

const SongList: FC<Props> = props => (
    <List {...props} filters={<SongFilter />}>
        <Datagrid rowClick="edit">
            <TextField source="title" />
            <TextField source="artist" />
            <TextField source="writer" />
            <TextField source="producer" />
            <TextField source="recordCompany" />
            <NumberField source="rank" />
            <DateField source="released" />
        </Datagrid>
    </List>
);

Saved queries in Filter button

Multiple Many To Many Input Components In One Form

The ra-relationships module offers the <ReferenceManyToManyInput> component to edit many-to-many relationships:

Many to many relationships

In previous versions of this module, you could only have one <ReferenceManyToManyInput> per form. This limit was lifted in version 2.0 of the ra-relationships module. That change was not possible in a backward-compatible way, that's why we bumped the major version number of the ra-relationships module.

To enable one or many <ReferenceManyToManyInput> inside a form, you must now wrap the form inside a <ManyToManyReferenceContextProvider>:

import React from 'react';
import { Edit, SelectArrayInput, SimpleForm, TextInput } from 'react-admin';

import { ReferenceManyToManyInput, ManyToManyReferenceContextProvider } from '@react-admin/ra-many-to-many';

const ArtistEdit = (props) => (
    <Edit {...props}>
        {/*
            The ManyToManyReferenceContextProvider wrapper around the form is needed.
            This component is responsible for handling the references updates even when
            there are multiple ReferenceManyToManyInput targeting different relations.
        */}
        <ManyToManyReferenceContextProvider>
            <SimpleForm>
                <TextInput disabled source="id" />
                <TextInput source="first_name" />
                <TextInput source="last_name" />
                <ReferenceManyToManyInput
                    source="id"
                    reference="events"
                    through="performances"
                    using="artist_id,event_id"
                    fullWidth
                    label="Performances"
                >
                    <SelectArrayInput optionText="name" />
                </ReferenceManyToManyInput>
            </SimpleForm>
        </ManyToManyReferenceContextProvider>
    </Edit>
);

export default ArtistEdit;

Conclusion

Each private module now shows a changelog section on its documentation page, so make sure you check the ones of the modules you use. For instance, here is a glimpse of the ra-preferences changelog:

ra-preference changelog

Overall, since the last time we published a blog post about react-admin Enterprise Edition, we've pushed more than 70 changes (bug fixes, UI tweaks, documentation changes, new features) to the private modules.

If you're an Enterprise Edition customer, you get all these updates for free! Just run yarn upgrade to get the latest version of the @react-admin/XXX modules.

If you're not an Enterprise Edition customer yet, check out our plans: from 125€/month, you can dramatically speed up your development by using these modules that we've carefully crafted, tested, and documented, instead of writing your own code. A single one of these modules would already provide enough value to justify the subscription - but by subscribing, you get access to all 12 of them. And unlimited professional support!

Enterprise edition modules

Did you like this article? Share it!