Skip to content

Commit bdea9db

Browse files
committed
Introduce useBulkUpdateController
1 parent 7df8d6a commit bdea9db

File tree

4 files changed

+154
-149
lines changed

4 files changed

+154
-149
lines changed

packages/ra-core/src/controller/button/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ import useDeleteWithConfirmController from './useDeleteWithConfirmController';
33

44
export { useDeleteWithUndoController, useDeleteWithConfirmController };
55
export * from './useDeleteController';
6+
export * from './useBulkUpdateController';
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { useCallback, useMemo } from 'react';
2+
import { useRefresh } from '../../dataProvider/useRefresh';
3+
import { useListContext } from '../list/useListContext';
4+
import { useNotify } from '../../notification/useNotify';
5+
import { RaRecord, MutationMode } from '../../types';
6+
import { useResourceContext } from '../../core/useResourceContext';
7+
import { useTranslate } from '../../i18n/useTranslate';
8+
import {
9+
useUpdateMany,
10+
UseUpdateManyOptions,
11+
} from '../../dataProvider/useUpdateMany';
12+
13+
export const useBulkUpdateController = <
14+
RecordType extends RaRecord = any,
15+
ErrorType = Error,
16+
>(
17+
props: UseBulkUpdateControllerParams<RecordType, ErrorType>
18+
): UseBulkUpdateControllerReturn => {
19+
const {
20+
mutationMode = 'undoable',
21+
mutationOptions = {},
22+
successMessage,
23+
} = props;
24+
const { meta: mutationMeta, ...otherMutationOptions } = mutationOptions;
25+
const resource = useResourceContext(props);
26+
const notify = useNotify();
27+
const refresh = useRefresh();
28+
const translate = useTranslate();
29+
const { selectedIds, onUnselectItems } = useListContext();
30+
31+
const [updateMany, { isPending }] = useUpdateMany<RecordType, ErrorType>(
32+
resource,
33+
undefined,
34+
{
35+
onSuccess: () => {
36+
notify(
37+
successMessage ??
38+
`resources.${resource}.notifications.updated`,
39+
{
40+
type: 'info',
41+
messageArgs: {
42+
smart_count: selectedIds.length,
43+
_: translate('ra.notification.updated', {
44+
smart_count: selectedIds.length,
45+
}),
46+
},
47+
undoable: mutationMode === 'undoable',
48+
}
49+
);
50+
onUnselectItems();
51+
},
52+
onError: (error: any) => {
53+
notify(
54+
typeof error === 'string'
55+
? error
56+
: error.message || 'ra.notification.http_error',
57+
{
58+
type: 'error',
59+
messageArgs: {
60+
_:
61+
typeof error === 'string'
62+
? error
63+
: error && error.message
64+
? error.message
65+
: undefined,
66+
},
67+
}
68+
);
69+
refresh();
70+
},
71+
}
72+
);
73+
74+
const handleUpdate = useCallback(
75+
(data: Partial<RecordType>) => {
76+
updateMany(
77+
resource,
78+
{
79+
data,
80+
ids: selectedIds,
81+
meta: mutationMeta,
82+
},
83+
{
84+
mutationMode,
85+
...otherMutationOptions,
86+
}
87+
);
88+
},
89+
[
90+
updateMany,
91+
mutationMeta,
92+
mutationMode,
93+
otherMutationOptions,
94+
resource,
95+
selectedIds,
96+
]
97+
);
98+
99+
return useMemo(
100+
() => ({
101+
isPending,
102+
isLoading: isPending,
103+
handleUpdate,
104+
}),
105+
[isPending, handleUpdate]
106+
);
107+
};
108+
109+
export interface UseBulkUpdateControllerParams<
110+
RecordType extends RaRecord = any,
111+
MutationOptionsError = unknown,
112+
> {
113+
mutationMode?: MutationMode;
114+
mutationOptions?: UseUpdateManyOptions<RecordType, MutationOptionsError>;
115+
resource?: string;
116+
successMessage?: string;
117+
}
118+
119+
export interface UseBulkUpdateControllerReturn<
120+
RecordType extends RaRecord = any,
121+
> {
122+
isLoading: boolean;
123+
isPending: boolean;
124+
handleUpdate: (data: Partial<RecordType>) => void;
125+
}

packages/ra-ui-materialui/src/button/BulkUpdateWithConfirmButton.tsx

