Merged PR 34: rating

This commit is contained in:
D'Alia, Florinda
2024-12-11 15:42:24 +00:00
4 changed files with 214 additions and 39 deletions

View File

@@ -6,7 +6,7 @@
{{ user.name + " " + user.surname }} {{ user.name + " " + user.surname }}
</span> </span>
<!-- <button @click="redirectProject()" class="p-button p-button-outlined">Projects</button> --> <!-- <button @click="redirectProject()" class="p-button p-button-outlined">Projects</button> -->
<button @click="auth.logout()" class="p-button p-button-danger p-button-outlined">Logout</button> <button @click="auth.logout()" class="p-button p-button-outlined">Logout</button>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -9,13 +9,11 @@ import App from './App.vue';
import router from './router'; import router from './router';
import Aura from '@primevue/themes/aura'; import { definePreset } from '@primevue/themes';
import Lara from '@primevue/themes/lara';
import Nora from '@primevue/themes/nora'; import Nora from '@primevue/themes/nora';
import PrimeVue from 'primevue/config'; import PrimeVue from 'primevue/config';
import ConfirmationService from 'primevue/confirmationservice'; import ConfirmationService from 'primevue/confirmationservice';
import ToastService from 'primevue/toastservice'; import ToastService from 'primevue/toastservice';
import { definePreset } from '@primevue/themes';
import BlockViewer from '@/components/BlockViewer.vue'; import BlockViewer from '@/components/BlockViewer.vue';
@@ -172,3 +170,4 @@ axios.interceptors.response.use(function (response) {
}, function (error) { }, function (error) {
return Promise.reject(error); return Promise.reject(error);
}); });

View File

@@ -20,6 +20,12 @@ export const ScenarioService = {
getExecScenariosByUser() { getExecScenariosByUser() {
return axios.get('/scenariosByUser') return axios.get('/scenariosByUser')
},
getScenariosForRE(){
return axios.get('/getScenariosForRE')
},
updateScenarioExecRating(data) {
return axios.post('/updateRating', data)
} }
} }

View File

