Requisito Toscano

This commit is contained in:
Florinda
2025-01-29 11:08:58 +01:00
parent 36a9811f05
commit b590508ca4
8 changed files with 1146 additions and 77 deletions

View File

@@ -33,13 +33,15 @@
<template #header>
<div class="flex items-center gap-2">
<span class="font-bold">Execution Input for ID {{execution_id}}</span>
<div v-if="rating!=null" class="flex justify-end">
</div>
</template>
<template #icons>
<div v-if="updateLoading" class="flex justify-end">
<Rating
:modelValue="rating"
:stars="5"
:readonly="true"
@change="updateRating($event)"
/>
</div>
<div v-else class="flex justify-end">
@@ -47,23 +49,50 @@
:modelValue="rating"
:stars="5"
:readonly="true"
@change="updateRating($event)"
/>
</div>
</div>
</template>
<div class="box p-4 border rounded-md shadow-sm" style="background-color: white;">
<div v-for="(input, index) in inputs" :key="index" class="input-container">
<div class="input-wrapper">
<span class="font-bold">{{ index.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase()) }}:</span>
<span>{{ input }}</span>
</div>
</div>
<table class="table-auto w-full border-collapse border border-gray-300">
<tbody>
<tr v-for="(input, index) in inputs" :key="index">
<th v-if="index === 'MultiFileUpload'" class="border border-gray-300 px-4 py-2">
Files Uploaded
</th>
<th v-else-if="index === 'SingleFileUpload'" class="border border-gray-300 px-4 py-2 bg-gray-500 text-white">
Parameter
</th>
<th v-else class="border border-gray-300 px-4 py-2">
{{ index.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase()) }}
</th>
<td class="border border-gray-300 px-4 py-2">
<div v-if="index === 'MultiFileUpload'">
<div v-if="fileNames.length">
<ul>
<li v-for="(file, idx) in fileNames" :key="idx" class="file-item">
{{ file.substring(file.lastIndexOf('/') + 1) }}
<Button
icon="pi pi-download"
class="p-button-text p-button-sm"
label="Download"
@click="downloadFileInput(file)"
/>
</li>
</ul>
</div>
<div v-else>
<p>No files found in the zip.</p>
</div>
</div>
<div v-else-if="index !== 'SingleFileUpload'">{{ input }}</div>
</td>
</tr>
</tbody>
</table>
</div>
</Panel>
<Panel class="mt-6">
@@ -84,8 +113,34 @@
<div v-if="scenario.outputType == 'ciaOutput'">
<ChangeImpactOutputViewer :scenario_output="scenario_output" />
</div>
<div v-else-if="loadingStore.exectuion_loading && loadingStore.getExecIdLoading===execution_id">
<div class="flex justify-center mt-4">
<jellyfish-loader :loading="loadingStore.exectuion_loading" scale="1" color="#A100FF" />
</div>
</div>
<div v-else>
<div v-if="fileType == 'FILE'">
<ul>
<li class="file-item">
sf_document-{{execution_id}}
<Button
icon="pi pi-download"
class="p-button-text p-button-sm"
label="Download"
@click="downloadFile(scenario_output)"
/>
</li>
</ul>
</div>
<div v-else-if="fileType == 'MARKDOWN'">
<div v-html="fileContent" class="markdown-content"></div>
</div>
<div v-else-if="fileType == 'JSON'">
<pre>{{ fileContent }}</pre>
</div>
<div v-else>
<MdPreview class="editor" v-model="scenario_output" language="en-US" />
</div>
</div>
</div>
</Panel>
@@ -107,17 +162,23 @@
<script setup>
import ChangeImpactOutputViewer from '@/components/ChangeImpactOutputViewer.vue';
import { LoadingStore } from '@/stores/LoadingStore.js';
import axios from 'axios';
import JsonEditorVue from 'json-editor-vue';
import JSZip from 'jszip';
import { marked } from 'marked';
import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import ProgressSpinner from 'primevue/progressspinner';
import { useToast } from 'primevue/usetoast';
import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { JellyfishLoader } from "vue3-spinner";
import { ScenarioService } from '../../service/ScenarioService.js';
import { ScenarioExecutionStore } from '../../stores/ScenarioExecutionStore.js';
import { UserPrefStore } from '../../stores/UserPrefStore.js';
const loadingStore = LoadingStore();
const router = useRouter();
const route = useRoute();
const value = ref('');
@@ -135,7 +196,16 @@ const scenario_execution_store = ScenarioExecutionStore();
const execution = scenario_execution_store.getSelectedExecScenario;
const execution_id = ref(null)
const inputs = ref(null);
const steps = ref(null);
const toast = useToast();
const fileNames = ref([]); // Memorizza i nomi dei file nello zip
const fileNamesOutput = ref([]); // Memorizza i nomi dei file nello zip
const zipInput = ref(null); // Contenitore per il file zip
const zipOutput = ref(null); // Contenitore per il file zip
const userPrefStore = UserPrefStore();
const updateLoading = ref(false);
const fileContent = ref('');
const fileType = ref('');
onMounted(() => {
@@ -149,6 +219,7 @@ onMounted(() => {
console.log(execution_id.value)
}
retrieveScenarioExec(execution_id.value)
});
const retrieveScenarioExec = (id) => {
@@ -165,9 +236,266 @@ const retrieveScenarioExec = (id) => {
scenario_output.value = response.data.execSharedMap.scenario_output;
exec_id.value = response.data.scenarioExecution_id
inputs.value = response.data.scenarioExecutionInput.inputs
steps.value = response.data.scenario.steps
if(inputs.value['MultiFileUpload']){
extractFiles(inputs.value['MultiFileUpload'], 'input', zipInput)
if(steps.value[0].attributes['codegenie_output_type']){
if(steps.value[0].attributes['codegenie_output_type'] == 'FILE'){
//console.log('base64 ', scenario_output.value)
//extractFiles(scenario_output.value, 'output', zipOutput)
fileType.value = 'FILE'
}
else if(steps.value[0].attributes['codegenie_output_type'] == 'MARKDOWN'){
fileType.value = 'MARKDOWN'
showFileContent(scenario_output.value, 'MARKDOWN')
}
else if(steps.value[0].attributes['codegenie_output_type'] == 'JSON'){
fileType.value = 'JSON'
showFileContent(scenario_output.value, 'JSON')
}
}
}
if(exec_scenario.value.executedByUsername===userPrefStore.getUser.username){
updateLoading.value = true
}
});
};
const extractFiles = async (base64String, type, zip) => {
try {
// Decodifica la base64 in un array di byte
const byteCharacters = atob(base64String);
const byteNumbers = Array.from(byteCharacters, char => char.charCodeAt(0));
const byteArray = new Uint8Array(byteNumbers);
// Carica il file zip con JSZip
const zipData = await JSZip.loadAsync(byteArray);
zip.value = zipData;
// Ottieni tutti i file (compresi quelli nelle sottocartelle)
if(type == 'input'){
fileNames.value = getFileNamesInput(zipData);
}else{
fileNamesOutput.value = getFileNames(zipData);
}
} catch (error) {
console.error('Error extracting zip:', error);
if(type == 'input'){
fileNames.value = [];
}else{
fileNamesOutput.value = [];
}
}
};
const showFileContent = (base64String, type) => {
try {
// Decodifica la stringa Base64
const binaryString = atob(base64String);
const binaryLength = binaryString.length;
const bytes = new Uint8Array(binaryLength);
for (let i = 0; i < binaryLength; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Converti i byte in una stringa leggibile
const textContent = new TextDecoder().decode(bytes);
// Gestione del tipo di file
if (type === 'MARKDOWN') {
//fileType.value = 'markdown';
fileContent.value = marked(textContent); // Converte Markdown in HTML
} else if (type === 'JSON') {
//fileType.value = 'json';
const jsonObject = JSON.parse(textContent); // Parse JSON
fileContent.value = JSON.stringify(jsonObject, null, 2); // Formatta JSON
} else {
fileContent.value = 'Tipo di file non supportato.';
}
} catch (error) {
fileContent.value = 'Errore durante la decodifica o il parsing del file.';
console.error(error);
}
};
// Funzione ricorsiva per ottenere tutti i file (anche quelli dentro le cartelle)
const getFileNames = (zipData) => {
const files = [];
// Esplora tutti i file nel file zip, considerando anche le sottocartelle
zipData.forEach((relativePath, file) => {
if (!file.dir) { // Escludiamo le cartelle
const fileName = relativePath.split('/').pop(); // Estrai solo il nome del file
files.push(fileName); // Aggiungiamo solo il nome del file
}
});
return files;
};
const getFileNamesInput = (zipData) => {
const files = [];
// Esplora tutti i file nel file zip, considerando anche le sottocartelle
zipData.forEach((relativePath, file) => {
if (!file.dir) { // Escludiamo le cartelle
files.push(relativePath); // Aggiungiamo il percorso relativo del file
}
});
return files;
};
// Funzione per scaricare il file
const downloadFileInput = async (fileName) => {
if (!zipInput.value) return;
try {
// Estrai il file dallo zip
const fileContent = await zipInput.value.file(fileName).async('blob');
const url = URL.createObjectURL(fileContent);
// Crea un link per scaricare il file
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error(`Error downloading file "${fileName}":`, error);
}
};
const downloadFileOutput = async (fileName) => {
if (!zipOutput.value) return;
try {
// Estrai il file dallo zip
const fileContent = await zipOutput.value.file(fileName).async('blob');
const url = URL.createObjectURL(fileContent);
// Crea un link per scaricare il file
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error(`Error downloading file "${fileName}":`, error);
}
};
const downloadFile = (base64String) => {
// Decodifica la stringa Base64
const binaryString = atob(base64String);
const binaryLength = binaryString.length;
const bytes = new Uint8Array(binaryLength);
for (let i = 0; i < binaryLength; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Creazione di un Blob dal file binario
const blob = new Blob([bytes], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
// Creazione di un URL per il Blob
const url = URL.createObjectURL(blob);
// Creazione di un elemento anchor per il download
const link = document.createElement('a');
link.href = url;
link.download = 'sf_document-'+execution_id.value+'.docx';
// Simulazione di un click per scaricare il file
document.body.appendChild(link);
link.click();
// Pulizia del DOM
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
const downloadFolderFromBase64 = async (base64String) => {
try {
// Decodifica la stringa base64 in un array di byte
const binaryString = atob(base64String); // Decodifica base64 in una stringa binaria
const byteArray = new Uint8Array(binaryString.length); // Crea un array di byte
for (let i = 0; i < binaryString.length; i++) {
byteArray[i] = binaryString.charCodeAt(i); // Popola l'array di byte
}
// Crea un oggetto JSZip per lavorare con il contenuto ZIP
const zip = await JSZip.loadAsync(byteArray); // Carica direttamente l'array di byte
// Estrai il primo nome della cartella presente nello ZIP
let folderName = Object.keys(zip.files).find(file => zip.files[file].dir); // Trova il primo file che è una cartella
// Se non è stata trovata alcuna cartella, creiamo una cartella chiamata "folderToDownload"
if (!folderName) {
folderName = 'docOutput-' + execution_id.value;
console.log(`Non è stata trovata alcuna cartella nello ZIP. Verrà creata una cartella chiamata "${folderName}".`);
}
// Crea un nuovo archivio ZIP in cui mettere i file della cartella
const newZip = new JSZip();
// Se una cartella esiste nello ZIP, estrai i file da essa, altrimenti crea una nuova cartella
const folder = zip.folder(folderName);
if (folder) {
// Aggiungi ogni file della cartella al nuovo archivio ZIP
const files = folder.files;
for (const fileName in files) {
const file = files[fileName];
// Controlla se il file è valido (non nullo)
if (file && file.async) {
try {
const fileContent = await file.async('blob'); // Estrai il contenuto del file
newZip.file(fileName, fileContent);
} catch (fileError) {
console.error(`Errore durante l'estrazione del file "${fileName}":`, fileError);
}
} else {
console.warn(`Il file "${fileName}" non è valido o non può essere elaborato.`);
}
}
} else {
// Se la cartella non esiste, crea una cartella vuota con nome "folderToDownload"
newZip.folder(folderName);
}
// Crea il nuovo file ZIP da scaricare
const newZipContent = await newZip.generateAsync({ type: 'blob' });
const url = URL.createObjectURL(newZipContent);
// Crea un link per scaricare il file ZIP
const a = document.createElement('a');
a.href = url;
a.download = `${folderName}.zip`; // Nome del file ZIP che contiene la cartella
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error('Errore durante il download della cartella:', error);
}
};
async function updateRating(newRating) {
loading_data.value = true;
@@ -179,7 +507,7 @@ async function updateRating(newRating) {
console.log('Rating aggiornato con successo:', response.data);
toast.add({
severity: 'success', // Tipo di notifica (successo)
summary: 'Successo', // Titolo della notifica
summary: 'Success', // Titolo della notifica
detail: 'Rating updated with success.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
@@ -187,7 +515,7 @@ async function updateRating(newRating) {
console.error('Errore nell\'aggiornamento del rating', response.data);
toast.add({
severity: 'error', // Tipo di notifica (errore)
summary: 'Errore', // Titolo della notifica
summary: 'Error', // Titolo della notifica
detail: 'Error updating rating. Try later.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
@@ -237,5 +565,33 @@ const openDebug = () => {
.editor ul {
list-style-type: disc !important;
}
pre {
white-space: pre-wrap; /* Fa andare a capo il contenuto automaticamente */
word-wrap: break-word; /* Interrompe le parole troppo lunghe */
overflow-wrap: break-word; /* Per compatibilità con più browser */
max-width: 100%; /* Imposta una larghezza massima pari al contenitore genitore */
overflow-x: auto; /* Aggiunge uno scorrimento orizzontale solo se necessario */
background-color: #f5f5f5; /* Colore di sfondo opzionale per migliorare leggibilità */
padding: 10px; /* Spaziatura interna */
border-radius: 5px; /* Bordo arrotondato opzionale */
font-family: monospace; /* Font specifico per codice */
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* Ombra per migliorare estetica */
}
.markdown-content {
white-space: pre-wrap; /* Gestisce correttamente gli spazi e i ritorni a capo */
word-wrap: break-word; /* Spezza le parole lunghe */
overflow-wrap: break-word; /* Per compatibilità con più browser */
max-width: 100%; /* Adatta il contenuto alla larghezza del contenitore */
overflow-x: auto; /* Aggiunge scorrimento orizzontale solo se necessario */
background-color: #f5f5f5; /* Sfondo per distinguere il contenuto */
padding: 10px; /* Margini interni */
border-radius: 5px; /* Bordo arrotondato */
font-family: Arial, sans-serif; /* Puoi scegliere un font leggibile */
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* Effetto estetico di ombra */
line-height: 1.5; /* Aumenta la leggibilità */
}
</style>