Lines changed: 15 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,14 @@ import {
99
import {
1010
useListContext,
1111
useTranslate,
12-
useUpdateMany,
13-
useNotify,
14-
useUnselectAll,
1512
useResourceContext,
16-
type MutationMode,
1713
type RaRecord,
18-
type UpdateManyParams,
14+
useBulkUpdateController,
15+
UseBulkUpdateControllerParams,
1916
} from 'ra-core';
2017

2118
import { Confirm } from '../layout';
2219
import { Button, type ButtonProps } from './Button';
23-
import type { UseMutationOptions } from '@tanstack/react-query';
2420
import { humanize, inflect } from 'inflection';
2521

2622
export const BulkUpdateWithConfirmButton = (
@@ -30,10 +26,8 @@ export const BulkUpdateWithConfirmButton = (
3026
props: inProps,
3127
name: PREFIX,
3228
});
33-
const notify = useNotify();
3429
const translate = useTranslate();
3530
const resource = useResourceContext(props);
36-
const unselectAll = useUnselectAll(resource);
3731
const [isOpen, setOpen] = useState(false);
3832
const { selectedIds } = useListContext();
3933

@@ -45,54 +39,12 @@ export const BulkUpdateWithConfirmButton = (
4539
label = 'ra.action.update',
4640
mutationMode = 'pessimistic',
4741
onClick,
48-
onSuccess = () => {
49-
notify(`resources.${resource}.notifications.updated`, {
50-
type: 'info',
51-
messageArgs: {
52-
smart_count: selectedIds.length,
53-
_: translate('ra.notification.updated', {
54-
smart_count: selectedIds.length,
55-
}),
56-
},
57-
undoable: mutationMode === 'undoable',
58-
});
59-
unselectAll();
60-
setOpen(false);
61-
},
62-
onError = (error: Error | string) => {
63-
notify(
64-
typeof error === 'string'
65-
? error
66-
: error.message || 'ra.notification.http_error',
67-
{
68-
type: 'error',
69-
messageArgs: {
70-
_:
71-
typeof error === 'string'
72-
? error
73-
: error && error.message
74-
? error.message
75-
: undefined,
76-
},
77-
}
78-
);
79-
setOpen(false);
80-
},
81-
mutationOptions = {},
8242
...rest
8343
} = props;
84-
const { meta: mutationMeta, ...otherMutationOptions } = mutationOptions;
85-
86-
const [updateMany, { isPending }] = useUpdateMany(
87-
resource,
88-
{ ids: selectedIds, data, meta: mutationMeta },
89-
{
90-
onSuccess,
91-
onError,
92-
mutationMode,
93-
...otherMutationOptions,
94-
}
95-
);
44+
const { handleUpdate, isPending } = useBulkUpdateController({
45+
...rest,
46+
mutationMode,
47+
});
9648

9749
const handleClick = e => {
9850
setOpen(true);
@@ -103,8 +55,9 @@ export const BulkUpdateWithConfirmButton = (
10355
setOpen(false);
10456
};
10557

106-
const handleUpdate = e => {
107-
updateMany();
58+
const handleConfirm = e => {
59+
setOpen(false);
60+
handleUpdate(data);
10861

10962
if (typeof onClick === 'function') {
11063
onClick(e);
@@ -155,7 +108,7 @@ export const BulkUpdateWithConfirmButton = (
155108
),
156109
}),
157110
}}
158-
onConfirm={handleUpdate}
111+
onConfirm={handleConfirm}
159112
onClose={handleDialogClose}
160113
/>
161114
</Fragment>
@@ -164,30 +117,20 @@ export const BulkUpdateWithConfirmButton = (
164117

165118
const sanitizeRestProps = ({
166119
label,
167-
onSuccess,
168-
onError,
120+
resource,
121+
successMessage,
169122
...rest
170-
}: Omit<
171-
BulkUpdateWithConfirmButtonProps,
172-
'resource' | 'selectedIds' | 'icon' | 'data'
173-
>) => rest;
123+
}: Omit<BulkUpdateWithConfirmButtonProps, 'icon' | 'data'>) => rest;
174124

175125
export interface BulkUpdateWithConfirmButtonProps<
176126
RecordType extends RaRecord = any,
177127
MutationOptionsError = unknown,
178-
> extends ButtonProps {
128+
> extends ButtonProps,
129+
UseBulkUpdateControllerParams<RecordType, MutationOptionsError> {
179130
confirmContent?: React.ReactNode;
180131
confirmTitle?: React.ReactNode;
181132
icon?: React.ReactNode;
182133
data: any;
183-
onSuccess?: () => void;
184-
onError?: (error: any) => void;
185-
mutationMode?: MutationMode;
186-
mutationOptions?: UseMutationOptions<
187-
RecordType,
188-
MutationOptionsError,
189-
UpdateManyParams<RecordType>
190-
> & { meta?: any };
191134
}
192135

193136
const PREFIX = 'RaBulkUpdateWithConfirmButton';

0 commit comments

Comments
 (0)