Skip to content

Commit c18e407

Browse files
committed
docs: update docs for bulk-ai-flow plugin
1 parent ba98648 commit c18e407

12 files changed

Lines changed: 128 additions & 38 deletions

File tree

‎adminforth/design/adoption.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ Which packages follow the [design concepts](./system-concepts.md): tokens instea
99
| @adminforth/audit-log | ✓ |
1010
| @adminforth/auto-remove | no UI |
1111
| @adminforth/background-jobs | ✓ |
12-
| @adminforth/bulk-ai-flow | |
13-
| @adminforth/clone-row | |
14-
| @adminforth/crud-approve-plugin | |
12+
| @adminforth/bulk-ai-flow | ✓ |
13+
| @adminforth/clone-row | ✓ |
14+
| @adminforth/crud-approve-plugin | ✓ |
1515
| @adminforth/dashboard | ✓ |
1616
| @adminforth/email-invite | ✓ |
1717
| @adminforth/email-password-reset | ✓ |
18-
| @adminforth/foreign-inline-list | |
18+
| @adminforth/foreign-inline-list | |
1919
| @adminforth/foreign-inline-show | |
2020
| @adminforth/i18n | ✓ |
2121
| @adminforth/i18n-static | ✓ |
@@ -63,4 +63,4 @@ The cards of the landing gallery (`documentation/src/pages/index.tsx`) show eith
6363

6464
- If simple - get of flowbite, prefer pure AFCL + Tailwind, move features we luck to AFCL
6565
- Existing icon fonts apart lucide - try to get rid of them if not complex
66-
- Core uses only Lucide icons and `flag-icons` flags (`afcl/CountryFlag.vue`). `vue-flowbite` and `vue-heroicons` stay in `spa/package.json` while plugins import them (flowbite: bulk-ai-flow, clone-row, foreign-inline-list, inline-create, user-soft-delete, and dashboard only for `flowbite:chart-pie-solid`, the old default menu icon which stored dashboard configs may name; no plugin imports heroicons any more). After plugins rework it they should disappear.
66+
- Core uses only Lucide icons and `flag-icons` flags (`afcl/CountryFlag.vue`). `vue-flowbite` and `vue-heroicons` stay in `spa/package.json` while plugins import them (flowbite: foreign-inline-list, inline-create, user-soft-delete, and dashboard only for `flowbite:chart-pie-solid`, the old default menu icon which stored dashboard configs may name; no plugin imports heroicons any more). After plugins rework it they should disappear.

