@adminforth/bulk-ai-flow 1.1.4 → 1.2.0
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 +400 -80
- package/custom/visionTable.vue +56 -4
- package/dist/custom/imageGenerationCarousel.vue +462 -0
- package/dist/custom/visionAction.vue +400 -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
|
@@ -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="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="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">{{ "Network error, please try again" }}</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"
|
|
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,65 @@ 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 errorMessage = ref('');
|
|
110
|
+
const checkedCount = ref(0);
|
|
72
111
|
|
|
73
112
|
const openDialog = async () => {
|
|
74
113
|
confirmDialog.value.open();
|
|
75
114
|
await getRecords();
|
|
76
|
-
|
|
115
|
+
if (props.meta.isFieldsForAnalizeFromImages || props.meta.isImageGeneration) {
|
|
116
|
+
await getImages();
|
|
117
|
+
}
|
|
77
118
|
tableHeaders.value = generateTableHeaders(props.meta.outputFields);
|
|
78
119
|
const result = generateTableColumns();
|
|
79
120
|
tableColumns.value = result.tableData;
|
|
80
121
|
tableColumnsIndexes.value = result.indexes;
|
|
81
|
-
|
|
82
|
-
customFieldNames.value = tableHeaders.value.slice(3).map(h => h.fieldName);
|
|
122
|
+
customFieldNames.value = tableHeaders.value.slice(props.meta.isFieldsForAnalizeFromImages ? 3 : 2).map(h => h.fieldName);
|
|
83
123
|
setSelected();
|
|
84
|
-
|
|
124
|
+
for (let i = 0; i < selected.value?.length; i++) {
|
|
125
|
+
openGenerationCarousel.value[i] = props.meta.outputImageFields?.reduce((acc,key) =>{
|
|
126
|
+
acc[key] = false;
|
|
127
|
+
return acc;
|
|
128
|
+
},{[primaryKey]: records.value[i][primaryKey]} as Record<string, boolean>);
|
|
129
|
+
}
|
|
130
|
+
isLoading.value = true;
|
|
131
|
+
const tasks = [];
|
|
132
|
+
if (props.meta.isFieldsForAnalizeFromImages) {
|
|
133
|
+
tasks.push(analyzeFields());
|
|
134
|
+
}
|
|
135
|
+
if (props.meta.isFieldsForAnalizePlain) {
|
|
136
|
+
tasks.push(analyzeFieldsNoImages());
|
|
137
|
+
}
|
|
138
|
+
if (props.meta.isImageGeneration) {
|
|
139
|
+
tasks.push(generateImages());
|
|
140
|
+
}
|
|
141
|
+
await Promise.all(tasks);
|
|
142
|
+
isLoading.value = false;
|
|
85
143
|
}
|
|
86
144
|
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
|
|
145
|
+
watch(selected, (val) => {
|
|
146
|
+
//console.log('Selected changed:', val);
|
|
147
|
+
checkedCount.value = val.filter(item => item.isChecked === true).length;
|
|
148
|
+
}, { deep: true });
|
|
90
149
|
|
|
91
150
|
const closeDialog = () => {
|
|
92
151
|
confirmDialog.value.close();
|
|
93
|
-
|
|
152
|
+
isAiResponseReceivedAnalize.value = [];
|
|
153
|
+
isAiResponseReceivedImage.value = [];
|
|
154
|
+
|
|
155
|
+
records.value = [];
|
|
156
|
+
images.value = [];
|
|
157
|
+
selected.value = [];
|
|
158
|
+
tableColumns.value = [];
|
|
159
|
+
tableColumnsIndexes.value = [];
|
|
160
|
+
isError.value = false;
|
|
161
|
+
errorMessage.value = '';
|
|
94
162
|
}
|
|
95
163
|
|
|
96
164
|
function formatLabel(str) {
|
|
@@ -105,8 +173,9 @@ function generateTableHeaders(outputFields) {
|
|
|
105
173
|
|
|
106
174
|
headers.push({ label: 'Checkboxes', fieldName: 'checkboxes' });
|
|
107
175
|
headers.push({ label: 'Field name', fieldName: 'label' });
|
|
108
|
-
|
|
109
|
-
|
|
176
|
+
if (props.meta.isFieldsForAnalizeFromImages) {
|
|
177
|
+
headers.push({ label: 'Source Images', fieldName: 'images' });
|
|
178
|
+
}
|
|
110
179
|
for (const key in outputFields) {
|
|
111
180
|
headers.push({
|
|
112
181
|
label: formatLabel(key),
|
|
@@ -145,7 +214,7 @@ function setSelected() {
|
|
|
145
214
|
selected.value = records.value.map(() => ({}));
|
|
146
215
|
records.value.forEach((record, index) => {
|
|
147
216
|
for (const key in props.meta.outputFields) {
|
|
148
|
-
if(isInColumnEnum(key)){
|
|
217
|
+
if (isInColumnEnum(key)) {
|
|
149
218
|
const colEnum = props.meta.columnEnums.find(c => c.name === key);
|
|
150
219
|
const object = colEnum.enum.find(item => item.value === record[key]);
|
|
151
220
|
selected.value[index][key] = object ? record[key] : null;
|
|
@@ -155,7 +224,7 @@ function setSelected() {
|
|
|
155
224
|
}
|
|
156
225
|
selected.value[index].isChecked = true;
|
|
157
226
|
selected.value[index][primaryKey] = record[primaryKey];
|
|
158
|
-
|
|
227
|
+
isAiResponseReceivedAnalize.value[index] = true;
|
|
159
228
|
});
|
|
160
229
|
}
|
|
161
230
|
|
|
@@ -167,30 +236,48 @@ function isInColumnEnum(key: string): boolean {
|
|
|
167
236
|
return true;
|
|
168
237
|
}
|
|
169
238
|
|
|
239
|
+
function handleTableError(errorData) {
|
|
240
|
+
isError.value = errorData.isError;
|
|
241
|
+
errorMessage.value = errorData.errorMessage;
|
|
242
|
+
}
|
|
243
|
+
|
|
170
244
|
async function getRecords() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
245
|
+
try {
|
|
246
|
+
const res = await callAdminForthApi({
|
|
247
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get_records`,
|
|
248
|
+
method: 'POST',
|
|
249
|
+
body: {
|
|
250
|
+
record: props.checkboxes,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
records.value = res.records;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error('Failed to get records:', error);
|
|
256
|
+
isError.value = true;
|
|
257
|
+
errorMessage.value = `Failed to get records ${error}. Please, try to re-run the action.`;
|
|
258
|
+
// Handle error appropriately
|
|
259
|
+
}
|
|
179
260
|
}
|
|
180
261
|
|
|
181
262
|
async function getImages() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
263
|
+
try {
|
|
264
|
+
const res = await callAdminForthApi({
|
|
265
|
+
path: `/plugin/${props.meta.pluginInstanceId}/get_images`,
|
|
266
|
+
method: 'POST',
|
|
267
|
+
body: {
|
|
268
|
+
record: records.value,
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
images.value = res.images;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error('Failed to get images:', error);
|
|
274
|
+
isError.value = true;
|
|
275
|
+
errorMessage.value = `Failed to get images ${error}. Please, try to re-run the action.`;
|
|
276
|
+
// Handle error appropriately
|
|
277
|
+
}
|
|
191
278
|
}
|
|
192
279
|
|
|
193
|
-
function prepareDataForSave() {
|
|
280
|
+
async function prepareDataForSave() {
|
|
194
281
|
const checkedItems = selected.value
|
|
195
282
|
.filter(item => item.isChecked === true)
|
|
196
283
|
.map(item => {
|
|
@@ -200,51 +287,284 @@ function prepareDataForSave() {
|
|
|
200
287
|
const checkedItemsIDs = selected.value
|
|
201
288
|
.filter(item => item.isChecked === true)
|
|
202
289
|
.map(item => item[primaryKey]);
|
|
290
|
+
|
|
291
|
+
const promises = [];
|
|
292
|
+
for (const item of checkedItems) {
|
|
293
|
+
for (const [key, value] of Object.entries(item)) {
|
|
294
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
295
|
+
const p = convertImages(key, value).then(result => {
|
|
296
|
+
item[key] = result;
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
promises.push(p);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
await Promise.all(promises);
|
|
304
|
+
|
|
203
305
|
return [checkedItemsIDs, checkedItems];
|
|
204
306
|
}
|
|
205
307
|
|
|
308
|
+
async function convertImages(fieldName, img) {
|
|
309
|
+
let imgBlob;
|
|
310
|
+
if (img.startsWith('data:')) {
|
|
311
|
+
const base64 = img.split(',')[1];
|
|
312
|
+
const mimeType = img.split(';')[0].split(':')[1];
|
|
313
|
+
const byteCharacters = atob(base64);
|
|
314
|
+
const byteNumbers = new Array(byteCharacters.length);
|
|
315
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
316
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
317
|
+
}
|
|
318
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
319
|
+
imgBlob = new Blob([byteArray], { type: mimeType });
|
|
320
|
+
} else {
|
|
321
|
+
imgBlob = await fetch(
|
|
322
|
+
`/adminapi/v1/plugin/${props.meta.outputImagesPluginInstanceIds[fieldName]}/cors-proxy?url=${encodeURIComponent(img)}`
|
|
323
|
+
).then(res => { return res.blob() });
|
|
324
|
+
}
|
|
325
|
+
return imgBlob;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
206
329
|
async function analyzeFields() {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
330
|
+
try {
|
|
331
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
332
|
+
|
|
333
|
+
const res = await callAdminForthApi({
|
|
334
|
+
path: `/plugin/${props.meta.pluginInstanceId}/analyze`,
|
|
335
|
+
method: 'POST',
|
|
336
|
+
body: {
|
|
337
|
+
selectedIds: props.checkboxes,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
342
|
+
|
|
343
|
+
res.result.forEach((item, idx) => {
|
|
344
|
+
const pk = selected.value[idx]?.[primaryKey]
|
|
345
|
+
|
|
346
|
+
if (pk) {
|
|
347
|
+
selected.value[idx] = {
|
|
348
|
+
...selected.value[idx],
|
|
349
|
+
...item,
|
|
350
|
+
isChecked: true,
|
|
351
|
+
[primaryKey]: pk
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error('Failed to get records:', error);
|
|
357
|
+
isError.value = true;
|
|
358
|
+
errorMessage.value = `Failed to get records ${error}. Please, try to re-run the action.`;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
async function analyzeFieldsNoImages() {
|
|
364
|
+
try {
|
|
365
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => false);
|
|
216
366
|
|
|
217
|
-
|
|
367
|
+
const res = await callAdminForthApi({
|
|
368
|
+
path: `/plugin/${props.meta.pluginInstanceId}/analyze_no_images`,
|
|
369
|
+
method: 'POST',
|
|
370
|
+
body: {
|
|
371
|
+
selectedIds: props.checkboxes,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
if(!props.meta.isFieldsForAnalizeFromImages) {
|
|
375
|
+
isAiResponseReceivedAnalize.value = props.checkboxes.map(() => true);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
res.result.forEach((item, idx) => {
|
|
379
|
+
const pk = selected.value[idx]?.[primaryKey]
|
|
218
380
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
381
|
+
if (pk) {
|
|
382
|
+
selected.value[idx] = {
|
|
383
|
+
...selected.value[idx],
|
|
384
|
+
...item,
|
|
385
|
+
isChecked: true,
|
|
386
|
+
[primaryKey]: pk
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
})
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.error('Failed to get records:', error);
|
|
392
|
+
isError.value = true;
|
|
393
|
+
errorMessage.value = `Failed to get records ${error}. Please, try to re-run the action.`;
|
|
394
|
+
}
|
|
228
395
|
}
|
|
229
396
|
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
|
|
230
400
|
async function saveData() {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
401
|
+
if (!selected.value?.length) {
|
|
402
|
+
adminforth.alert({ message: 'No items selected', variant: 'warning' });
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
isLoading.value = true;
|
|
407
|
+
const [checkedItemsIDs, reqData] = await prepareDataForSave();
|
|
408
|
+
|
|
409
|
+
const imagesToUpload = [];
|
|
410
|
+
for (const item of reqData) {
|
|
411
|
+
for (const [key, value] of Object.entries(item)) {
|
|
412
|
+
if(props.meta.outputImageFields?.includes(key)) {
|
|
413
|
+
const p = uploadImage(value, item[primaryKey], key).then(result => {
|
|
414
|
+
item[key] = result;
|
|
415
|
+
});
|
|
416
|
+
imagesToUpload.push(p);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
await Promise.all(imagesToUpload);
|
|
421
|
+
|
|
422
|
+
const res = await callAdminForthApi({
|
|
423
|
+
path: `/plugin/${props.meta.pluginInstanceId}/update_fields`,
|
|
424
|
+
method: 'POST',
|
|
425
|
+
body: {
|
|
426
|
+
selectedIds: checkedItemsIDs,
|
|
427
|
+
fields: reqData,
|
|
428
|
+
},
|
|
429
|
+
});
|
|
241
430
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
431
|
+
if(res.ok) {
|
|
432
|
+
confirmDialog.value.close();
|
|
433
|
+
props.updateList();
|
|
434
|
+
props.clearCheckboxes();
|
|
435
|
+
} else {
|
|
436
|
+
console.error('Error saving data:', res);
|
|
437
|
+
isError.value = true;
|
|
438
|
+
errorMessage.value = `Failed to save data ${res}. Please, try to re-run the action.`;
|
|
439
|
+
}
|
|
440
|
+
} catch (error) {
|
|
441
|
+
console.error('Error saving data:', error);
|
|
442
|
+
isError.value = true;
|
|
443
|
+
errorMessage.value = `Failed to save data ${error}. Please, try to re-run the action.`;
|
|
444
|
+
} finally {
|
|
445
|
+
isLoading.value = false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async function generateImages() {
|
|
450
|
+
isAiResponseReceivedImage.value = props.checkboxes.map(() => false);
|
|
451
|
+
let res;
|
|
452
|
+
let error = null;
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
res = await callAdminForthApi({
|
|
456
|
+
path: `/plugin/${props.meta.pluginInstanceId}/initial_image_generate`,
|
|
457
|
+
method: 'POST',
|
|
458
|
+
body: {
|
|
459
|
+
selectedIds: props.checkboxes,
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
} catch (e) {
|
|
463
|
+
console.error('Error generating images:', e);
|
|
464
|
+
isError.value = true;
|
|
465
|
+
errorMessage.value = `Failed to generate images ${e}. Please, try to re-run the action.`;
|
|
466
|
+
}
|
|
467
|
+
isAiResponseReceivedImage.value = props.checkboxes.map(() => true);
|
|
468
|
+
|
|
469
|
+
if (res?.error) {
|
|
470
|
+
error = res.error;
|
|
471
|
+
}
|
|
472
|
+
if (!res) {
|
|
473
|
+
error = 'Error generating images, something went wrong';
|
|
474
|
+
isError.value = true;
|
|
475
|
+
errorMessage.value = `Failed to generate images ${e}. Please, try to re-run the action.`;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (error) {
|
|
479
|
+
adminforth.alert({
|
|
480
|
+
message: error,
|
|
481
|
+
variant: 'danger',
|
|
482
|
+
timeout: 'unlimited',
|
|
483
|
+
});
|
|
246
484
|
} else {
|
|
247
|
-
|
|
485
|
+
res.result.forEach((item, idx) => {
|
|
486
|
+
const pk = selected.value[idx]?.[primaryKey]
|
|
487
|
+
if (pk) {
|
|
488
|
+
selected.value[idx] = {
|
|
489
|
+
...selected.value[idx],
|
|
490
|
+
...item,
|
|
491
|
+
[primaryKey]: pk
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
})
|
|
248
495
|
}
|
|
249
496
|
}
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
async function uploadImage(imgBlob, id, fieldName) {
|
|
500
|
+
const file = new File([imgBlob], `generated_${fieldName}_${id}.${imgBlob.type.split('/').pop()}`, { type: imgBlob.type });
|
|
501
|
+
const { name, size, type } = file;
|
|
502
|
+
|
|
503
|
+
const extension = name.split('.').pop();
|
|
504
|
+
const nameNoExtension = name.replace(`.${extension}`, '');
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
const { uploadUrl, uploadExtraParams, filePath, error } = await callAdminForthApi({
|
|
508
|
+
path: `/plugin/${props.meta.outputImagesPluginInstanceIds[fieldName]}/get_file_upload_url`,
|
|
509
|
+
method: 'POST',
|
|
510
|
+
body: {
|
|
511
|
+
originalFilename: nameNoExtension,
|
|
512
|
+
contentType: type,
|
|
513
|
+
size,
|
|
514
|
+
originalExtension: extension,
|
|
515
|
+
recordPk: route?.params?.primaryKey,
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
if (error) {
|
|
520
|
+
adminforth.alert({
|
|
521
|
+
message: t('File was not uploaded because of error: {error}', { error }),
|
|
522
|
+
variant: 'danger'
|
|
523
|
+
});
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const xhr = new XMLHttpRequest();
|
|
528
|
+
const success = await new Promise((resolve) => {
|
|
529
|
+
xhr.upload.onprogress = (e) => {
|
|
530
|
+
if (e.lengthComputable) {
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
xhr.addEventListener('loadend', () => {
|
|
534
|
+
const success = xhr.readyState === 4 && xhr.status === 200;
|
|
535
|
+
// try to read response
|
|
536
|
+
resolve(success);
|
|
537
|
+
});
|
|
538
|
+
xhr.open('PUT', uploadUrl, true);
|
|
539
|
+
xhr.setRequestHeader('Content-Type', type);
|
|
540
|
+
uploadExtraParams && Object.entries(uploadExtraParams).forEach(([key, value]: [string, string]) => {
|
|
541
|
+
xhr.setRequestHeader(key, value);
|
|
542
|
+
})
|
|
543
|
+
xhr.send(file);
|
|
544
|
+
});
|
|
545
|
+
if (!success) {
|
|
546
|
+
adminforth.alert({
|
|
547
|
+
messageHtml: `<div>${t('Sorry but the file was not uploaded because of internal storage Request Error:')}</div>
|
|
548
|
+
<pre style="white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; max-width: 100%;">${
|
|
549
|
+
xhr.responseText.replace(/</g, '<').replace(/>/g, '>')
|
|
550
|
+
}</pre>`,
|
|
551
|
+
variant: 'danger',
|
|
552
|
+
timeout: 30,
|
|
553
|
+
});
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
return filePath;
|
|
557
|
+
} catch (error) {
|
|
558
|
+
console.error('Error uploading file:', error);
|
|
559
|
+
adminforth.alert({
|
|
560
|
+
message: 'Sorry but the file was not be uploaded. Please try again.',
|
|
561
|
+
variant: 'danger'
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
isError.value = true;
|
|
565
|
+
errorMessage.value = `Failed to upload images. Please, try to re-run the action.`;
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
250
570
|
</script>
|