@adminforth/bulk-ai-flow 1.1.5 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.log +3 -2
- package/custom/imageGenerationCarousel.vue +462 -0
- package/custom/visionAction.vue +403 -80
- package/custom/visionTable.vue +56 -4
- package/dist/custom/imageGenerationCarousel.vue +462 -0
- package/dist/custom/visionAction.vue +403 -80
- package/dist/custom/visionTable.vue +56 -4
- package/dist/index.js +295 -17
- package/index.ts +346 -26
- package/package.json +1 -1
- package/types.ts +41 -3
package/custom/visionAction.vue
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div @click="openDialog">
|
|
3
|
-
|
|
2
|
+
<div class="flex items-end justify-start gap-2" @click="openDialog">
|
|
3
|
+
<div class="flex items-center justify-center text-white bg-gradient-to-r h-[18px] from-purple-500 via-purple-600 to-purple-700 hover:bg-gradient-to-br focus:ring-4 focus:outline-none focus:ring-purple-300 dark:focus:ring-purple-800 font-medium rounded-md text-sm px-1 text-center">
|
|
4
|
+
AI
|
|
5
|
+
</div>
|
|
6
|
+
<p class="text-justify max-h-[18px]">{{ props.meta.actionName }}</p>
|
|
4
7
|
</div>
|
|
5
8
|
<Dialog ref="confirmDialog">
|
|
6
9
|
<div
|
|
7
|
-
class="fixed inset-0 z-
|
|
10
|
+
class="fixed inset-0 z-20 flex items-center justify-center bg-black/40"
|
|
8
11
|
@click="closeDialog"
|
|
9
12
|
>
|
|
10
13
|
<div
|
|
11
|
-
class="bulk-vision-dialog relative max-w-[95vw] max-h-[90vh] bg-white dark:bg-gray-900 rounded-md shadow-2xl overflow-hidden"
|
|
14
|
+
class="bulk-vision-dialog flex items-center justify-center relative max-w-[95vw] min-w-[640px] max-h-[90vh] bg-white dark:bg-gray-900 rounded-md shadow-2xl overflow-hidden"
|
|
12
15
|
@click.stop
|
|
13
16
|
>
|
|
14
|
-
<div class="bulk-vision-table flex flex-col items-
|
|
17
|
+
<div class="bulk-vision-table flex flex-col items-center justify-evenly gap-4 w-full h-full p-6 overflow-y-auto">
|
|
18
|
+
<button type="button"
|
|
19
|
+
@click="closeDialog"
|
|
20
|
+
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" >
|
|
21
|
+
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
22
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
23
|
+
</svg>
|
|
24
|
+
</button>
|
|
25
|
+
|
|
15
26
|
<VisionTable
|
|
16
27
|
v-if="records && props.checkboxes.length"
|
|
17
28
|
:checkbox="props.checkboxes"
|
|
@@ -24,15 +35,30 @@
|
|
|
24
35
|
:customFieldNames="customFieldNames"
|
|
25
36
|
:tableColumnsIndexes="tableColumnsIndexes"
|
|
26
37
|
:selected="selected"
|
|
27
|
-
:
|
|
38
|
+
:isAiResponseReceivedAnalize="isAiResponseReceivedAnalize"
|
|
39
|
+
:isAiResponseReceivedImage="isAiResponseReceivedImage"
|
|
28
40
|
:primaryKey="primaryKey"
|
|
41
|
+
:openGenerationCarousel="openGenerationCarousel"
|
|
42
|
+
@error="handleTableError"
|
|
29
43
|
/>
|
|
30
|
-
<
|
|
31
|
-
class="
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
<div class="flex w-full items-end justify-end gap-4">
|
|
45
|
+
<div class="h-full text-red-600 font-semibold flex items-center justify-center mb-2">
|
|
46
|
+
<p v-if="isError === true">{{ errorMessage }}</p>
|
|
47
|
+
</div>
|
|
48
|
+
<button type="button" class="py-2.5 px-5 ms-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
49
|
+
@click="closeDialog"
|
|
50
|
+
>
|
|
51
|
+
{{'Cancel'}}
|
|
52
|
+
</button>
|
|
53
|
+
<Button
|
|
54
|
+
class="w-64"
|
|
55
|
+
@click="saveData"
|
|
56
|
+
:disabled="isLoading || checkedCount < 1 || isCriticalError"
|
|
57
|
+
:loader="isLoading"
|
|
58
|
+
>
|
|
59
|
+
{{ checkedCount > 1 ? 'Save fields' : 'Save field' }}
|
|
60
|
+
</Button>
|
|
61
|
+
</div>
|
|
36
62
|
</div>
|
|
37
63
|
</div>
|
|
38
64
|
</div>
|
|
@@ -41,15 +67,22 @@
|
|
|
41
67
|
|
|
42
68
|
<script lang="ts" setup>
|
|
43
69
|
import { callAdminForthApi } from '@/utils';
|
|
44
|
-
import { ref, watch } from 'vue'
|
|
70
|
+
import { handleError, ref, watch } from 'vue'
|
|
45
71
|
import { Dialog, Button } from '@/afcl';
|
|
46
72
|
import VisionTable from './visionTable.vue'
|
|
73
|
+
import adminforth from '@/adminforth';
|
|
74
|
+
import { useI18n } from 'vue-i18n';
|
|
75
|
+
import { useRoute } from 'vue-router';
|
|
76
|
+
import { type AdminUser, type AdminForthResourceCommon } from '@/types';
|
|
77
|
+
|
|
78
|
+
const route = useRoute();
|
|
79
|
+
const { t } = useI18n();
|
|
47
80
|
|
|
48
81
|
const props = defineProps<{
|
|
49
82
|
checkboxes: any,
|
|
50
83
|
meta: any,
|
|
51
|
-
resource:
|
|
52
|
-
adminUser:
|
|
84
|
+
resource: AdminForthResourceCommon,
|
|
85
|
+
adminUser: AdminUser,
|
|
53
86
|
updateList: {
|
|
54
87
|
type: Function,
|
|
55
88
|
required: true
|
|
@@ -67,30 +100,66 @@ const tableColumns = ref([]);
|
|
|
67
100
|
const tableColumnsIndexes = ref([]);
|
|
68
101
|
const customFieldNames = ref([]);
|
|
69
102
|
const selected = ref<any[]>([]);
|
|
70
|
-
const
|
|
103
|
+
const isAiResponseReceivedAnalize = ref([]);
|
|
104
|
+
const isAiResponseReceivedImage = ref([]);
|
|
71
105
|
const primaryKey = props.meta.primaryKey;
|
|
106
|
+
const openGenerationCarousel = ref([]);
|
|
107
|
+
const isLoading = ref(false);
|
|
108
|
+
const isError = ref(false);
|
|
109
|
+
const isCriticalError = ref(false);
|
|
110
|
+
const errorMessage = ref('');
|
|
111
|
+
const checkedCount = ref(0);
|
|
72
112
|
|
|
73
113
|
const openDialog = async () => {
|
|
74
114
|
confirmDialog.value.open();
|
|
75
115
|
await getRecords();
|
|
76
|
-
|
|
116
|
+
if (props.meta.isFieldsForAnalizeFromImages || props.meta.isImageGeneration) {
|
|
117
|
+
await getImages();
|
|
118
|
+
}
|
|
77
119
|
tableHeaders.value = generateTableHeaders(props.meta.outputFields);
|
|
78
120
|
const result = generateTableColumns();
|
|
79
121
|
tableColumns.value = result.tableData;
|
|
80
122
|
tableColumnsIndexes.value = result.indexes;
|
|
81
|
-
|
|
82
|
-
customFieldNames.value = tableHeaders.value.slice(3).map(h => h.fieldName);
|
|
123
|
+
customFieldNames.value = tableHeaders.value.slice(props.meta.isFieldsForAnalizeFromImages ? 3 : 2).map(h => h.fieldName);
|
|
83
124
|
setSelected();
|
|
84
|
-
|
|
125
|
+
for (let i = 0; i < selected.value?.length; i++) {
|
|
126
|
+
openGenerationCarousel.value[i] = props.meta.outputImageFields?.reduce((acc,key) =>{
|
|
127
|
+
acc[key] = false;
|
|
128
|
+
return acc;
|
|
129
|
+
},{[primaryKey]: records.value[i][primaryKey]} as Record<string, boolean>);
|
|
130
|
+
}
|
|
131
|
+
isLoading.value = true;
|
|
132
|
+
const tasks = [];
|
|
133
|
+
if (props.meta.isFieldsForAnalizeFromImages) {
|
|
134
|
+
tasks.push(analyzeFields());
|
|
135
|
+
}
|
|
136
|
+
if (props.meta.isFieldsForAnalizePlain) {
|
|
137
|
+
tasks.push(analyzeFieldsNoImages());
|
|
138
|
+
}
|
|
139
|
+
if (props.meta.isImageGeneration) {
|
|
140
|
+
tasks.push(generateImages());
|
|
141
|
+
}
|
|
142
|
+
await Promise.all(tasks);
|
|
143
|
+
isLoading.value = false;
|
|
85
144
|
}
|
|
86
145
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
146
|
+
watch(selected, (val) => {
|
|
147
|
+
console.log('Selected changed:', val);
|
|
148
|
+
checkedCount.value = val.filter(item => item.isChecked === true).length;
|
|
149
|
+
}, { deep: true });
|
|
90
150
|
|
|
91
151
|
const closeDialog = () => {
|
|
92
152
|
confirmDialog.value.close();
|
|
93
|
-
|
|
153
|
+
isAiResponseReceivedAnalize.value = [];
|
|
154
|
+
isAiResponseReceivedImage.value = [];
|
|
155
|
+
|
|
156
|
+
records.value = [];
|
|
157
|
+
images.value = [];
|
|
158
|
+
selected.value = [];
|
|
159
|
+
tableColumns.value = [];
|
|
160
|
+
tableColumnsIndexes.value = [];
|
|
161
|
+
isError.value = false;
|
|
162
|
+
errorMessage.value = '';
|
|
94
163
|
}
|
|
95
164
|
|
|
96
165
|
function formatLabel(str) {
|
|
@@ -105,8 +174,9 @@ function generateTableHeaders(outputFields) {
|
|
|
105
174
|
|
|
106
175
|
headers.push({ label: 'Checkboxes', fieldName: 'checkboxes' });
|
|
107
176
|
headers.push({ label: 'Field name', fieldName: 'label' });
|
|
108
|
-
|
|
109
|
-
|
|
177
|
+
if (props.meta.isFieldsForAnalizeFromImages) {
|
|
178
|
+
headers.push({ label: 'Source Images', fieldName: 'images' });
|
|
179
|
+
}
|
|
110
180
|
for (const key in outputFields) {
|
|
111
181
|
headers.push({
|
|
112
182
|
label: formatLabel(key),
|
|
@@ -145,7 +215,7 @@ function setSelected() {
|
|
|
145
215
|
selected.value = records.value.map(() => ({}));
|
|
146
216
|
records.value.forEach((record, index) => {
|
|
147
217
|
for (const key in props.meta.outputFields) {
|
|
148
|
-
if(isInColumnEnum(key)){
|
|
218
|
+
if (isInColumnEnum(key)) {
|
|
149
219
|
const colEnum = props.meta.columnEnums.find(c => c.name === key);
|
|
150
220
|
const object = colEnum.enum.find(item => item.value === record[key]);
|
|
151
221
|
selected.value[index][key] = object ? record[key] : null;
|
|
@@ -155,7 +225,7 @@ function setSelected() {
|
|
|
155
225
|
}
|
|
156
226
|
selected.value[index].isChecked = true;
|
|
157
227
|
selected.value[index][primaryKey] = record[primaryKey];
|
|
158
|
-
|
|
228
|
+
isAiResponseReceivedAnalize.value[index] = true;
|
|
159
229
|
});
|
|
160
230
|
}
|
|
161
231
|
|
|
@@ -167,30 +237,48 @@ function isInColumnEnum(key: string): boolean {
|
|
|
167
237
|
return true;
|
|
168
238
|
}
|
|
169
239
|
|
|
240
|
+
function handleTableError(errorData) {
|
|
241
|
+
isError.value = errorData.isError;
|
|
242
|
+
errorMessage.value = errorData.errorMessage;
|
|
243
|
+
}
|
|
244
|
+
|
|
170
245
|
async function getRecords() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
246
|
+
try {
|
|
247
|
+
const res = await callAdminForthApi({
|
|
248
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get_records`,
|
|
249
|
+
method: 'POST',
|
|
250
|
+
body: {
|
|
251
|
+
record: props.checkboxes,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
records.value = res.records;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error('Failed to get records:', error);
|
|
257
|
+
isError.value = true;
|
|
258
|
+
errorMessage.value = `Failed to fetch records. Please, try to re-run the action.`;
|
|
259
|
+
// Handle error appropriately
|
|
260
|
+
}
|
|
179
261
|
}
|
|
180
262
|
|
|
181
263
|
async function getImages() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
264
|
+
try {
|
|
265
|
+
const res = await callAdminForthApi({
|
|
266
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get_images`,
|
|
267
|
+
method: 'POST',
|
|
268
|
+
body: {
|
|
269
|
+
record: records.value,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
images.value = res.images;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error('Failed to get images:', error);
|
|
275
|
+
isError.value = true;
|
|
276
|
+
errorMessage.value = `Failed to fetch images. Please, try to re-run the action.`;
|
|
277
|
+
// Handle error appropriately
|
|
278
|
+
}
|
|
191
279
|
}
|
|
192
280
|
|
|
193
|
-
function prepareDataForSave() {
|
|
281
|
+
async function prepareDataForSave() {
|
|
194
282
|
const checkedItems = selected.value
|
|
195
283
|
.filter(item => item.isChecked === true)
|
|
196
284
|
.map(item => {
|
|
@@ -200,51 +288,286 @@ function prepareDataForSave() {
|
|
|
200
288
|
const checkedItemsIDs = selected.value
|
|
201
289
|
.filter(item => item.isChecked === true)
|
|
202
290
|
.map(item => item[primaryKey]);
|
|
291
|
+
|
|
292
|
+
const promises = [];
|
|
293
|
+
for (const item of checkedItems) {
|
|
294
|
+
for (const [key, value] of Object.entries(item)) {
|
|
295
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
296
|
+
const p = convertImages(key, value).then(result => {
|
|
297
|
+
item[key] = result;
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
promises.push(p);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
await Promise.all(promises);
|
|
305
|
+
|
|
203
306
|
return [checkedItemsIDs, checkedItems];
|
|
204
307
|
}
|
|
205
308
|
|
|
309
|
+
async function convertImages(fieldName, img) {
|
|
310
|
+
let imgBlob;
|
|
311
|
+
if (img.startsWith('data:')) {
|
|
312
|
+
const base64 = img.split(',')[1];
|
|
313
|
+
const mimeType = img.split(';')[0].split(':')[1];
|
|
314
|
+
const byteCharacters = atob(base64);
|
|
315
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
316
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
317
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
318
|
+
}
|
|
319
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
320
|
+
imgBlob = new Blob([byteArray], { type: mimeType });
|
|
321
|
+
} else {
|
|
322
|
+
imgBlob = await fetch(
|
|
323
|
+
`/adminapi/v1/plugin/${props.meta.outputImagesPluginInstanceIds[fieldName]}/cors-proxy?url=${encodeURIComponent(img)}`
|
|
324
|
+
).then(res => { return res.blob() });
|
|
325
|
+
}
|
|
326
|
+
return imgBlob;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
206
330
|
async function analyzeFields() {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
331
|
+
try {
|
|
332
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
333
|
+
|
|
334
|
+
const res = await callAdminForthApi({
|
|
335
|
+
path: `/plugin/${props.meta.pluginInstanceId}/analyze`,
|
|
336
|
+
method: 'POST',
|
|
337
|
+
body: {
|
|
338
|
+
selectedIds: props.checkboxes,
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
343
|
+
|
|
344
|
+
res.result.forEach((item, idx) => {
|
|
345
|
+
const pk = selected.value[idx]?.[primaryKey]
|
|
346
|
+
|
|
347
|
+
if (pk) {
|
|
348
|
+
selected.value[idx] = {
|
|
349
|
+
...selected.value[idx],
|
|
350
|
+
...item,
|
|
351
|
+
isChecked: true,
|
|
352
|
+
[primaryKey]: pk
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
})
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error('Failed to analyze image(s):', error);
|
|
358
|
+
isError.value = true;
|
|
359
|
+
errorMessage.value = `Failed to fetch analyze image(s). Please, try to re-run the action.`;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
async function analyzeFieldsNoImages() {
|
|
365
|
+
try {
|
|
366
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
216
367
|
|
|
217
|
-
|
|
368
|
+
const res = await callAdminForthApi({
|
|
369
|
+
path: `/plugin/${props.meta.pluginInstanceId}/analyze_no_images`,
|
|
370
|
+
method: 'POST',
|
|
371
|
+
body: {
|
|
372
|
+
selectedIds: props.checkboxes,
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
if(!props.meta.isFieldsForAnalizeFromImages) {
|
|
376
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
res.result.forEach((item, idx) => {
|
|
380
|
+
const pk = selected.value[idx]?.[primaryKey]
|
|
218
381
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
382
|
+
if (pk) {
|
|
383
|
+
selected.value[idx] = {
|
|
384
|
+
...selected.value[idx],
|
|
385
|
+
...item,
|
|
386
|
+
isChecked: true,
|
|
387
|
+
[primaryKey]: pk
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error('Failed to analyze fields:', error);
|
|
393
|
+
isError.value = true;
|
|
394
|
+
errorMessage.value = `Failed to analyze fields. Please, try to re-run the action.`;
|
|
395
|
+
}
|
|
228
396
|
}
|
|
229
397
|
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
|
|
230
401
|
async function saveData() {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
402
|
+
if (!selected.value?.length) {
|
|
403
|
+
adminforth.alert({ message: 'No items selected', variant: 'warning' });
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
isLoading.value = true;
|
|
408
|
+
const [checkedItemsIDs, reqData] = await prepareDataForSave();
|
|
409
|
+
|
|
410
|
+
const imagesToUpload = [];
|
|
411
|
+
for (const item of reqData) {
|
|
412
|
+
for (const [key, value] of Object.entries(item)) {
|
|
413
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
414
|
+
const p = uploadImage(value, item[primaryKey], key).then(result => {
|
|
415
|
+
item[key] = result;
|
|
416
|
+
});
|
|
417
|
+
imagesToUpload.push(p);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
await Promise.all(imagesToUpload);
|
|
422
|
+
|
|
423
|
+
const res = await callAdminForthApi({
|
|
424
|
+
path: `/plugin/${props.meta.pluginInstanceId}/update_fields`,
|
|
425
|
+
method: 'POST',
|
|
426
|
+
body: {
|
|
427
|
+
selectedIds: checkedItemsIDs,
|
|
428
|
+
fields: reqData,
|
|
429
|
+
},
|
|
430
|
+
});
|
|
241
431
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
432
|
+
if(res.ok) {
|
|
433
|
+
confirmDialog.value.close();
|
|
434
|
+
props.updateList();
|
|
435
|
+
props.clearCheckboxes();
|
|
436
|
+
} else {
|
|
437
|
+
console.error('Error saving data:', res);
|
|
438
|
+
isError.value = true;
|
|
439
|
+
errorMessage.value = `Failed to save data. Please, try to re-run the action.`;
|
|
440
|
+
}
|
|
441
|
+
} catch (error) {
|
|
442
|
+
console.error('Error saving data:', error);
|
|
443
|
+
isError.value = true;
|
|
444
|
+
errorMessage.value = `Failed to save data. Please, try to re-run the action.`;
|
|
445
|
+
} finally {
|
|
446
|
+
isLoading.value = false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
async function generateImages() {
|
|
451
|
+
isAiResponseReceivedImage.value = props.checkboxes.map(() => false);
|
|
452
|
+
let res;
|
|
453
|
+
let error = null;
|
|
454
|
+
|
|
455
|
+
try {
|
|
456
|
+
res = await callAdminForthApi({
|
|
457
|
+
path: `/plugin/${props.meta.pluginInstanceId}/initial_image_generate`,
|
|
458
|
+
method: 'POST',
|
|
459
|
+
body: {
|
|
460
|
+
selectedIds: props.checkboxes,
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
} catch (e) {
|
|
464
|
+
console.error('Error generating images:', e);
|
|
465
|
+
isError.value = true;
|
|
466
|
+
isCriticalError.value = true;
|
|
467
|
+
errorMessage.value = `Failed to generate images. Please, try to re-run the action.`;
|
|
468
|
+
}
|
|
469
|
+
isAiResponseReceivedImage.value = props.checkboxes.map(() => true);
|
|
470
|
+
|
|
471
|
+
if (res?.error) {
|
|
472
|
+
error = res.error;
|
|
473
|
+
}
|
|
474
|
+
if (!res) {
|
|
475
|
+
error = 'Error generating images, something went wrong';
|
|
476
|
+
isError.value = true;
|
|
477
|
+
isCriticalError.value = true;
|
|
478
|
+
errorMessage.value = `Failed to generate images. Please, try to re-run the action.`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (error) {
|
|
482
|
+
adminforth.alert({
|
|
483
|
+
message: error,
|
|
484
|
+
variant: 'danger',
|
|
485
|
+
timeout: 'unlimited',
|
|
486
|
+
});
|
|
246
487
|
} else {
|
|
247
|
-
|
|
488
|
+
res.result.forEach((item, idx) => {
|
|
489
|
+
const pk = selected.value[idx]?.[primaryKey]
|
|
490
|
+
if (pk) {
|
|
491
|
+
selected.value[idx] = {
|
|
492
|
+
...selected.value[idx],
|
|
493
|
+
...item,
|
|
494
|
+
[primaryKey]: pk
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
})
|
|
248
498
|
}
|
|
249
499
|
}
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
async function uploadImage(imgBlob, id, fieldName) {
|
|
503
|
+
const file = new File([imgBlob], `generated_${fieldName}_${id}.${imgBlob.type.split('/').pop()}`, { type: imgBlob.type });
|
|
504
|
+
const { name, size, type } = file;
|
|
505
|
+
|
|
506
|
+
const extension = name.split('.').pop();
|
|
507
|
+
const nameNoExtension = name.replace(`.${extension}`, '');
|
|
508
|
+
|
|
509
|
+
try {
|
|
510
|
+
const { uploadUrl, uploadExtraParams, filePath, error } = await callAdminForthApi({
|
|
511
|
+
path: `/plugin/${props.meta.outputImagesPluginInstanceIds[fieldName]}/get_file_upload_url`,
|
|
512
|
+
method: 'POST',
|
|
513
|
+
body: {
|
|
514
|
+
originalFilename: nameNoExtension,
|
|
515
|
+
contentType: type,
|
|
516
|
+
size,
|
|
517
|
+
originalExtension: extension,
|
|
518
|
+
recordPk: route?.params?.primaryKey,
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
if (error) {
|
|
523
|
+
adminforth.alert({
|
|
524
|
+
message: t('File was not uploaded because of error: {error}', { error }),
|
|
525
|
+
variant: 'danger'
|
|
526
|
+
});
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const xhr = new XMLHttpRequest();
|
|
531
|
+
const success = await new Promise((resolve) => {
|
|
532
|
+
xhr.upload.onprogress = (e) => {
|
|
533
|
+
if (e.lengthComputable) {
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
xhr.addEventListener('loadend', () => {
|
|
537
|
+
const success = xhr.readyState === 4 && xhr.status === 200;
|
|
538
|
+
// try to read response
|
|
539
|
+
resolve(success);
|
|
540
|
+
});
|
|
541
|
+
xhr.open('PUT', uploadUrl, true);
|
|
542
|
+
xhr.setRequestHeader('Content-Type', type);
|
|
543
|
+
uploadExtraParams && Object.entries(uploadExtraParams).forEach(([key, value]: [string, string]) => {
|
|
544
|
+
xhr.setRequestHeader(key, value);
|
|
545
|
+
})
|
|
546
|
+
xhr.send(file);
|
|
547
|
+
});
|
|
548
|
+
if (!success) {
|
|
549
|
+
adminforth.alert({
|
|
550
|
+
messageHtml: `<div>${t('Sorry but the file was not uploaded because of internal storage Request Error:')}</div>
|
|
551
|
+
<pre style="white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; max-width: 100%;">${
|
|
552
|
+
xhr.responseText.replace(/</g, '<').replace(/>/g, '>')
|
|
553
|
+
}</pre>`,
|
|
554
|
+
variant: 'danger',
|
|
555
|
+
timeout: 30,
|
|
556
|
+
});
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
return filePath;
|
|
560
|
+
} catch (error) {
|
|
561
|
+
console.error('Error uploading file:', error);
|
|
562
|
+
adminforth.alert({
|
|
563
|
+
message: 'Sorry but the file was not be uploaded. Please try again.',
|
|
564
|
+
variant: 'danger'
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
isError.value = true;
|
|
568
|
+
errorMessage.value = `Failed to upload images. Please, try to re-run the action.`;
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
250
573
|
</script>
|