‎documentation/docs/tutorial/09-Plugins/17-bulk-ai-flow.md‎

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,13 @@ export const admin = new AdminForth({
187187
> ⚠️ Make sure your attachFiles function returns a valid array of image URLs or an empty array. Returning anything else may cause an error.
188188
189189
## Usage
190-
1. Select fields you want to fill
190+
1. Select records you want to fill
191191
2. Click on the three dots menu
192-
3. Click analyze
192+
3. Click the action
193193
![alt text](Bulk-vision-1.png)
194-
4. Wait for finish analyze
195-
5. Check and edit result
194+
4. Wait while AI fills the records. The list on the left shows the status of every record, the record on the right shows the current value of each field next to the generated one. You can edit a generated value, regenerate one field or the whole record, or uncheck a record to skip it. `All`, `Changed` and `Failed` filter the list, `↑` and `↓` move between records and `Space` includes or skips the selected one.
196195
![alt text](Bulk-vision-2.png)
197-
6. Save changhes
196+
5. Click `Save`, only the checked records are saved
198197
![alt text](Bulk-vision-3.png)
199198
200199
@@ -346,24 +345,12 @@ new ImageGenerationAdapterOpenAI({
346345
347346
## Comparing new and old images
348347
349-
If you want to compare a generated image with an image stored in your storage, you need to add the preview prop in your upload plugin setup:
350-
351-
```ts
352-
new UploadPlugin({
353-
...
354-
//diff-add
355-
preview: {
356-
//diff-add
357-
previewUrl: ({filePath}) => `https://static.my-domain.com/${filePath}`,
358-
//diff-add
359-
}
360-
...
361-
})
362-
```
363-
After generation, you’ll see a button labeled "old image". Clicking it will open a pop-up where you can compare the generated image with the stored one:
348+
For an image field the review shows the stored image next to the generated one, a click opens an image in full size:
364349
365350
![alt text](Bulk-vision-4.png)
366351
352+
The link to the stored image comes from the Upload plugin of the column, the same way as its own previews: `preview.previewUrl` when it is set, otherwise a link of the storage adapter.
353+
367354
## Extra data fields
368355
369356
When creating default prompts, you can use Handlebars to pass data from the record into the prompt:
@@ -407,12 +394,12 @@ askConfirmationBeforeGenerating: true,
407394
...
408395
```
409396
410-
The user will now see a popup with a "Start generation" button and an "Edit prompts" button, allowing them to modify the prompt before running it.
397+
Before the generation starts, the user now sees the fields which will be generated. The pencil next to a field opens its prompt for editing. `Save prompt` keeps the edited prompt for the next runs, a prompt which is not saved (marked `Unsaved`) is used for this run only. `Reset to default` brings back the prompt from the plugin config. Here the user also chooses whether AI overwrites existing values or fills only empty fields.
411398
412399
![alt text](Bulk-vision-5.png)
413400
![alt text](Bulk-vision-6.png)
414401
415-
> ☝️ Updated prompts are stored in the user's local storage. Changes are local to that browser and do not affect other users or devices.
402+
> ☝️ Saved prompts are stored in the user's local storage. Changes are local to that browser and do not affect other users or devices.
416403
417404
## Processing big sets of data ( filtered records )
418405
@@ -456,14 +443,14 @@ And there won't be more than 5 parallel requests being handled.
456443
457444
## Controlling page size in the generation dialog
458445
459-
When users trigger bulk generation, records are displayed as cards in a paginated dialog. By default, 6 cards are shown per page. Use `pageSize` to tune this — lower values help when cards contain large images or long text, keeping the dialog fast and reviewable:
446+
When users trigger bulk generation, the dialog lists the records page by page, 10 records per page by default. Use `pageSize` to tune this:
460447
461448
```ts
462449
new BulkAiFlowPlugin({
463450
actionName: 'Generate description and Price',
464451

465452
//diff-add
466-
pageSize: 10, // default is 6
453+
pageSize: 20, // default is 10
467454

468455
// ...
469456
}),
-263 KB
Loading
-664 KB
Loading
-16.3 KB
Loading
-60.3 KB
Loading

‎documentation/src/pages/index.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ const images: GalleryItem[] = [
172172
description: 'Export tables to CSV and import from CSV with one click. Move data between environments easily'
173173
},
174174
{
175-
original: require('@site/static/img/previews/bulk-ai-flow.png').default,
175+
preview: 'bulk-ai-flow',
176176
title: 'Bulk AI Plugin - generate data for your resources',
177177
link: '/docs/tutorial/Plugins/bulk-ai-flow/',
178178
description: 'Use LLMs to fill records with generated data or images. For example, generate product descriptions based on product name and image or generate products images'
-635 KB
Binary file not shown.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Bulk AI flow</title>
6+
<link rel="stylesheet" href="previews.css">
7+
<style>
8+
.scrim { position: absolute; inset: 0; background: var(--scrim); display: flex; align-items: center; justify-content: center; }
9+
.dlg { width: 800px; background: var(--surface); border-radius: 12px; box-shadow: var(--shadow-pop); overflow: hidden; }
10+
.dlg-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px 8px; font-size: 15px; font-weight: 600; }
11+
.dlg-head .i { color: var(--muted); }
12+
.dlg-body { display: flex; flex-direction: column; gap: 10px; padding: 4px 18px 14px; }
13+
.dlg-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 18px; border-top: 1px solid var(--border); background: var(--bg); }
14+
.prog { display: flex; align-items: center; gap: 10px; font-size: 13px; }
15+
.prog .bar { flex: 1; }
16+
.ok { color: var(--success); }
17+
.err { color: var(--danger-fg); }
18+
.md { display: grid; grid-template-columns: 240px 1fr; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
19+
.rows { border-right: 1px solid var(--border); background: var(--bg); }
20+
.row { display: grid; grid-template-columns: 16px 34px 1fr 16px; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--border); border-left: 2px solid transparent; }
21+
.row.is-on { background: var(--accent-soft); border-left-color: var(--accent); }
22+
.row img { width: 34px; height: 24px; border-radius: 4px; object-fit: cover; }
23+
.row b { display: block; font-size: 12.5px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
24+
.row span { display: block; font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
25+
.row .cb:checked { display: grid; }
26+
.rec-head { display: flex; align-items: center; gap: 8px; padding: 9px 14px; border-bottom: 1px solid var(--border); }
27+
.rec-head b { font-size: 13px; font-weight: 600; }
28+
.rec-head .t-xs { display: block; }
29+
.field { padding: 8px 14px; border-bottom: 1px solid var(--border); }
30+
.field:last-child { border-bottom: 0; }
31+
.field-head { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; font-size: 12.5px; font-weight: 500; }
32+
.field-head .i { margin-left: auto; color: var(--muted); }
33+
.badge-new { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-fg); height: 18px; font-size: 11px; }
34+
.pair { display: grid; grid-template-columns: 1fr 14px 1fr; gap: 8px; align-items: start; }
35+
.pair > .i { width: 14px; height: 14px; margin-top: 8px; color: var(--faint); }
36+
.pair.media { align-items: center; }
37+
.pair.media > .i { margin-top: 0; }
38+
.cur { min-height: 30px; padding: 6px 9px; border-radius: 6px; background: var(--surface-2); color: var(--muted); font-size: 12px; line-height: 17px; }
39+
.gen { min-height: 30px; padding: 6px 9px; border-radius: 6px; border: 1px solid var(--border-strong); background: var(--surface); font-size: 12px; line-height: 17px; }
40+
.pic { width: 100%; height: 64px; border-radius: 6px; object-fit: cover; border: 1px solid var(--border); }
41+
.pic.toon { filter: saturate(1.9) contrast(1.15) brightness(1.05); }
42+
</style>
43+
</head>
44+
<body>
45+
<!-- the review of the bulk AI flow: records with their status on the left, current value next to the generated one on the right -->
46+
<div class="screen app" style="width: 880px; height: 550px">
47+
<aside class="side rail" data-active="Apartments"></aside>
48+
<div class="page">
49+
<header class="topbar">
50+
<span class="avatar"><svg class="i"><use href="icons.svg#user"/></svg></span>
51+
</header>
52+
<main class="content"></main>
53+
</div>
54+
<div class="scrim">
55+
<div class="dlg">
56+
<div class="dlg-head">Process with AI<svg class="i"><use href="icons.svg#x"/></svg></div>
57+
<div class="dlg-body">
58+
<div class="prog">
59+
<span class="spin"></span>
60+
<span class="num">5 of 8 records processed</span>
61+
<div class="bar"><span style="width: 62%"></span></div>
62+
</div>
63+
<div class="md">
64+
<div class="rows">
65+
<div class="row"><input class="cb" type="checkbox" checked><img src="villa.jpg" alt=""><div><b>Villa in Sitges</b><span>2 fields changed</span></div><svg class="i ok"><use href="icons.svg#circle-check"/></svg></div>
66+
<div class="row is-on"><input class="cb" type="checkbox" checked><img src="park-guell.jpg" alt=""><div><b>Loft near Park Güell</b><span>3 fields changed</span></div><svg class="i ok"><use href="icons.svg#circle-check"/></svg></div>
67+
<div class="row"><input class="cb" type="checkbox" checked><img src="login-bg.jpg" alt=""><div><b>Flat in Eixample</b><span style="color: var(--danger-fg)">Rate limit exceeded</span></div><svg class="i err"><use href="icons.svg#circle-alert"/></svg></div>
68+
<div class="row"><input class="cb" type="checkbox" checked><img src="villa.jpg" alt="" style="filter: hue-rotate(40deg)"><div><b>House in Girona</b><span>Generating…</span></div><span class="spin"></span></div>
69+
<div class="row" style="border-bottom: 0"><input class="cb" type="checkbox" checked><img src="park-guell.jpg" alt="" style="filter: grayscale(.6)"><div><b>Studio in Gràcia</b><span>Waiting</span></div><svg class="i t-muted"><use href="icons.svg#clock"/></svg></div>
70+
</div>
71+
<div>
72+
<div class="rec-head">
73+
<div style="flex: 1"><b>Loft near Park Güell</b><span class="t-xs t-muted">Record 2 of 8</span></div>
74+
<span class="btn btn-secondary btn-sm"><svg class="i i-sm"><use href="icons.svg#refresh-cw"/></svg>Regenerate</span>
75+
<span class="btn btn-secondary btn-sm btn-icon"><svg class="i"><use href="icons.svg#chevron-up"/></svg></span>
76+
<span class="btn btn-secondary btn-sm btn-icon"><svg class="i"><use href="icons.svg#chevron-down"/></svg></span>
77+
</div>
78+
<div class="field">
79+
<div class="field-head">Description <span class="badge badge-new">New</span><svg class="i i-sm"><use href="icons.svg#refresh-cw"/></svg></div>
80+
<div class="pair"><div class="cur">-</div><svg class="i"><use href="icons.svg#arrow-right"/></svg><div class="gen">Bright loft with a balcony facing Park Güell, calm street, five minutes to the metro.</div></div>
81+
</div>
82+
<div class="field">
83+
<div class="field-head">Square meter <span class="badge badge-new">Changed</span><svg class="i i-sm"><use href="icons.svg#refresh-cw"/></svg></div>
84+
<div class="pair"><div class="cur num">47</div><svg class="i"><use href="icons.svg#arrow-right"/></svg><div class="gen num">62</div></div>
85+
</div>
86+
<div class="field">
87+
<div class="field-head">Processed image <span class="badge badge-new">Changed</span><svg class="i i-sm"><use href="icons.svg#refresh-cw"/></svg></div>
88+
<div class="pair media"><img class="pic" src="park-guell.jpg" alt=""><svg class="i"><use href="icons.svg#arrow-right"/></svg><img class="pic toon" src="park-guell.jpg" alt=""></div>
89+
</div>
90+
</div>
91+
</div>
92+
</div>
93+
<div class="dlg-foot">
94+
<span class="btn btn-secondary">Cancel</span>
95+
<span class="btn btn-secondary">Save processed (5)</span>
96+
<span class="btn btn-primary is-disabled">Save 8 records</span>
97+
</div>
98+
</div>
99+
</div>
100+
</div>
101+
<script src="previews.js"></script>
102+
</body>
103+
</html>

‎documentation/static/landing-previews/icons.svg‎

Lines changed: 2 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)