Merged PR 72: Clean Canvas and resolve bug

This commit is contained in:
2025-03-03 12:57:42 +00:00
6 changed files with 1323 additions and 717 deletions

View File

@@ -6,7 +6,9 @@
{{ user.name + " " + user.surname }}
</span>
<!-- <button @click="redirectProject()" class="p-button p-button-outlined">Projects</button> -->
<button @click="auth.logout()" class="p-button p-button-outlined">Logout</button>
<!-- <button @click="auth.logout()" class="p-button p-button-outlined">Logout</button> -->
<button @click="logout()" class="p-button p-button-outlined">Logout</button>
</div>
</div>
</template>
@@ -16,14 +18,26 @@
import { useAuth } from '@websanova/vue-auth/src/v3.js';
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { ScenarioStore } from '@/stores/ScenarioStore';
const auth = useAuth();
const user = computed(() => auth.user());
const router = useRouter();
const scenario_store = ScenarioStore();
function redirectProject() {
router.push('/projects'); // Specifica il percorso per la pagina "Projects"
}
function logout() {
auth.logout().then(() => {
scenario_store.resetStore();
router.push('/auth/login');
}).catch(err => {
console.error("Errore durante il logout:", err);
});
}
</script>

View File

@@ -6,7 +6,6 @@ import { UserPrefStore } from './UserPrefStore'
export const ScenarioStore = defineStore('scenario_store', () => {
const projectScenarios = ref([])
const globalScenarios = ref([])
const applicationScenarios = ref([])
@@ -18,7 +17,6 @@ export const ScenarioStore = defineStore('scenario_store', () => {
const userPrefStore = UserPrefStore()
const loadingStore = LoadingStore()
async function fetchScenarios() {
loadingStore.scenario_loading = true;
await ScenarioService.getScenariosProject(userPrefStore.selectedProject).then(resp => {
@@ -26,7 +24,6 @@ export const ScenarioStore = defineStore('scenario_store', () => {
allScenarios.value = [...projectScenarios.value]
loadingStore.scenario_loading = false;
});
}
async function fetchScenariosForRE() {
@@ -35,7 +32,6 @@ export const ScenarioStore = defineStore('scenario_store', () => {
scenariosForRE.value = resp.data;
loadingStore.scenario_loading = false;
});
}
async function fetchScenariosCross() {
@@ -45,19 +41,16 @@ export const ScenarioStore = defineStore('scenario_store', () => {
allScenarios.value = [...globalScenarios.value]
loadingStore.scenario_loading = false;
});
}
async function fetchApplicationScenarios() {
loadingStore.scenario_loading = true;
console.log("fetchApplicationScenarios >= selectedApp", userPrefStore.selectedApp) ;
await ScenarioService.getScenariosApplication(userPrefStore.selectedApp).then(resp=>{
console.log("fetchApplicationScenarios >= selectedApp", userPrefStore.selectedApp);
await ScenarioService.getScenariosApplication(userPrefStore.selectedApp).then(resp => {
console.log("response scenari", resp);
applicationScenarios.value = resp.data
allScenarios.value = [...projectScenarios.value, ...applicationScenarios.value]
loadingStore.scenario_loading = false;
})
}
@@ -65,7 +58,6 @@ export const ScenarioStore = defineStore('scenario_store', () => {
return allScenarios.value
})
const filteredScenarios = computed(() => {
console.log("scenarios", allScenarios.value);
var filteredScenario = []
@@ -91,12 +83,43 @@ export const ScenarioStore = defineStore('scenario_store', () => {
});
})
const filteredScenariosCanvas = computed(() => {
console.log("scenarios", allScenarios.value);
var filteredScenario = []
return {filteredScenarios,
projectScenarios,
applicationScenarios,
fetchScenarios,
fetchApplicationScenarios,
scenarios,
filterString , typeFilter, fetchScenariosCross, globalScenarios, fetchScenariosForRE, scenariosForRE}
})
if(userPrefStore.getSelApp != null) {
filteredScenario = [...globalScenarios.value,...projectScenarios.value, ...applicationScenarios.value]
} else {
filteredScenario = [...globalScenarios.value,...projectScenarios.value]
}
return filteredScenario
})
function resetStore() {
projectScenarios.value = []
globalScenarios.value = []
applicationScenarios.value = []
filterString.value = ''
allScenarios.value = []
typeFilter.value = { name: 'All', value: 'all' }
scenariosForRE.value = []
}
return {
filteredScenarios,
filteredScenariosCanvas,
projectScenarios,
applicationScenarios,
fetchScenarios,
fetchApplicationScenarios,
scenarios,
filterString,
typeFilter,
fetchScenariosCross,
globalScenarios,
fetchScenariosForRE,
scenariosForRE,
resetStore
}
})

File diff suppressed because it is too large Load Diff

View File

@@ -102,10 +102,8 @@ const scenarioTypeOp = ref([
onMounted(() => {
userPrefStore.fetchUserData().then(() => {
scenario_store.fetchScenarios();
scenario_store.fetchScenariosCross();
scenario_store.fetchScenarios();
if(userPrefStore.getSelApp != null){
scenario_store.fetchApplicationScenarios();
}

View File

@@ -7,7 +7,7 @@
<Dialog v-model:visible="execHermioneDialogVisible" modal :header="modalTitle" :style="{ width: '70rem' }">
<div v-if="askDialog" class="card flex flex-col gap-4 p-1">
<div class="flex flex-col gap-2">
<InputText id="question" type="text" placeholder="Write a Question" v-model="userQuestion" />
<Textarea id="question" type="text" placeholder="Write a Question" v-model="userQuestion" />
</div>
<div class="flex justify-center">
<Button label="Submit" @click="askHermione()" class=""/>
@@ -24,7 +24,8 @@
<button class="p-button p-button-primary" @click="addFromDialog(response)">Add to Canvas</button>
</div>
<div class="flex mt-2">
<Editor v-model="response" editorStyle="height: 320px; width: 930px" />
<!-- <Editor v-model="response" editorStyle="height: 320px; width: 930px" /> -->
<MdPreview class="editor" v-model="response" language="en-US" />
</div>
</div>
</Dialog>
@@ -46,12 +47,16 @@ import MdScenarioExctuotionDialog from '@/views/pages/canvas/MdScenarioExecution
import { Emoji, ExportPDF, Mark } from '@vavt/v3-extension';
import '@vavt/v3-extension/lib/asset/style.css';
import axios from 'axios';
import { MdEditor } from 'md-editor-v3';
import { MdEditor, MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import { computed, onMounted, ref } from 'vue';
import { computed, onMounted, ref} from 'vue';
import { ScenarioStore } from '@/stores/ScenarioStore';
import { UserPrefStore } from '@/stores/UserPrefStore';
const menu = ref();
const content = ref('# Welcome to WizardAI Canvas....');
const content = ref('# Welcome to WizardAI Canvas.... \n- Right-click to execute one of the available scenarios or ask general questions to Workflow.\n- Select text and right-click to summarize or rewrite it.');
const scenario_store = ScenarioStore();
const userPrefStore = UserPrefStore();
const scenarios = ref([]);
const items = ref([]);
const execScenarioDialogVisible = ref(false);
@@ -81,7 +86,7 @@ import { computed, onMounted, ref } from 'vue';
});
}
const onRightClick = (event) => {
const onRightClick = async (event) => {
if(editor.value.getSelectedText().length > 0) {
items.value=[
{label: 'Summarize',icon: 'pi pi-language', command:()=>openEditorAssistant(editor.value.getSelectedText(), "summarize")},
@@ -89,28 +94,34 @@ import { computed, onMounted, ref } from 'vue';
]
}else {
items.value=[
{ label: 'Ask Workflow', icon: 'pi pi-language', command:()=>openAskDialog()},
{ separator: true},
{ label: 'Execute Scenario', icon: 'pi pi-volume-up',items: scenarioMenuItems}
]
}
{label: 'Ask Workflow', icon: 'pi pi-language', command:()=>openAskDialog()},
{separator: true}
]
if (scenarioMenuItems().length > 0) {
items.value.push({label: 'Execute Scenario', icon: 'pi pi-volume-up', items: scenarioMenuItems()})
}
}
menu.value.show(event);
};
const scenarioMenuItems = computed(() => {
function scenarioMenuItems() {
const scenario_items = [];
for (let i = 0; i < scenarios.value.length; i++) {
scenario_items.push({
label: scenarios.value[i].name,
icon: 'pi pi-file',
command: () => {
executeScenario(scenarios.value[i]);
}
});
}
scenario_store.filteredScenariosCanvas.forEach(scenario => {
if (scenario.canvasEnabled) {
scenario_items.push({
label: scenario.name,
icon: 'pi pi-wrench',
command: () => {
executeScenario(scenario);
}
});
}
});
return scenario_items;
});
};
const executeScenario = (scenario) => {
@@ -197,12 +208,20 @@ import { computed, onMounted, ref } from 'vue';
};
onMounted(() => {
ScenarioService.getScenarios().then(resp=>{
scenarios.value = resp.data
})
userPrefStore.fetchUserData().then(() => {
scenario_store.fetchScenariosCross();
scenario_store.fetchScenarios();
if(userPrefStore.getSelApp != null){
scenario_store.fetchApplicationScenarios();
}
});
scenarioMenuItems();
});
const toolbars = ref([
'bold',
'underline',

View File

@@ -1,7 +1,6 @@
<template>
<div>
<div v-if="!data_loaded && !loading_data" class="card flex flex-col gap-4 p-1">
<div v-for="input in scenario.inputs" :key="input.name" class="flex flex-col gap-2" >
<div v-if="!data_loaded && !loading_data" class="card flex flex-col gap-4 p-1">
<!-- <div v-for="input in scenario.inputs" :key="input.name" class="flex flex-col gap-2" >
<label :for="input.name">{{ input.label }}</label>
<component
:is="getInputComponent(input.type)"
@@ -9,97 +8,727 @@
v-model="inputData[input.name]"
class="full-width-input"
/>
</div>
<div class="flex justify-center">
<Button label="Submit" @click="execScenario" class=""/>
</div>
</div> -->
<div v-for="input in scenario.inputs" :key="input.name" :class="['input-container', input.type === 'textarea' ? 'col-span-12' : '']">
<div v-if="input.type === 'singlefile' || input.type === 'singlefile_acceptall'">
<label :for="input.name">
<b>{{ input.label }}</b>
<i class="pi pi-info-circle text-violet-600 cursor-pointer" v-tooltip="'Upload one document from the suggested types. Mandatory if you want to execute scenario.'"></i>
</label>
<div>
<FileUpload
:name="'MultiFileUpload'"
:customUpload="false"
:url="uploadUrlPR"
@upload="(event) => onUpload(event, 'SingleFileUpload')"
:multiple="false"
:accept="acceptedFormats"
auto
:showUploadButton="false"
:showCancelButton="false"
:maxFileSize="10000000"
v-model:files="uploadedFiles"
>
<template #content="{ files, uploadedFiles, removeUploadedFileCallback, removeFileCallback }">
<div class="pt-4">
<!-- Tabella per file in caricamento -->
<div v-if="uploadedFiles.length > 0">
<table class="table-auto w-full border-collapse border border-gray-200">
<thead>
<tr>
<th class="border border-gray-300 p-2">Name</th>
<th class="border border-gray-300 p-2">Dimension</th>
<th class="border border-gray-300 p-2">Status</th>
<th class="border border-gray-300 p-2">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="(file, index) in uploadedFiles" :key="file.name + file.size" class="hover:bg-gray-50">
<td class="border border-gray-300 p-2">{{ file.name }}</td>
<td class="border border-gray-300 p-2">{{ formatSize(file.size) }}</td>
<td class="border border-gray-300 p-2">
<Badge value="UPLOADED" severity="success" />
</td>
<td class="border border-gray-300 p-2">
<Button label="Remove" @click="onRemove({ file, index }, removeUploadedFileCallback, 'SingleFileUpload')" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<template #empty>
<div class="flex items-center justify-center flex-col">
<i class="pi pi-cloud-upload !border-2 !rounded-full !p-8 !text-4xl !text-muted-color" />
<p class="mt-6 mb-0">Drag and drop files to here to upload.</p>
</div>
</template>
</FileUpload>
</div>
<ProgressSpinner v-if="loading_data" style="width: 50px; height: 50px" strokeWidth="8" fill="transparent"
animationDuration=".5s" aria-label="Custom ProgressSpinner" />
<div v-if="data_loaded" class="card flex flex-col gap-4 p-1">
<div class="flex items-center gap-2 p-0">
<button class="p-button p-button-primary" @click="addToCanvas">Add to Canvas</button>
</div>
<div class="flex mt-2">
<MdPreview class="editor" v-model="scenario_output" language="en-US" />
</div>
</div>
</div>
<div v-else-if="input.type === 'multifile'">
<label :for="input.name">
<b>{{ input.label }} </b>
<i class="pi pi-info-circle text-violet-600 cursor-pointer" v-tooltip="'Upload others documents of .docx, .msg, .text type. Optional.'"></i>
</label>
<div>
<FileUpload
:name="'MultiFileUpload'"
:customUpload="false"
:url="uploadUrlOther"
@upload="(event) => onUpload(event, 'MultiFileUpload')"
:multiple="true"
accept=".msg,.txt,.docx"
auto
:showUploadButton="false"
:showCancelButton="false"
:maxFileSize="10000000"
v-model:files="uploadedFiles"
>
<template #content="{ files, uploadedFiles, removeUploadedFileCallback, removeFileCallback }">
<div class="pt-4">
<!-- Tabella per file in caricamento -->
<div v-if="uploadedFiles.length > 0">
<table class="table-auto w-full border-collapse border border-gray-200">
<thead>
<tr>
<th class="border border-gray-300 p-2">Name</th>
<th class="border border-gray-300 p-2">Dimension</th>
<th class="border border-gray-300 p-2">Status</th>
<th class="border border-gray-300 p-2">Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="(file, index) in uploadedFiles" :key="file.name + file.size" class="hover:bg-gray-50">
<td class="border border-gray-300 p-2">{{ file.name }}</td>
<td class="border border-gray-300 p-2">{{ formatSize(file.size) }}</td>
<td class="border border-gray-300 p-2">
<Badge value="UPLOADED" severity="success" />
</td>
<td class="border border-gray-300 p-2">
<Button label="Remove" @click="onRemove({ file, index }, removeUploadedFileCallback, 'MultiFileUpload')" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<template #empty>
<div class="flex items-center justify-center flex-col">
<i class="pi pi-cloud-upload !border-2 !rounded-full !p-8 !text-4xl !text-muted-color" />
<p class="mt-6 mb-0">Drag and drop files to here to upload.</p>
</div>
</template>
</FileUpload>
</div>
</div>
<div v-else>
<label :for="input.name"
><b>{{ input.label }}</b></label
>
<div class="input-wrapper">
<component :is="getInputComponent(input.type)" :id="input.name" v-model="formData[input.name]" :options="input.options" class="full-width-input" :disabled="loadingStore.exectuion_loading" />
</div>
</div>
</div>
<div class="flex justify-center">
<Button :disabled="loadingStore.exectuion_loading || !isInputFilled" label="Execute" @click="execScenario" size="large" iconPos="right" icon="pi pi-cog"></Button>
</div>
</div>
<div v-if="loading_data" class="flex flex-col items-center">
<div class="flex justify-center mt-4">
<jellyfish-loader :loading="loadingStore.exectuion_loading" scale="1" color="#A100FF" />
</div>
<div v-if="scenario_response_message && scenario_response_message.includes('/')">
<span>{{ scenario_response_message }}</span>
</div>
<div v-else>Starting execution...</div>
<div class="flex justify-center" style="margin-bottom: 30px">
<p>Time elapsed:&nbsp;</p>
<div id="timer" class="timer">00:00</div>
</div>
</div>
<div v-if="data_loaded">
<Panel class="mt-6">
<template #header>
<div class="flex items-center gap-2">
<span class="font-bold">Workflow Response</span>
</div>
<!-- <div class="flex items-center gap-2 p-0">
<button class="p-button p-button-primary" @click="addToCanvas">Add to Canvas</button>
</div> -->
</template>
<template #icons>
<div class="flex justify-end">
<div class="flex">
<Rating :modelValue="rating" :stars="5" @change="updateRating($event)" />
</div>
<div>
<Button severity="secondary" rounded @click="openDebug" v-tooltip.left="'View code'">
<i class="pi pi-code"></i>
</Button>
</div>
</div>
</template>
<div class="card flex flex-col gap-4 w-full">
<div class="flex items-center gap-2 p-0">
<button class="p-button p-button-primary" @click="addToCanvas">Add to Canvas</button>
</div>
<div>
<MdPreview class="editor" v-model="scenario_output" language="en-US" />
</div>
</div>
</Panel>
<Dialog v-model:visible="debug_modal" maximizable modal :header="scenario.name" :style="{ width: '75%' }" :breakpoints="{ '1199px': '75vw', '575px': '90vw' }">
<div class="flex">
<div class="card flex flex-col gap-4 w-full">
<JsonEditorVue v-model="exec_scenario" />
</div>
</div>
</Dialog>
</div>
</template>
<script setup>
import { ref ,defineEmits} from 'vue';
import InputText from 'primevue/inputtext';
import ProgressSpinner from 'primevue/progressspinner';
import Select from 'primevue/select';
import Textarea from 'primevue/textarea';
import axios from 'axios';
import { MdEditor } from 'md-editor-v3';
import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import ChangeImpactOutputViewer from '@/components/ChangeImpactOutputViewer.vue';
import ChangeImpactOutputViewer from '@/components/ChangeImpactOutputViewer.vue';
import { ScenarioService } from '@/service/ScenarioService';
import { LoadingStore } from '@/stores/LoadingStore';
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 moment from 'moment';
import { usePrimeVue } from 'primevue/config';
import InputText from 'primevue/inputtext';
import Select from 'primevue/select';
import Textarea from 'primevue/textarea';
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
import { computed, onMounted, ref, defineEmits} from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { JellyfishLoader } from 'vue3-spinner';
const props = defineProps(['scenario'])
const emit = defineEmits(['add'])
const loadingStore = LoadingStore();
const toast = useToast();
const zip = ref(null);
const router = useRouter();
const route = useRoute();
const value = ref('');
const rating = ref(0);
const scenario_response = ref(null);
const scenario_output = ref(null);
const scenario_response_message = ref(null);
const loading = ref(false);
const data_loaded = ref(false);
const loading_data = ref(false);
const formData = ref({});
const exec_id = ref(null);
const exec_scenario = ref({});
const debug_modal = ref(false);
let pollingInterval = null;
const folderName = ref('');
const fileNamesOutput = ref([]);
// URL di upload
const uploadUrlBase = import.meta.env.VITE_BACKEND_URL;
const uploadUrl = ref('');
const uploadUrlPR = ref('');
const uploadUrlOther = ref('');
// File che l'utente ha selezionato
const uploadedFiles = ref([]);
const numberPrFiles = ref(0);
const acceptedFormats = ref('.docx');
// :url="`http://localhost:8081/uploadListFiles/${folderName}`"
// Stato per l'ID univoco della cartella
const uniqueFolderId = ref(generateUniqueId());
const confirm = useConfirm();
const loading_data = ref(false);
const data_loaded = ref(false);
const props = defineProps(['scenario']);
const emit = defineEmits(['add'])
const $primevue = usePrimeVue();
const files = ref([]);
const fileContent = ref('');
const fileType = ref('');
const reqMultiFile = ref(false);
const inputData = ref({});
const scenario_output = ref("");
let startTime = ref(null);
let timerInterval = ref(null);
const getInputComponent = (type) => {
switch (type) {
case 'text':
return InputText;
case 'textarea':
return Textarea;
case 'select':
return Select;
default:
return InputText;
function startTimer() {
startTime = Date.now();
timerInterval = setInterval(() => {
const elapsedTime = moment.duration(Date.now() - startTime);
document.getElementById('timer').textContent = moment.utc(elapsedTime.asMilliseconds()).format('mm:ss');
}, 1000);
}
function stopTimer() {
clearInterval(timerInterval);
}
const isInputFilled = computed(() => {
var isFilled = true;
if (props.scenario.inputs === undefined) {
console.log('No inputs found');
return false;
}
props.scenario.inputs.forEach((input) => {
if (formData.value[input.name] === undefined || formData.value[input.name] === '') {
console.log('Input not filled: ', input.name);
isFilled = false;
}
};
});
return isFilled;
});
const execScenario = () => {
onMounted(() => {
console.log('MdScenarioExecutionDialog montato!');
const timestamp = Date.now(); //Ottiene il timestamp corrente
const randomNumber = Math.floor(Math.random() * 1000);
folderName.value = `${timestamp}_${randomNumber}`;
uploadUrl.value = uploadUrlBase + '/uploadListFiles/' + folderName.value;
uploadUrlPR.value = uploadUrl.value + '/PR';
uploadUrlOther.value = uploadUrl.value + '/OTHER';
console.log('Upload URL:', uploadUrl);
});
const getInputComponent = (type) => {
switch (type) {
case 'text':
return InputText;
case 'textarea':
return Textarea;
case 'select':
return Select;
default:
return InputText;
}
};
const execScenario = () => {
if (numberPrFiles.value !== 1 && reqMultiFile.value) {
toast.add({
severity: 'warn', // Tipo di notifica (errore)
summary: 'Attention', // Titolo della notifica
detail: 'You can upload only 1 PR file. Please remove others.' // Messaggio dettagliato
});
} else {
loading_data.value = true;
data_loaded.value = false;
rating.value = 0;
startTimer();
loadingStore.exectuion_loading = true;
const data = {
scenario_id: props.scenario.id,
inputs: { ...inputData.value }
inputs: { ...formData.value }
};
console.log(data);
axios.post('/scenarios/execute', data)
.then(response => {
loading_data.value = false;
data_loaded.value = true;
console.log(response);
axios
.post('/scenarios/execute-async', data)
.then((response) => {
console.log('Response data exec 1:', response.data);
scenario_response.value = response.data;
scenario_response_message.value = response.data.message;
scenario_output.value = response.data.stringOutput;
exec_id.value = response.data.scenarioExecution_id;
loadingStore.setIdExecLoading(exec_id.value);
// Start polling
startPolling();
})
.catch(error => {
.catch((error) => {
console.error('Error executing scenario:', error);
loadingStore.exectuion_loading = false;
});
};
const addToCanvas = () => {
console.log("Aggiunto");
}
};
const openDebug = () => {
axios.get('/scenarios/execute/' + exec_id.value).then((resp) => {
exec_scenario.value = resp.data;
debug_modal.value = true;
});
};
const pollBackendAPI = () => {
axios.get('/scenarios/getExecutionProgress/' + exec_id.value).then((response) => {
if (response.data.status == 'OK' || response.data.status == 'ERROR') {
console.log('Condition met, stopping polling.');
stopPolling();
stopTimer();
loading_data.value = false;
data_loaded.value = true;
scenario_output.value = response.data.stringOutput;
console.log('Response data exec 2:', response.data);
exec_id.value = response.data.scenarioExecution_id;
scenario_response_message.value = null; //if != null, next scenario starts with old message
console.log('Scenario 3:', props.scenario);
// Controlla se l'array `inputs` contiene un elemento con `name = 'MultiFileUpload'`
if (props.scenario.inputs.some((input) => input.name === 'MultiFileUpload')) {
if (response.data.status == 'OK') {
// Accedi al primo step e controlla se esiste l'attributo `codegenie_output_type`
const firstStep = props.scenario.steps[0];
if (firstStep?.attributes?.['codegenie_output_type']) {
// Controlla se `codegenie_output_type` è uguale a 'FILE'
// if (firstStep.attributes['codegenie_output_type'] === 'FILE') {
// console.log('base64 ', scenario_output.value);
// Chiama la funzione `extractFiles` con il valore di `scenario_output.value`
//extractFiles(scenario_output.value);
//}
if (firstStep.attributes['codegenie_output_type'] == 'FILE') {
//console.log('base64 ', scenario_output.value)
//extractFiles(scenario_output.value, 'output', zipOutput)
fileType.value = 'FILE';
} else if (firstStep.attributes['codegenie_output_type'] == 'MARKDOWN') {
fileType.value = 'MARKDOWN';
showFileContent(scenario_output.value, 'MARKDOWN');
} else if (firstStep.attributes['codegenie_output_type'] == 'JSON') {
fileType.value = 'JSON';
showFileContent(scenario_output.value, 'JSON');
}
}
} else {
console.log('Error in execution');
}
}
} else {
console.log('Condition not met, polling continues.');
scenario_response.value = response.data;
scenario_response_message.value = response.data.message;
}
});
};
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);
}
};
// Function to start polling
function startPolling() {
// Set polling interval (every 2.5 seconds in this case)
pollingInterval = setInterval(pollBackendAPI, 2500);
console.log('Polling started.');
}
// Function to stop polling
function stopPolling() {
clearInterval(pollingInterval);
loadingStore.exectuion_loading = false;
loadingStore.setIdExecLoading('');
console.log('Polling stopped.');
}
const extractFiles = async (base64String) => {
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)
fileNamesOutput.value = getFileNames(zipData);
} catch (error) {
console.error('Error extracting zip:', error);
fileNamesOutput.value = [];
}
};
// 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
files.push(relativePath); // Aggiungiamo il percorso relativo del file
}
});
return files;
};
async function updateRating(newRating) {
ScenarioService.updateScenarioExecRating(exec_id.value, newRating.value)
.then((response) => {
console.log('response:', response);
if (response.data === 'OK') {
rating.value = newRating.value;
console.log('Rating aggiornato con successo:', response.data);
toast.add({
severity: 'success', // Tipo di notifica (successo)
summary: 'Success', // Titolo della notifica
detail: 'Rating updated with success.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
} else {
console.error("Errore nell'aggiornamento del rating", response.data);
toast.add({
severity: 'error', // Tipo di notifica (errore)
summary: 'Error', // Titolo della notifica
detail: 'Error updating rating. Try later.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
}
})
.catch((error) => {
console.error('Errore durante la chiamata al backend:', error);
});
}
// Funzione per generare un ID univoco
function generateUniqueId() {
return Date.now(); // Puoi usare anche UUID.randomUUID() o una libreria simile
}
const onRemove = (event, removeUploadedFileCallback, type) => {
const { file, index } = event;
console.log('Removing file:', folderName.value);
try {
axios
.post(
`/deleteFile`,
{ fileName: file.name, folderName: folderName.value }, // Invio nome del file come payload
{
headers: {
'Content-Type': 'application/json'
}
}
)
.then((response) => {
if (response.status === 200) {
console.log('File removed successfully:', response.data);
// Mostra notifica di successo
toast.add({
severity: 'success',
summary: 'Success',
detail: 'File removed successfully!',
life: 3000
});
if (type === 'SingleFileUpload') {
numberPrFiles.value -= 1;
console.log('Number of PR files: ', numberPrFiles.value);
}
// Aggiorna lista dei file caricati
removeUploadedFileCallback(index);
} else {
console.error('Failed to remove file:', response.statusText);
// Mostra notifica di errore
toast.add({
severity: 'error',
summary: 'Error',
detail: `Failed to remove file. Status: ${response.statusText}`,
life: 3000
});
}
})
.catch((error) => {
console.error('Error while removing file:', error);
// Mostra notifica di errore
toast.add({
severity: 'error',
summary: 'Error',
detail: `Error while removing file: ${error.message}`,
life: 3000
});
});
} catch (error) {
console.error('Error while removing file:', error);
}
};
const onUpload = (event, uploadType) => {
console.log('response upload ', event.xhr.response);
const { xhr } = event; // Estraggo l'oggetto XMLHttpRequest
if (xhr.status === 200) {
// Risposta OK
if (uploadType === 'SingleFileUpload') {
//formData.value['SingleFileUpload'] = "OK";
if (event.files && event.files.length > 0) {
console.log('File uploaded:', event.files);
formData.value['SingleFileUpload'] = event.files[0].name; // Nome del primo file
} else {
formData.value['SingleFileUpload'] = 'UnknownFile';
}
numberPrFiles.value += 1;
console.log('Number of PR files: ', numberPrFiles.value);
}
formData.value['MultiFileUpload'] = xhr.response;
console.log('Form value upload ', formData.value['MultiFileUpload']);
console.log('Upload completato con successo. Risposta:', xhr.response);
toast.add({
severity: 'success',
summary: 'Success',
detail: 'File uploaded successfully!',
life: 3000
});
} else {
// Errore durante l'upload
console.error("Errore durante l'upload. Stato:", xhr.status, 'Risposta:', xhr.response);
toast.add({
severity: 'error',
summary: 'Error',
detail: `Failed to upload file. Stato: ${xhr.status}`,
life: 3000
});
}
};
// Funzione per scaricare il file
const downloadZipFile = async (fileName) => {
if (!zip.value) return;
try {
// Estrai il file dallo zip
const fileContent = await zip.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);
}
};
function downloadFile() {
try {
// Converti la stringa base64 in un blob
const base64String = this.scenario_output;
const byteCharacters = atob(base64String);
const byteNumbers = Array.from(byteCharacters, (char) => char.charCodeAt(0));
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray]);
// Crea un link temporaneo per il download
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'sf_document-' + exec_id.value + '.docx'; // Specifica il nome del file
document.body.appendChild(a);
a.click();
// Rimuovi il link temporaneo
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('Errore durante il download del file:', error);
}
}
const formatSize = (bytes) => {
const k = 1024;
const sizes = $primevue.config.locale.fileSizeTypes;
if (bytes === 0) {
return `0 ${sizes[0]}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(k));
const truncatedSize = Math.trunc(bytes / Math.pow(k, i)); // Troncamento del valore
return `${truncatedSize} ${sizes[i]}`;
};
const addToCanvas = () => {
console.log("Added");
emit('add',scenario_output.value)
}
</script>
<style>
.editor ol {
list-style-type: decimal !important;
}
<style >
.editor ul {
list-style-type: disc !important;
}
.input-container {
margin-bottom: 1em;
}
.input-wrapper {
display: flex;
align-items: center;
gap: 0.5em;
margin-top: 10px;
}
.full-width-input {
width: 100%;
}
.editor ol {
list-style-type: decimal !important;
@@ -108,4 +737,25 @@
.editor ul {
list-style-type: disc !important;
}
</style>
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 {
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 */
}
</style>