Skip to content

Commit 0e54ed4

Browse files
author
React-Admin CI
committed
[Doc] Update RA Core EE documentation
1 parent c90b5b5 commit 0e54ed4

10 files changed

+417
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
title: History Setup
3+
---
4+
5+
`@react-admin/ra-core-ee` contains hooks and components to help you track the changes made in your admin. See the history of revisions, compare differences between any two versions, and revert to a previous state if needed.
6+
7+
## Installation
8+
9+
The history features require a valid [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription. Once subscribed, follow the [instructions to get access to the private npm repository](https://react-admin-ee.marmelab.com/setup).
10+
11+
You can then install the npm package providing the history features using your favorite package manager:
12+
13+
```sh
14+
npm install --save @react-admin/ra-core-ee
15+
# or
16+
yarn add @react-admin/ra-core-ee
17+
```
18+
19+
## Data Provider Requirements
20+
21+
`ra-core-ee` relies on the `dataProvider` to read, create and delete revisions. In order to use the history features, you must add 3 new methods to your data provider: `getRevisions`, `addRevision` and `deleteRevisions`.
22+
23+
```tsx
24+
const dataProviderWithRevisions = {
25+
...dataProvider,
26+
getRevisions: async (resource, params) => {
27+
const { recordId } = params;
28+
// ...
29+
return { data: revisions };
30+
},
31+
addRevision: async (resource, params) => {
32+
const { recordId, data, authorId, message, description } = params;
33+
// ...
34+
return { data: revision };
35+
},
36+
deleteRevisions: async resource => {
37+
const { recordId } = params;
38+
// ...
39+
return { data: deletedRevisionIds };
40+
},
41+
};
42+
```
43+
44+
**Tip**: Revisions are immutable, so you don't need to implement an `updateRevision` method.
45+
46+
A `revision` is an object with the following properties:
47+
48+
```js
49+
{
50+
id: 123, // the revision id
51+
resource: 'products', // the resource name
52+
recordId: 456, // the id of the record
53+
data: {
54+
id: 456,
55+
title: 'Lorem ipsum',
56+
teaser: 'Lorem ipsum dolor sit amet',
57+
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
58+
}, // the data of the record
59+
// metadata
60+
authorId: 789, // the id of the author
61+
date: '2021-10-01T00:00:00.000Z', // the date of the revision
62+
message: 'Updated title, teaser, body', // the commit message
63+
description: 'Added a teaser', // the commit description
64+
}
65+
```
66+
67+
You can read an example data provider implementation in the package source at `src/history/dataProvider/builder/addRevisionMethodsBasedOnSingleResource.ts`.
68+
69+
Instead of implementing these new methods yourself, you can use one of the provided builders to generate them:
70+
71+
- `addRevisionMethodsBasedOnSingleResource` stores the revisions for all resources in a single `revisions` resource:
72+
73+
```tsx
74+
// in src/dataProvider.ts
75+
import { addRevisionMethodsBasedOnSingleResource } from '@react-admin/ra-core-ee';
76+
import baseDataProvider from './baseDataProvider';
77+
78+
export const dataProvider = addRevisionMethodsBasedOnSingleResource(
79+
baseDataProvider,
80+
{ resourceName: 'revisions' }
81+
);
82+
```
83+
84+
- `addRevisionMethodsBasedOnRelatedResource` stores the revisions of each resource in a related resource (e.g. store the revisions of `products` in `products_history`):
85+
86+
```tsx
87+
// in src/dataProvider.ts
88+
import { addRevisionMethodsBasedOnRelatedResource } from '@react-admin/ra-core-ee';
89+
import baseDataProvider from './baseDataProvider';
90+
91+
export const dataProvider = addRevisionMethodsBasedOnRelatedResource(
92+
baseDataProvider,
93+
{ getRevisionResourceName: resource => `${resource}_history` }
94+
);
95+
```
96+
97+
Once your provider has the three revisions methods, pass it to the `<CoreAdmin>` component and you're ready to start using the history features of `ra-core-ee`.
98+
99+
```tsx
100+
// in src/App.tsx
101+
import { CoreAdmin } from 'ra-core';
102+
import { dataProvider } from './dataProvider';
103+
104+
const App = () => (
105+
<CoreAdmin dataProvider={dataProvider}>{/* ... */}</CoreAdmin>
106+
);
107+
```

docs_headless/src/content/docs/ReferenceManyInputBase.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
---
22
title: "<ReferenceManyInputBase>"
33
---
4+
45
Use `<ReferenceManyInputBase>` in an edition or creation views to edit one-to-many relationships, e.g. to edit the variants of a product in the product edition view.
56

67
`<ReferenceManyInputBase>` fetches the related records, and renders them in a sub-form. When users add, remove of update related records, the `<ReferenceManyInputBase>` component stores these changes locally. When the users actually submit the form, `<ReferenceManyInputBase>` computes a diff with the existing relationship, and sends the related changes (additions, deletions, and updates) to the server.
78

9+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
10+
811
## Usage
912

1013
An example one-to-many relationship can be found in ecommerce systems: a product has many variants.

docs_headless/src/content/docs/ReferenceManyToManyFieldBase.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This component fetches a list of referenced records by lookup in an associative
66

77
Note: The `<ReferenceManyToManyFieldBase>` cannot currently display multiple records with the same id from the end reference resource, even though they might have different properties in the associative table.
88

9+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
10+
911
## Usage
1012

1113
Let's imagine that you're writing an app managing concerts for artists. The data model features a many-to-many relationship between the `bands` and `venues` tables through a `performances` associative table.

docs_headless/src/content/docs/ReferenceManyToManyInputBase.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ This component allows adding or removing relationships between two resources sha
66

77
**Note**: The `<ReferenceManyToManyInputBase>` cannot currently display multiple records with the same id from the end reference resource even though they might have different properties in the associative table.
88

9+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
10+
911
## Usage
1012

1113
Let's imagine that you're writing an app managing concerts for artists. The data model features a many-to-many relationship between the `bands` and `venues` tables through a `performances` associative table.

docs_headless/src/content/docs/ReferenceOneInputBase.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ title: "<ReferenceOneInputBase>"
33
---
44
Use `<ReferenceOneInputBase>` in an `<EditBase>` or `<CreateBase>` view to edit one-to-one relationships, e.g. to edit the details of a book in the book edition view.
55

6+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
7+
68
## Usage
79

810
Here is an example one-to-one relationship: a `book` has at most one `book_details` row associated to it.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
title: "useAddRevisionAfterMutation"
3+
---
4+
5+
This hook registers a mutation [middleware](https://marmelab.com/ra-core/useregistermutationmiddleware/) that automatically creates a revision after a successful create or update operation.
6+
7+
This middleware reads the revision metadata from a field named after the `REVISION_FIELD` constant, then removes it from the form data before saving the record.
8+
9+
The `REVISION_FIELD` constant can be imported from `@react-admin/ra-core-ee`.
10+
11+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
12+
13+
## Usage
14+
15+
Below is an example showing how to override the [`SaveContext`](https://marmelab.com/ra-core/usesavecontext/) to provide the revision metadata when saving a form:
16+
17+
```tsx
18+
import React, { ReactNode, useMemo } from 'react';
19+
import {
20+
EditBase,
21+
SaveContextProvider,
22+
useSaveContext,
23+
type SaveContextValue,
24+
} from 'ra-core';
25+
import { SimpleForm, TextInput, DeleteButton } from 'my-react-admin-ui-library';
26+
import {
27+
useAddRevisionAfterMutation,
28+
useGenerateChangeMessage,
29+
REVISION_FIELD,
30+
} from '@react-admin/ra-core-ee';
31+
32+
export const ProductEdit = () => (
33+
<EditBase>
34+
<CreateRevisionOnSave>
35+
<SimpleForm>
36+
<TextInput source="reference" />
37+
<TextInput source="category" />
38+
</SimpleForm>
39+
</CreateRevisionOnSave>
40+
</EditBase>
41+
);
42+
43+
const CreateRevisionOnSave = ({ children }: { children: ReactNode }) => {
44+
const originalSaveContext = useSaveContext();
45+
useAddRevisionAfterMutation();
46+
const generateChangeMessage = useGenerateChangeMessage();
47+
48+
// Wrap the original save function to add the revision data before saving
49+
const saveContext = useMemo<SaveContextValue>(
50+
() => ({
51+
...originalSaveContext,
52+
save: async (record, callbacks) =>
53+
originalSaveContext.save!(
54+
{
55+
...record,
56+
// Store the revision metadata in a special field that will be removed by the middleware
57+
[REVISION_FIELD]: {
58+
message: generateChangeMessage({ data: record }),
59+
description: '',
60+
authorId: 'john',
61+
},
62+
},
63+
callbacks
64+
),
65+
}),
66+
[generateChangeMessage, originalSaveContext]
67+
);
68+
69+
return (
70+
<SaveContextProvider value={saveContext}>
71+
{children}
72+
</SaveContextProvider>
73+
);
74+
};
75+
```
76+
77+
**Tip:** This example also leverages the [`useGenerateChangeMessage`](./useGenerateChangeMessage.md) hook to automatically generate a revision message based on the changes made in the form.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
title: "useApplyChangesBasedOnSearchParam"
3+
---
4+
Monitors the URL for a `_change` search parameter and automatically applies those changes to the current form. This is useful for implementing a "revert to revision" functionality.
5+
6+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
7+
8+
## Usage
9+
10+
```tsx
11+
import { EditBase } from 'ra-core';
12+
import { SimpleForm, TextInput } from 'my-react-admin-ui-library';
13+
import { useApplyChangesBasedOnSearchParam } from '@react-admin/ra-core-ee';
14+
15+
const ApplyChangesBasedOnSearchParam = () => {
16+
const hasCustomParams = useApplyChangesBasedOnSearchParam();
17+
return hasCustomParams ? (
18+
<div
19+
style={{
20+
backgroundColor: '#fff3cd',
21+
color: '#856404',
22+
border: '1px solid #856404',
23+
padding: '0.75em 1em',
24+
}}
25+
role="alert"
26+
>
27+
This form has been pre-filled with the changes from a previous
28+
revision. You can still modify the data before saving it.
29+
</div>
30+
) : null;
31+
};
32+
33+
const ProductEdit = () => (
34+
<EditBase>
35+
<SimpleForm>
36+
<ApplyChangesBasedOnSearchParam />
37+
<TextInput source="name" />
38+
<TextInput source="description" />
39+
</SimpleForm>
40+
</EditBase>
41+
);
42+
```
43+
44+
**Usage:**
45+
Navigate to a URL like `/products/1?_change={"name":"New Name","description":"New Description"}` to pre-fill the form with the specified data.
46+
47+
**Returns:**
48+
49+
- `boolean`: `true` if the form was pre-filled from URL parameters and the user hasn't made changes yet. Useful to show a warning message.
50+
51+
The hook:
52+
53+
1. Reads the `_change` parameter from the URL
54+
2. Parses the JSON data
55+
3. Sets form values using `setValue` with `shouldDirty: true`
56+
4. Removes the search parameter from the URL
57+
5. Tracks whether the form has custom parameters and user modifications
58+
59+
**Tip:** Be sure to use this hook as a child of a form component such as `<Form>` so that it can access the form context.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
title: "useDeleteRevisions"
3+
---
4+
Provides a mutation function to delete all revisions for a specific record. This is useful notably to delete all revisions when the record itself is deleted.
5+
6+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
7+
8+
## Usage
9+
10+
Here is an example showing how to implement a `<DeleteWithRevisionsButton>` that deletes both the record and its revisions:
11+
12+
```tsx
13+
import { EditBase, Form, useResourceContext, useRecordContext } from 'ra-core';
14+
import { TextInput, DeleteButton } from 'my-react-admin-ui-library';
15+
import { useDeleteRevisions } from '@react-admin/ra-core-ee';
16+
17+
const DeleteWithRevisionsButton = () => {
18+
const resource = useResourceContext();
19+
const record = useRecordContext();
20+
const [deleteRevisions] = useDeleteRevisions();
21+
return (
22+
<DeleteButton
23+
mutationOptions={{
24+
onSettled: (_data, error) => {
25+
if (error) return;
26+
deleteRevisions(resource, { recordId: record?.id });
27+
},
28+
}}
29+
label="Delete with revisions"
30+
/>
31+
);
32+
};
33+
34+
export const ProductEdit = () => (
35+
<EditBase>
36+
<Form>
37+
<TextInput source="reference" />
38+
<TextInput source="category" />
39+
<DeleteWithRevisionsButton />
40+
</Form>
41+
</EditBase>
42+
);
43+
```
44+
45+
**Hook Parameters:**
46+
47+
- `resource?`: Resource name. Defaults to the current resource context.
48+
- `params?`: Default parameters with `recordId`
49+
- `options?`: Default mutation options
50+
51+
**Returns:**
52+
A tuple with:
53+
54+
1. `deleteRevisions`: Function to trigger the deletion
55+
2. `mutation`: React Query mutation result object
56+
57+
**`deleteRevisions` Parameters:**
58+
59+
- `resource?`: Resource name (overrides hook-time resource)
60+
- `params?`: Object with `recordId` (overrides hook-time params)
61+
- `options?`: Mutation options including `returnPromise: boolean` (overrides hook-time options)

0 commit comments

Comments
 (0)