Presto

Presto is a library built on React that simplifies building CRUD interfaces by encouraging you to define ViewModel classes that describe the things your are managing.

When dealing with backend frameworks like django you define classes that model your data upfront that contain all kinds of useful metadata for building out interfaces. This includes information like the label of a field, help text about the field & the type of field. Once you know the type of a field you know the kind of widget in a form to render (eg. a Boolean field might be rendered as a checkbox) or how to display a user friendly version of a field value (eg. a foreign key field might render as the label of the related model).

Here's a simple example of a ViewModel:

import { BooleanField, CharField, EmailField, viewModelFactory } from '@prestojs/viewmodel';

export class User extends viewModelFactory(
    {
        id: new NumberField(),
        firstName: new CharField(),
        lastName: new CharField(),
        email: new EmailField(),
        isActive: new BooleanField({ label: 'Active?' }),
        contactType: new CharField({
            defaultValue: 'lead',
            choices: [
                ['lead', 'Lead'],
                ['contact', 'Contact'],
            ],
        }),
    },
    { pkFieldName: 'id' }
) {
    static label = 'User';
    static labelPlural = 'Users';
}

Here's example of generating a form from it:

const ExampleForm = () => (
    <Form onSubmit={data => console.log(data)}>
        <Form.Item field={User.fields.firstName} />
        <Form.Item field={User.fields.lastName} />
        <Form.Item field={User.fields.email} />
        <Form.Item field={User.fields.isActive} />
        <Form.Item field={User.fields.contactType} />
    </Form>
);

... or a view of the data:

function UserView() {
    const user = new User({
        id: 1,
        firstName: 'John',
        lastName: 'Doe',
        email: 'john@example.com',
        isActive: true,
        contactType: 'lead',
    });
    return (
        <dl>
            {User.fieldNames.map(fieldName => (
                <React.Fragment key={fieldName}>
                    <dt>{User.fields[fieldName].label}</dt>
                    <dd>
                        <FieldFormatter field={User.fields[fieldName]} value={user[fieldName]} />
                    </dd>
                </React.Fragment>
            ))}
        </dl>
    );
}