@@ -24,6 +24,7 @@
:paginatorTemplate="paginatorTemplate" :paginatorTemplate="paginatorTemplate"
:totalRecords="scenario_execution_store.scenariosExecution.length" :totalRecords="scenario_execution_store.scenariosExecution.length"
:first="first" :first="first"
filterDisplay="menu"
@page="onPage" :globalFilterFields="['id', 'scenario.name', 'execSharedMap.user_input.selected_project', 'execSharedMap.user_input.selected_application']"> @page="onPage" :globalFilterFields="['id', 'scenario.name', 'execSharedMap.user_input.selected_project', 'execSharedMap.user_input.selected_application']">
<template #header> <template #header>
@@ -37,41 +38,132 @@
</div> </div>
</template> </template>
<!-- <Column field="id" header="Execution ID"></Column> -->
<Column field="scenario.name" header="Scenario Name"> <Column field="scenario.name" header="Scenario Name" sortable
style="min-width: 12rem">
<template #body="slotProps"> <template #body="slotProps">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
{{ slotProps.data.scenario.name }} {{ slotProps.data.scenario?.name }}
<i <i
class="pi pi-info-circle text-blue-500 cursor-pointer" class="pi pi-info-circle text-violet-600 cursor-pointer"
v-tooltip="slotProps.data.scenario.description" v-tooltip="slotProps.data?.scenario?.description || 'No description available'"
></i> ></i>
<!-- controllare il tooltip -->
</div> </div>
</template> </template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column> </Column>
<Column field="execSharedMap.user_input.selected_project" header="Project Input"></Column> <Column field="execSharedMap.user_input.selected_project" header="Project Input" sortable
<Column field="execSharedMap.user_input.selected_application" header="Application Input"></Column> style="min-width: 12rem">
<Column header="Start Date">
<template #body="slotProps"> <template #body="slotProps">
<div class="flex items-center gap-2">
{{ slotProps.data.execSharedMap?.user_input?.selected_project }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column>
<Column field="execSharedMap.user_input.selected_application" header="Application Input" sortable
style="min-width: 12rem">
<template #body="slotProps">
<div class="flex items-center gap-2">
{{ slotProps.data.execSharedMap?.user_input?.selected_application }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column>
<Column field="formattedEndDate"
filterField="endDate" header="Start Date" sortable
style="min-width: 12rem">
<template #body="slotProps">
<div class="flex items-center gap-2">
{{ moment(slotProps.data.startDate).format('DD-MM-YYYY HH:mm:ss') }} {{ moment(slotProps.data.startDate).format('DD-MM-YYYY HH:mm:ss') }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<Calendar
v-model="filterModel.value"
@input="(value) => {
filterModel.value = new Date(value); // Converte in oggetto Date
filterCallback();
}"
dateFormat="yy-mm-dd"
placeholder="Filter by Date"
/>
</template> </template>
</Column> </Column>
<Column header="End Date"> <Column field="endDate"
header="End Date" sortable
style="min-width: 12rem">
<template #body="slotProps"> <template #body="slotProps">
<div class="flex items-center gap-2">
{{ moment(slotProps.data.endDate).format('DD-MM-YYYY HH:mm:ss') }} {{ moment(slotProps.data.endDate).format('DD-MM-YYYY HH:mm:ss') }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template> </template>
</Column> </Column>
<Column field="scenario.aiModel.apiProvider" header="Model AI"></Column> <Column field="scenario.aiModel.apiProvider" header="Model AI" sortable
<Column field="scenario.aiModel.model" header="Version"></Column> style="min-width: 12rem">
<Column field="usedTokens" header="Tokens used"></Column>
<Column header="Output Type">
<template #body="slotProps"> <template #body="slotProps">
{{ slotProps.data.scenario.outputType || 'text' }} <div class="flex items-center gap-2">
{{ slotProps.data.scenario?.aiModel?.apiProvider }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column>
<Column field="scenario.aiModel.model" header="Version" sortable
style="min-width: 12rem">
<template #body="slotProps">
<div class="flex items-center gap-2">
{{ slotProps.data.scenario?.aiModel?.model || 'N/A' }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column>
<Column field="usedTokens" header="Tokens used" sortable
style="min-width: 12rem">
<template #body="slotProps">
<div class="flex items-center gap-2">
{{ slotProps.data.usedTokens || 'N/A' }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column>
<Column filterField="outputTypeFilter" header="Output Type" sortable
style="min-width: 12rem">
<template #body="slotProps">
<div class="flex items-center gap-2">
{{ slotProps.data.scenario?.outputType || 'text' }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template> </template>
</Column> </Column>
<Column field="rating" header="Rating"> <Column field="rating" header="Rating">
<template #body="slotProps"> <template #body="slotProps">
<Rating :value="slotProps.data.rating" :stars="5" readonly cancel="false" /> <Rating :modelValue="slotProps.data.rating" :stars="5" @change="updateRating(slotProps.data, $event)" />
</template> </template>
</Column> </Column>
<Column field="id" :style="{ position: 'sticky', right: '0', zIndex: '1', background: '#f3f3f3'}"> <Column field="id" :style="{ position: 'sticky', right: '0', zIndex: '1', background: '#f3f3f3'}">
@@ -97,13 +189,15 @@
<script setup> <script setup>
import { FilterMatchMode } from '@primevue/core/api'; import { FilterMatchMode, FilterOperator } from '@primevue/core/api';
import 'md-editor-v3/lib/style.css'; import 'md-editor-v3/lib/style.css';
import moment from 'moment';
import ProgressSpinner from 'primevue/progressspinner'; import ProgressSpinner from 'primevue/progressspinner';
import { useToast } from 'primevue/usetoast';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { ScenarioService } from '../../service/ScenarioService.js';
import { ScenarioExecutionStore } from '../../stores/ScenarioExecutionStore.js'; import { ScenarioExecutionStore } from '../../stores/ScenarioExecutionStore.js';
import moment from 'moment';
const first = ref(0); const first = ref(0);
const router = useRouter(); const router = useRouter();
@@ -121,19 +215,95 @@ const debug_modal = ref(false);
const execution_id = ref(""); const execution_id = ref("");
const listScenarios = ref([]); const listScenarios = ref([]);
const scenario_execution_store = ScenarioExecutionStore(); const scenario_execution_store = ScenarioExecutionStore();
const toast = useToast();
const filters = ref({ const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }, global: { value: null, matchMode: FilterMatchMode.CONTAINS },
'id': { value: null, matchMode: FilterMatchMode.CONTAINS }, 'id': { value: null, matchMode: FilterMatchMode.CONTAINS },
'scenario.name': { value: null, matchMode: FilterMatchMode.CONTAINS }, 'scenario.name': {
'execSharedMap.user_input.selected_project': { value: null, matchMode: FilterMatchMode.CONTAINS }, operator: FilterOperator.AND,
'execSharedMap.user_input.selected_application': { value: null, matchMode: FilterMatchMode.CONTAINS } constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }]
},
'execSharedMap.user_input.selected_project': {
operator: FilterOperator.AND,
constraints: [{value: null, matchMode: FilterMatchMode.CONTAINS }]
},
'execSharedMap.user_input.selected_application': {
operator: FilterOperator.AND,
constraints: [{
value: null, matchMode: FilterMatchMode.CONTAINS
}]
},
'scenario.aiModel.apiProvider': {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }]
},
'scenario.aiModel.model': {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }]
},
'usedTokens': {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }]
},
outputTypeFilter: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }]
},
'startDate': {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
},
'endDate': {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
}
}); });
onMounted(() => { onMounted(() => {
scenario_execution_store.fetchScenariosExecution() scenario_execution_store.fetchScenariosExecution()
}); });
async function updateRating(rowData, newRating) {
// Aggiorna il rating nell'oggetto prima di inviarlo
rowData.rating = newRating.value;
console.log('data rating:', rowData);
// Mostra un loader se necessario
loading_data.value = true;
ScenarioService.updateScenarioExecRating(rowData).then((response) => {
console.log('response:', response);
if (response.data === "OK") {
console.log('Rating aggiornato con successo:', response.data);
scenario_execution_store.fetchScenariosExecution()
toast.add({
severity: 'success', // Tipo di notifica (successo)
summary: 'Successo', // 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: 'Errore', // 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);
}).finally(() => {
loading_data.value = false;
});
}
const goToScenarioExec = (execScenarioItem) => { const goToScenarioExec = (execScenarioItem) => {
console.log(execScenarioItem); console.log(execScenarioItem);