Skip to content

Commit f19375c

Browse files
committed
feat: enhance action handling with bulk support and improve documentation
1 parent d1de6d9 commit f19375c

File tree

5 files changed

+52
-5
lines changed

5 files changed

+52
-5
lines changed

adminforth/documentation/docs/tutorial/03-Customization/09-Actions.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,57 @@ Here's how to add a custom action:
4848
- `name`: Display name of the action
4949
- `icon`: Icon to show (using Flowbite icon set)
5050
- `allowed`: Function to control access to the action
51-
- `action`: Handler function that executes when action is triggered
51+
- `action`: Handler function that executes when action is triggered for a **single** record
52+
- `bulkHandler`: Handler function that executes when the action is triggered for **multiple** records at once (see [Bulk button with bulkHandler](#bulk-button-with-bulkhandler))
5253
- `showIn`: Controls where the action appears
5354
- `list`: whether to show in list view
5455
- `listThreeDotsMenu`: whether to show in three dots menu in the list view
5556
- `showButton`: whether to show as a button on show view
5657
- `showThreeDotsMenu`: when to show in the three-dots menu of show view
58+
- `bulkButton`: whether to show as a bulk-selection button in the list view
59+
60+
### Bulk button with `action`
61+
62+
When `showIn.bulkButton` is `true` and only `action` (not `bulkHandler`) is defined, AdminForth automatically calls your `action` function **once per selected record** using `Promise.all`. This is convenient for simple cases but means N separate handler invocations run in parallel:
63+
64+
```ts title="./resources/apartments.ts"
65+
{
66+
name: 'Auto submit',
67+
action: async ({ recordId }) => {
68+
// Called once per selected record when used as a bulk button
69+
await doSomething(recordId);
70+
return { ok: true, successMessage: 'Done' };
71+
},
72+
showIn: {
73+
bulkButton: true, // triggers Promise.all over selected records
74+
showButton: true,
75+
}
76+
}
77+
```
78+
79+
If your operation can be expressed more efficiently as a single batched query (e.g., a single `UPDATE … WHERE id IN (…)`), define `bulkHandler` instead. AdminForth will call it **once** with all selected record IDs:
80+
81+
```ts title="./resources/apartments.ts"
82+
{
83+
name: 'Auto submit',
84+
// bulkHandler receives all recordIds in one call – use it for batched operations
85+
bulkHandler: async ({ recordIds, adminforth, resource }) => {
86+
await doSomethingBatch(recordIds);
87+
return { ok: true, successMessage: `Processed ${recordIds.length} records` };
88+
},
89+
// You can still keep `action` for the single-record show/edit buttons
90+
action: async ({ recordId }) => {
91+
await doSomething(recordId);
92+
return { ok: true, successMessage: 'Done' };
93+
},
94+
showIn: {
95+
bulkButton: true,
96+
showButton: true,
97+
}
98+
}
99+
```
100+
101+
> ☝️ When both `action` and `bulkHandler` are defined, AdminForth uses `bulkHandler` for bulk operations and `action` for single-record operations. When only `action` is defined and `bulkButton` is enabled, AdminForth falls back to `Promise.all` over individual `action` calls.
57102
58103
### Access Control
59104

adminforth/modules/restApi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,8 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
682682
),
683683
actions: resource.options.actions?.map((action) => ({
684684
...action,
685-
bulkHandler: !!action.bulkHandler,
685+
hasBulkHandler: !!action.bulkHandler,
686+
bulkHandler: undefined,
686687
})),
687688
allowedActions,
688689
}

adminforth/spa/src/utils/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ export async function executeCustomBulkAction({
803803
try {
804804
const action = resource?.options?.actions?.find((a: any) => a.id === actionId) as AdminForthActionFront | undefined;
805805

806-
if (action?.bulkHandler && action?.showIn?.bulkButton) {
806+
if (action?.hasBulkHandler && action?.showIn?.bulkButton) {
807807
const result = await callAdminForthApi({
808808
path: '/start_custom_bulk_action',
809809
method: 'POST',

adminforth/spa/src/views/ListView.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ async function startCustomBulkActionInner(actionId: string | number) {
361361
const successResults = results.filter(r => r?.successMessage);
362362
if (successResults.length > 0) {
363363
alert({
364-
message: action?.bulkSuccessMessage ? action.bulkSuccessMessage : action?.bulkHandler ? successResults[0].successMessage : `${successResults.length} out of ${results.length} items processed successfully`,
364+
message: action?.bulkSuccessMessage ? action.bulkSuccessMessage : action?.hasBulkHandler ? successResults[0].successMessage : `${successResults.length} out of ${results.length} items processed successfully`,
365365
variant: 'success'
366366
});
367367
}

adminforth/types/Common.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,9 @@ export type FieldGroup = {
314314
noTitle?: boolean;
315315
};
316316

317-
export interface AdminForthActionFront extends Omit<AdminForthActionInput, 'id'> {
317+
export interface AdminForthActionFront extends Omit<AdminForthActionInput, 'id' | 'bulkHandler' | 'action' | 'allowed'> {
318318
id: string;
319+
hasBulkHandler?: boolean;
319320
}
320321

321322
export interface AdminForthBulkActionFront extends Omit<AdminForthBulkActionCommon, 'id'> {

0 commit comments

Comments
 (0)