3333            <Button  
3434              class =" w-64" 
3535              @click =" saveData" 
36+               :disabled =" isLoading" 
37+               :loader =" isLoading" 
3638            >
3739            {{ props.checkboxes.length > 1 ? 'Save fields' : 'Save field' }}
3840            </Button >
@@ -49,12 +51,18 @@ import { ref, watch } from 'vue'
4951import  { Dialog , Button  } from  ' @/afcl' 
5052import  VisionTable  from  ' ./visionTable.vue' 
5153import  adminforth  from  ' @/adminforth' 
54+ import  { useI18n  } from  ' vue-i18n' 
55+ import  { useRoute  } from  ' vue-router' 
56+ import  { type  AdminUser , type  AdminForthResourceCommon  } from  ' @/types' 
57+ 
58+ const =  useRoute ();
59+ const =  useI18n ();
5260
5361const =  defineProps <{
5462  checkboxes:  any , 
5563  meta:  any , 
56-   resource:  any , 
57-   adminUser:  any , 
64+   resource:  AdminForthResourceCommon , 
65+   adminUser:  AdminUser , 
5866  updateList:  { 
5967    type:  Function , 
6068    required:  true  
@@ -76,6 +84,7 @@ const isAiResponseReceived = ref([]);
7684const =  ref ([]);
7785const =  props .meta .primaryKey ;
7886const =  ref ([]);
87+ const =  ref (false );
7988
8089const =  async  () =>  {
8190  confirmDialog .value .open (); 
@@ -94,20 +103,28 @@ const openDialog = async () => {
94103    return  acc ; 
95104  },{[primaryKey ]: records .value [i ][primaryKey ]} as  Record <string , boolean >); 
96105  } 
106+   isLoading .value  =  true ; 
97107  await  Promise .all ([ 
98108    // analyzeFields(), 
99109    generateImages () 
100110  ]); 
111+   isLoading .value  =  false ; 
101112} 
102113  
103- watch (selected , (val ) =>  {
104-   console .log (' Selected changed:' val ); 
105- }, { deep: true  }); 
114+ //   watch(selected, (val) => {
115+ //     console.log('Selected changed:', val);
116+ //   }, { deep: true });
106117
107118const =  () =>  {
108119  confirmDialog .value .close (); 
109120  isAiResponseReceived .value  =  []; 
110121  isAiResponseReceivedImage .value  =  []; 
122+ 
123+   records .value  =  []; 
124+   images .value  =  []; 
125+   selected .value  =  []; 
126+   tableColumns .value  =  []; 
127+   tableColumnsIndexes .value  =  []; 
111128} 
112129
113130function  formatLabel(str ) {
@@ -185,29 +202,38 @@ function isInColumnEnum(key: string): boolean {
185202} 
186203
187204async  function  getRecords() {
188-   const =  await  callAdminForthApi ({ 
189-     path: ` /plugin/${props .meta .pluginInstanceId }/get_records ` , 
190-     method: ' POST'  
191-     body: { 
192-       record: props .checkboxes , 
193-     }, 
194-   }); 
195-   records .value  =  res .records ; 
205+   try  { 
206+     const =  await  callAdminForthApi ({ 
207+       path: ` /plugin/${props .meta .pluginInstanceId }/get_records ` , 
208+       method: ' POST'  
209+       body: { 
210+         record: props .checkboxes , 
211+       }, 
212+     }); 
213+     records .value  =  res .records ; 
214+   } catch  (error ) { 
215+     console .error (' Failed to get records:' error ); 
216+     //  Handle error appropriately 
217+   } 
196218} 
197219
198220async  function  getImages() {
199-   const =  await  callAdminForthApi ({ 
200-     path: ` /plugin/${props .meta .pluginInstanceId }/get_images ` , 
201-     method: ' POST'  
202-     body: { 
203-       record: records .value , 
204-     }, 
205-   }); 
206- 
207-   images .value  =  res .images ; 
221+   try  { 
222+     const =  await  callAdminForthApi ({ 
223+       path: ` /plugin/${props .meta .pluginInstanceId }/get_images ` , 
224+       method: ' POST'  
225+       body: { 
226+         record: records .value , 
227+       }, 
228+     }); 
229+     images .value  =  res .images ; 
230+   } catch  (error ) { 
231+     console .error (' Failed to get images:' error ); 
232+     //  Handle error appropriately 
233+   } 
208234} 
209235
210- function  prepareDataForSave() {
236+ async   function  prepareDataForSave() {
211237  const =  selected .value  
212238    .filter (item  =>  item .isChecked  ===  true ) 
213239    .map (item  =>  { 
@@ -217,55 +243,119 @@ function prepareDataForSave() {
217243  const =  selected .value  
218244    .filter (item  =>  item .isChecked  ===  true ) 
219245    .map (item  =>  item [primaryKey ]); 
246+ 
247+   const =  []; 
248+   for  (const of  checkedItems ) { 
249+     for  (const of  Object .entries (item )) { 
250+       if (props .meta .outputImageFields ?.includes (key )) { 
251+         const =  convertImages (key , value ).then (result  =>  { 
252+           item [key ] =  result ; 
253+         }); 
254+        
255+         promises .push (p ); 
256+       } 
257+     } 
258+   } 
259+   await  Promise .all (promises ); 
260+ 
220261  return  [checkedItemsIDs , checkedItems ]; 
221262} 
222263
264+ async  function  convertImages(fieldName , img ) {
265+   let  imgBlob; 
266+   if  (img .startsWith (' data:'  
267+     const =  img .split (' ,' 1 ]; 
268+     const =  img .split (' ;' 0 ].split (' :' 1 ]; 
269+     const =  atob (base64 ); 
270+     const =  new  Array (byteCharacters .length ); 
271+     for  (let  i =  0 ; i  <  byteCharacters .length ; i ++ ) { 
272+       byteNumbers [i ] =  byteCharacters .charCodeAt (i ); 
273+     } 
274+     const =  new  Uint8Array (byteNumbers ); 
275+     imgBlob  =  new  Blob ([byteArray ], { type: mimeType  }); 
276+   } else  { 
277+     imgBlob  =  await  fetch ( 
278+       ` /adminapi/v1/plugin/${props .meta .outputImagesPluginInstanceIds [fieldName ]}/cors-proxy?url=${encodeURIComponent (img )} `  
279+     ).then (res  =>  { return  res .blob () }); 
280+   } 
281+   return  imgBlob ; 
282+ } 
283+ 
284+ 
223285async  function  analyzeFields() {
224-   isAiResponseReceived .value  =  props .checkboxes .map (() =>  false ); 
225- 
226-   const =  await  callAdminForthApi ({ 
227-     path: ` /plugin/${props .meta .pluginInstanceId }/analyze ` , 
228-     method: ' POST'  
229-     body: { 
230-       selectedIds: props .checkboxes , 
231-     }, 
232-   }); 
286+   try  { 
287+     isAiResponseReceived .value  =  props .checkboxes .map (() =>  false ); 
233288
234-   isAiResponseReceived .value  =  props .checkboxes .map (() =>  true ); 
289+     const =  await  callAdminForthApi ({ 
290+       path: ` /plugin/${props .meta .pluginInstanceId }/analyze ` , 
291+       method: ' POST'  
292+       body: { 
293+         selectedIds: props .checkboxes , 
294+       }, 
295+     }); 
296+ 
297+     isAiResponseReceived .value  =  props .checkboxes .map (() =>  true ); 
235298
236-   res .result .forEach ((item , idx ) =>  { 
237-     const =  selected .value [idx ]?.[primaryKey ] 
299+      res .result .forEach ((item , idx ) =>  { 
300+        const =  selected .value [idx ]?.[primaryKey ] 
238301
239-     if  (pk ) { 
240-       selected .value [idx ] =  { 
241-         ... selected .value [idx ], 
242-         ... item , 
243-         isChecked: true , 
244-         [primaryKey ]: pk  
302+       if  (pk ) { 
303+         selected .value [idx ] =  { 
304+           ... selected .value [idx ], 
305+           ... item , 
306+           isChecked: true , 
307+           [primaryKey ]: pk  
308+         } 
245309      } 
246-     } 
247-   }) 
310+     }) 
311+   } catch  (error ) { 
312+     console .error (' Failed to get records:' error ); 
248313
314+   } 
249315} 
250316
251317async  function  saveData() {
252-   const =  prepareDataForSave (); 
253- 
254-   const =  await  callAdminForthApi ({ 
255-     path: ` /plugin/${props .meta .pluginInstanceId }/update_fields ` , 
256-     method: ' POST'  
257-     body: { 
258-       selectedIds: checkedItemsIDs , 
259-       fields: reqData , 
260-     }, 
261-   }); 
318+   if  (! selected .value ?.length ) { 
319+     adminforth .alert ({ message: ' No items selected' ' warning'  
320+     return ; 
321+   } 
322+   try  { 
323+     isLoading .value  =  true ; 
324+     const =  await  prepareDataForSave (); 
262325
263-   if (res .ok ) { 
264-     confirmDialog .value .close (); 
265-     props .updateList (); 
266-     props .clearCheckboxes (); 
267-   } else  { 
268-     console .error (' Error saving data:' res ); 
326+     const =  []; 
327+     for  (const of  reqData ) { 
328+       for  (const of  Object .entries (item )) { 
329+         if (props .meta .outputImageFields ?.includes (key )) { 
330+           const =  uploadImage (value , item [primaryKey ], key ).then (result  =>  { 
331+             item [key ] =  result ; 
332+           }); 
333+           imagesToUpload .push (p ); 
334+         } 
335+       } 
336+     } 
337+     await  Promise .all (imagesToUpload ); 
338+ 
339+     const =  await  callAdminForthApi ({ 
340+       path: ` /plugin/${props .meta .pluginInstanceId }/update_fields ` , 
341+       method: ' POST'  
342+       body: { 
343+         selectedIds: checkedItemsIDs , 
344+         fields: reqData , 
345+       }, 
346+     }); 
347+ 
348+     if (res .ok ) { 
349+       confirmDialog .value .close (); 
350+       props .updateList (); 
351+       props .clearCheckboxes (); 
352+     } else  { 
353+       console .error (' Error saving data:' res ); 
354+     } 
355+   } catch  (error ) { 
356+     console .error (' Error saving data:' error ); 
357+   } finally  { 
358+     isLoading .value  =  false ; 
269359  } 
270360} 
271361
@@ -314,4 +404,73 @@ async function generateImages() {
314404  } 
315405} 
316406
407+ 
408+ async  function  uploadImage(imgBlob , id , fieldName ) {
409+   const =  new  File ([imgBlob ], ` generated_${fieldName }_${id }.${imgBlob .type } ` , { type: imgBlob .type  }); 
410+   const =  file ; 
411+    
412+   const =  name .split (' .' pop (); 
413+   const =  name .replace (` .${extension } ` , ' '  
414+ 
415+   try  { 
416+     const =  await  callAdminForthApi ({ 
417+         path: ` /plugin/${props .meta .outputImagesPluginInstanceIds [fieldName ]}/get_file_upload_url ` , 
418+         method: ' POST'  
419+         body: { 
420+           originalFilename: nameNoExtension , 
421+           contentType: type , 
422+           size , 
423+           originalExtension: extension , 
424+           recordPk: route ?.params ?.primaryKey , 
425+         }, 
426+     }); 
427+ 
428+     if  (error ) { 
429+       adminforth .alert ({ 
430+         message: t (' File was not uploaded because of error: {error}' error  }), 
431+         variant: ' danger'  
432+       }); 
433+       return ; 
434+     } 
435+ 
436+     const =  new  XMLHttpRequest (); 
437+     const =  await  new  Promise ((resolve ) =>  { 
438+       xhr .upload .onprogress  =  (e ) =>  { 
439+         if  (e .lengthComputable ) { 
440+         } 
441+       }; 
442+       xhr .addEventListener (' loadend' =>  { 
443+         const =  xhr .readyState  ===  4  &&  xhr .status  ===  200 ; 
444+         //  try to read response 
445+         resolve (success ); 
446+       }); 
447+       xhr .open (' PUT' uploadUrl , true ); 
448+       xhr .setRequestHeader (' Content-Type' type ); 
449+       uploadExtraParams  &&  Object .entries (uploadExtraParams ).forEach (([key , value ]:  [string , string ]) =>  { 
450+         xhr .setRequestHeader (key , value ); 
451+       }) 
452+       xhr .send (file ); 
453+     }); 
454+     if  (! success ) { 
455+       adminforth .alert ({ 
456+         messageHtml: ` <div>${t (' Sorry but the file was not uploaded because of internal storage Request Error:'   
457+         <pre style="white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; max-width: 100%;">${ 
458+           xhr .responseText .replace (/ </ g ' <' replace (/ >/ g ' >'  
459+         }</pre> ` ,
460+         variant: ' danger'  
461+         timeout: 30 , 
462+       }); 
463+       return ; 
464+     } 
465+     return  filePath ; 
466+   } catch  (error ) { 
467+     console .error (' Error uploading file:' error ); 
468+     adminforth .alert ({ 
469+       message: ' Sorry but the file was not be uploaded. Please try again.'  
470+       variant: ' danger'  
471+     }); 
472+     return  null ; 
473+   } 
474+ } 
475+ 
317476script >
0 commit comments